0

Using Delphi XE2.

I have a database with a table in it called users. It has a user_id field, username field, a password field and an Active field in it. (the user_id is a unique number that identifies each user... 1,2,3 etc).

I am writing a database software package which requires username and passwords to login to it. (I already have a login form created).

How do I get it to match/check the usernames and passwords in the database then allow user to continue into the software? Also I would like the Active Field to store in the database 'Y' if user is logged in or 'N' if user is not logged in. Is this doable?

I am connected to the users table via TADQuery and TDataSource.

Example below of function which I thought would get me started(calling it at point of clicking the login button on the login form).

function TfrmLogin.CheckUser: Boolean;
begin
while not dmData.qryUser.Eof do
begin
if(editName.Text <> qryUser.FieldByName('uname').AsString) or (editPassword.Text <> qryUser.FieldByName('pword').AsString)
  then ShowMessage('Username and/or Password not recognised');
    Exit;
End;
Sharpie
  • 373
  • 2
  • 15
  • 34
  • Why would you want to store logged on state in the db? – David Heffernan Feb 10 '15 at 16:24
  • Honestly, just seeing if it can be done. If you advise against storing it then I'll accept your opinion. I haven't been doing this for very long. – Sharpie Feb 10 '15 at 16:27
  • 2
    Which part of the task are you having trouble with? What have you tried so far, and where did you get stuck? – Rob Kennedy Feb 10 '15 at 19:36
  • Allowing a user to log into the software with user credentials stored in the database....Thats where I am up to. I have tried the following, if name.text / password.text = fieldbyname('name').asstring / fieldbyname('password').asstring then begin LoginForm.Hide; mainform.create(self); try Mainform.showmodal finally mainform.free else ShowMessage('uname or pword not recognized'), Close; Sorry for the rubbish example, dont really know how else to put it. – Sharpie Feb 10 '15 at 20:17
  • @Dag Put some code into your question (indent 4 spaces so that it formats as code). Use a while loop to go through your dataset until you have a match or eof. Google for 'Delphi login form'. – Jan Doggen Feb 10 '15 at 21:10
  • edited question with code example, thanks – Sharpie Feb 10 '15 at 21:45
  • I'm assuming your problem is case insensitivity...? – Jerry Dodge Feb 10 '15 at 22:11
  • Honestly Jerry I don't know. – Sharpie Feb 10 '15 at 22:29
  • Don't verify them both at the same time. Loop to locate the username first. If not found, exit. If found check and verify password. If username is not unique, locate ID first, then check name and pass. – Sertac Akyuz Feb 10 '15 at 23:07
  • BTW, search on the web for an example on how to loop records. – Sertac Akyuz Feb 10 '15 at 23:12
  • 1
    Please, please, please do not store passwords in plain text in your database. At the very least, store a hashed version and compare the hash. Do some research into some basic things you can do to protect you and your users. – Sam M Feb 11 '15 at 06:58
  • my apologies i forgot to mention that the passwords stored in the db are hashed – Sharpie Feb 11 '15 at 11:12
  • There's a serious logic error in your `if` condition. You're saying "fail if the username doesn't match ***OR*** the password doesn't match". Therefore, it is guaranteed to fail for most of your records (it might pass for only 1 of them). What you want to do instead is: "if the username _matches_ **then** check the password (and fail if the password doesn't match)". – Disillusioned Feb 11 '15 at 11:28

2 Answers2

1

Looping through all rows in the database will not suffice, especially when you get more than a handful of users. You need to SELECT from the database for the specific user, and see if you get results back. You can do the SELECT based just on the username:

qryUser.SQL.Text := 'SELECT uname, pword FROM users WHERE uName = :uname';
qryUser.ParamByName('uname').AsString := editName.Text;
try
  qryUser.Open;
  if qryUser.IsEmpty then // No record found for user
    // Handle error
  else
  begin
    if qryUser.FieldByName('pword').AsString <> editPassword.Text then
      // Handle password mismatch;
  end;
finally
  qryUser.Close;
end;

It's not clear from your question which database components you're using (TADQuery might be a typo for TADOQuery, or it might be something else). If in fact it is TADOQuery, you'll need to make a couple of small changes to the code. (Actually, only three minor changes; two in the assignment of the parameter and one that reads the password value.)

qryUser.SQL.Text := 'SELECT uname, pword FROM users WHERE uName = :uname';
qryUser.Params.ParamByName('uname').Value := editName.Text;
try
  qryUser.Open;
  if qryUser.IsEmpty then // No record found for user
    // Handle error
  else
  begin
    if qryUser.FieldByName('pword').Value <> editPassword.Text then
      // Handle password mismatch;
  end;
finally
  qryUser.Close;
end;
Ken White
  • 123,280
  • 14
  • 225
  • 444
0

I'm with Sam, if possible do not store passwords in the database. If the database supports Active Directory Authentication (MS/SQL, Oracle, DB2, MySQL, SyBase) use the user name and password to validate against Active Directory before trying a connection to the database. Then only store the user name and active flag in the Users table.

This function ask Active Directory if the user and password are valid, before you ever try to make a connection to the database. Then you can build your connection parameters to the database (I assume you are using FireDAC and a TADConnection see here for instructions). Try to open the connection, if it fails the user does not have access to the database at all. If it passes, then query the Users table like Ken suggest but test the active field instead of the password. This way no one can see a users password and you do not have to manage passwords in your application. By using this method someone still needs to know a users Active Directory password to access your application even though your using Active Directory Authentication on the database.

function TfrmPassword.ActiveDirectoryValidate: Boolean;
var
  LHandle: THandle;
  lDomainName: String;
begin
  Screen.Cursor := crHourglass;
  try
   // Get the Domain Name
   lDomainName := GetEnvironmentVariable('USERDOMAIN');
   // Test the user Logon
   Result := LogonUser(PWideChar(edtUserID.Text),
                       PWideChar(lDomainName),
                       PWideChar(edtPassword.Text),
                       LOGON32_LOGON_NETWORK,
                       LOGON32_PROVIDER_DEFAULT,
                       LHandle);
   // If True, we got a Handle, so close it
   if Result then
    CloseHandle(LHandle);

  finally
   Screen.Cursor := crDefault;
  end;
end;
TDC
  • 377
  • 1
  • 6