c# - Lock user if failed 5 attempts -
i can decrease attemptsleft
in database set 5
. attemptsleft
decreased 1 if enter password wrongly correct username. can make user not log in system if user's status suspended
.
problem:
i set when attemptsleft
gets decreased 0, user's status become suspended
. however, attemptsleft
in database gets decreased , user's status not become suspended
, gets suspended
if enter username , password correctly, no matter how many attemptsleft
lefts.
what wrong?
i think user.attempts
retrieving database not working, , because user.attempts
0
, attemptsleft
in database gets decreased, because decreasing query
.
here code using:
usermanager class:
public void getattempts(string username, string password) { logincontext context = new logincontext(); using (sqlconnection conn = new sqlconnection(connectionstring)) { string query = "select [username], [password], [attemptsleft] [information] [username] = @username , [password] = @password"; conn.open(); using (sqlcommand cmd = new sqlcommand(query, conn)) { cmd.parameters.add("@username", sqldbtype.nvarchar); cmd.parameters["@username"].value = username; cmd.parameters.add("@password", sqldbtype.nvarchar); cmd.parameters["@password"].value = password; using (sqldatareader reader = cmd.executereader()) { while (reader.read()) { context.attempts = convert.toint32(reader["attemptsleft"]); } } } conn.close(); } } public bool checkuser(string username, string password) { using (sqlconnection conn = new sqlconnection(connectionstring)) { _query = "select [username], [password] [information] [username] = @username , [password] = @password"; conn.open(); using (sqlcommand cmd = new sqlcommand(_query, conn)) { cmd.parameters.add("@username", sqldbtype.nvarchar); cmd.parameters["@username"].value = username; cmd.parameters.add("@password", sqldbtype.nvarchar); cmd.parameters["@password"].value = password; using (sqldatareader reader = cmd.executereader()) { if (reader.hasrows) { return true; } else { return false; } } } } } public bool checkstatus(string username, string password) { using (sqlconnection conn = new sqlconnection(connectionstring)) { _query = "select [username], [currentstatus] [information] [username] = @username , [currentstatus] = @currentstatus"; conn.open(); using (sqlcommand cmd = new sqlcommand(_query, conn)) { cmd.parameters.add("@username", sqldbtype.nvarchar); cmd.parameters["@username"].value = username; cmd.parameters.add("@currentstatus", sqldbtype.nvarchar); cmd.parameters["@currentstatus"].value = "active"; using (sqldatareader reader = cmd.executereader()) { if (reader.hasrows) { return true; } else { return false; } } } } } public bool suspenduser(string username) { bool flag = false; using (sqlconnection conn = new sqlconnection(connectionstring)) { string query = "select [username], [currentstatus] [information] [username] = @username"; string _query = "update [information] set [currentstatus] = @currentstatus [username] = @username"; conn.open(); using (sqlcommand cmd = new sqlcommand(query, conn)) using (sqlcommand _cmd = new sqlcommand(_query, conn)) { cmd.parameters.add("@username", sqldbtype.nvarchar); cmd.parameters["@username"].value = username; _cmd.parameters.add("@username", sqldbtype.nvarchar); _cmd.parameters["@username"].value = username; _cmd.parameters.add("@currentstatus", sqldbtype.nvarchar); _cmd.parameters["@currentstatus"].value = "suspended"; flag = convert.toboolean(_cmd.executenonquery()); } conn.close(); } return flag; } public bool decreaseattempts(string username) { bool flag = false; using (sqlconnection conn = new sqlconnection(connectionstring)) { string query = "select [username], [attemptsleft] [information] [username] = @username"; string _query = "update [information] set [attemptsleft] = [attemptsleft] - 1 [username] = @username"; conn.open(); using (sqlcommand cmd = new sqlcommand(query, conn)) using (sqlcommand _cmd = new sqlcommand(_query, conn)) { cmd.parameters.add("@username", sqldbtype.nvarchar); cmd.parameters["@username"].value = username; _cmd.parameters.add("@username", sqldbtype.nvarchar); _cmd.parameters["@username"].value = username; flag = convert.toboolean(_cmd.executenonquery()); } conn.close(); } return flag; }
logincontext model:
public int attempts { get; set; }
controller:
usermanager manager = new usermanager(); [httpget] public actionresult login() { return view(); } [httppost] public actionresult login(logincontext user, string username, string password) { if (!request.isauthenticated) { if (modelstate.isvalid) { // `if` statement below gets executed when enter password , username correctly. if (manager.checkuser(username, password)) { // `if` statement below gets executed when user's status not `suspended` if (manager.checkstatus(username, password)) { manager.getattempts(username, password); // `if` statement below not gets executed. `user.attempts` `0`. if (user.attempts > 0) { formsauthentication.setauthcookie(user.username, false); return redirecttoaction("list", "home"); } //`else` statement below gets executed whenever enter username , password correctly. else { modelstate.addmodelerror(string.empty, "the account: " + username + ", has been locked due many failed login attempts!"); manager.suspenduser(username); } } //`else` statement below gets executed when user's status `suspended`. else { modelstate.addmodelerror(string.empty, "your account has been locked due many failed login attempts!"); } } // `else` statement below gets executed when enter password wrongly , username correctly or vice versa. else { modelstate.addmodelerror(string.empty, "username or password incorrect!"); manager.decreaseattempts(username); } } return view(user); } }
if follow scenario when user not valid decresases invalid attempts. doesnt suspend user unless enter login credentials correctly.
really sudo logic should go:
- validate credentials
- credentials valid
- check account status, suspended deny access
- reset attempts 5
- allow user application
- credentials invalid
- decrease attempts one
- set account suspended if required
- credentials valid
alot of logic can bound 1 or 2 methods. in decreaseattempts()
method have 2 sql commands sql command cmd
never executed
here example of doing in 1 method returning enum of status. basic example requires 1 method execute full authorization methods. have commented teh code.
public partial class usermanager { const int maxattempts = 5; public loginstatus validateuser(string username, string password) { if (string.isnullorwhitespace(username)) throw new argumentnullexception("username"); //set password empty if null password = password ?? ""; //create connection using (var connection = new sqlconnection(configuration.connectionstring)) { //assign local variables int attemptsleft = maxattempts; string currentstatus = "active"; string userpassword = null; //get information user, query username have data. match password later on string query = "select top(1) [username], [password], [attemptsleft], [currentstatus] [information] username = @username"; using (var command = new sqlcommand(query, connection)) { command.parameters.addwithvalue("@username", username); command.commandtype = system.data.commandtype.text; connection.open(); using (var reader = command.executereader()) { //no rows.. invalid username if (!reader.hasrows) { connection.close(); return loginstatus.invalidcredentials; } //read first row (hence break) while (reader.read()) { attemptsleft = (int)reader["attemptsleft"]; currentstatus = (string)reader["currentstatus"]; userpassword = (string)reader["password"]; break; } reader.close(); } connection.close(); } //if account suspended dont bother password checking if (currentstatus.equals("suspended", stringcomparison.currentcultureignorecase)) { return loginstatus.suspended; } //invalid password lets handle invalid credentials logic if (!password.equals(userpassword)) { attemptsleft -= 1; //decrease attempts, lets stop @ 0 dont need negative attempts if(attemptsleft >= 0) { query = "update [information] set [attemptsleft] = @attemptsleft username = @username"; using (var command = new sqlcommand(query, connection)) { command.parameters.addwithvalue("@username", username); command.parameters.addwithvalue("@attemptsleft", attemptsleft); connection.open(); command.executenonquery(); connection.close(); } } //suspend account when attempts less or equal 0 if (attemptsleft <= 0) { query = "update [information] set [currentstatus] = @currentstatus username = @username"; using (var command = new sqlcommand(query, connection)) { command.parameters.addwithvalue("@username", username); command.parameters.addwithvalue("@currentstatus", "suspended"); connection.open(); command.executenonquery(); connection.close(); } //exit method login account suspended return loginstatus.suspended; } //exit invalid login credentials return loginstatus.invalidcredentials; } //if here lets reset login attempts 5, , account status active valid login query = "update [information] set [attemptsleft] = @attemptsleft, [currentstatus] = @currentstatus username = @username"; using (var command = new sqlcommand(query, connection)) { command.parameters.addwithvalue("@username", username); command.parameters.addwithvalue("@attemptsleft", maxattempts); command.parameters.addwithvalue("@currentstatus", "active"); connection.open(); command.executenonquery(); connection.close(); } //if got here every thing match return loginstatus.authorized; } } } public enum loginstatus { authorized, invalidcredentials, suspended }
to use can simple below (note have change view redirections)
[httppost] public actionresult index(string username, string password) { if(string.isnullorwhitespace(username)) { this.modelstate.addmodelerror("", "invalid login credential. no username sent."); return view(); } var manager = new usermanager(); var result = manager.validateuser(username, password); switch (result) { case loginstatus.authorized: return redirecttoaction("about", "home"); case loginstatus.invalidcredentials: this.modelstate.addmodelerror("", "invalid login credentials. username or password incorrect"); break; case loginstatus.suspended: this.modelstate.addmodelerror("", "account suspeneded"); break; } return view(); }
just fun rewrote simple stored procedure.
create procedure validateuser @username nvarchar(50), @password nvarchar(50) begin set nocount on; declare @userpassword nvarchar(50) = null declare @maxattempts int = 5 declare @attemptsleft int = 5 declare @currentstatus nvarchar(50) /* return codes: 0 = authorized 1 = invalidcredentials 2 = suspended */ select top(1) @userpassword = [username], @attemptsleft = [attemptsleft], @currentstatus = [currentstatus] [information] username = @username if @userpassword null begin select 1 [result], @maxattempts [attemptsremaining] return end --account suspended.. return suspended result if @currentstatus = 'suspended' begin select 2 [result], 0 [attemptsremaining] return end --passwords dont match (note case insensitive on default collation) if @password null or @password <> @userpassword begin --decrease attempts set @attemptsleft = @attemptsleft - 1 --if attempts left greater 0 set account active , decrease attempts remaining if @attemptsleft > 0 begin update [information] set [currentstatus] = 'active', attemptsleft = @attemptsleft username = @username select 1 [result], @attemptsleft [attemptsremaining] return end --else attempts left less or equal 0 therefore should suspended , attempts left set 0 (dont want negative attempts) else begin update [information] set [currentstatus] = 'suspended', attemptsleft = 0 username = @username select 2 [result], 0 [attemptsremaining] return end end --if here , can reset account status , max attempts next login attempt update [information] set [currentstatus] = 'active', attemptsleft = @maxattempts username = @username select 0 [result], @maxattempts [attemptsremaining] end go
then calling simple (note have changed return type call returns both status , attempts remaining.
method
public loginresult validateuserstoredprocedure(string username, string password) { if (string.isnullorwhitespace(username)) throw new argumentnullexception("username"); //set password empty if null password = password ?? ""; //create connection using (var connection = new sqlconnection(configuration.connectionstring)) { var result = new loginresult { attemptsremaining = 5, status = loginstatus.invalidcredentials }; try { using (var command = new sqlcommand("exec validateuser @username, @password", connection)) { command.parameters.addwithvalue("@username", username); command.parameters.addwithvalue("@password", password); command.commandtype = system.data.commandtype.text; connection.open(); using (var reader = command.executereader()) { while (reader.read()) { result.status = ((loginstatus)(int)reader["result"]); result.attemptsremaining = (int)reader["attemptsremaining"]; break; } reader.close(); } connection.close(); } return result; } catch (exception ex) { if (connection.state != system.data.connectionstate.closed) connection.close(); debug.writeline("error on sql query:" + ex.message); return result; } } }
result class
public class loginresult { public loginstatus status { get; set; } public int attemptsremaining { get; set; } } public enum loginstatus : int { authorized = 0, invalidcredentials = 1, suspended = 2 }
controller
[httppost] public actionresult index(string username, string password) { if (string.isnullorwhitespace(username)) { this.modelstate.addmodelerror("", "invalid login credential. no username sent."); return view(); } var manager = new usermanager(); var result = manager.validateuserstoredprocedure(username, password); switch (result.status) { case loginstatus.authorized: return redirecttoaction("about", "home"); case loginstatus.invalidcredentials: if (result.attemptsremaining < 5) this.modelstate.addmodelerror("", "invalid login credentials. username or password incorrect. attempts remaining:" + result.attemptsremaining); else this.modelstate.addmodelerror("", "invalid login credentials. username or password incorrect."); break; case loginstatus.suspended: this.modelstate.addmodelerror("", "account suspeneded"); break; } return view(); }
how optimize level of authorization rather weak. shows storing passwords plain text. topic together.
Comments
Post a Comment