I would like to create a type of login window in Delphi. Unfortunately I can't get it to match the username and password.
I have a basic .mdb database, with an Users table. In this table, there is a Username and Password. I want Delphi to check the username and password in the database and if it matches those in the edit boxes, it goes to the next form or shows a message, otherwise it does nothing. When I enter the database's first row, username and password values, I get success, but with the second, nothing. I feel like I need a way to get it to move on to the second rows values and check those and so on. There is also currently no relationships in the database.
This is my layout of Data Access: ADOConnection -> ADOTable -> DataSource
Thanks in advance!
As per your guess, one solution can be to move record by record to check each one. Example:
function MatchPass(Table: TADOTable; const Name, Pass: string): Boolean;
begin
Result := False;
Table.First;
while not Table.Eof do begin
if Table.FieldByName('Username').AsString = Name then begin
Result := Table.FieldByName('Password').AsString = Pass;
Exit;
end;
Table.Next;
end;
end;
Can be called like:
if MatchPass(ADOTable1, Edit1.Text, Edit2.Text) then
..
Another solution can be to let the ADOTable search for a corresponding record:
function MatchPass(Table: TADOTable; const Name, Pass: string): Boolean;
begin
Result := Table.Locate('Username;Password', VarArrayOf([Name, Pass]), []);
end;
Related
I have a MySql database which has a users table and all the other tables are connected to the users table.
If we log in to an user that has id = 1. We want to make all the requests to mysql using the user_id = 1, so it'll be displayed only the data from this specific user.
I have a login screen in my Delphi app where the user can type his username and password. After a successfully login I want to take thie user_id to use and throughout this application to make the calls. How should I do it?
After the login in successfully, should I sotre this ID in Delphi somehow and access it whenever we want to make some call? If so, how do I save this id in Delphi so I can use it in several forms?
A way to do this is to add a DataModule to your project and make the UserID a
field of this.
So, using Delphi's default naming, you would have
type
TDataModule1 = class(TDataModule)
[...]
public
property UserID : Integer read FUserID;
end;
[...]
var
DataModule1 : TDataModule1;
Then, add to TDataModule1 the db-access components your project uses, e.g. a Query
component to access the Users table where user details are stored and a method to
try to login in a given user, and also a db-connection component of the type your db-access components use. So you could add a method like this:
function TDataModule1.LogInUser(const UserName : String) : Boolean;
begin
try
if qUsers.Active then
qUsers.Close;
FUserID := -1; // deliberately set an invalid UserID
Assert(Trim(UserName) <> '')); // raise exception if UserName is blank
// qUsers Sql.Text property would be something like 'select * from users where username = :username'
qUsers.ParamByName(UserName).Value := UserName;
qUsers.Open;
if qUsers.Locate('UserName', UserName, []) then
FUserID := qUsers.FieldByName('UserID').AsInteger;
finally
Result := FUserID >= 0; // assuming zero is a valid UserID
end;
end;
and save the module under the name, say, of DataModule1u. Notice that this method omits any reference to the user's password: This is because if you leave your db connection component's LoginPrompt property at the default True value, it will pop up a prompt for the user's password when the LogInUser method executes.
Once you've got that far, go to your Login form's unit and edit it to USE DataModule1u.
Then you can add code to it to use the LogInUser method like so:
if DataModule1.LogInUser(UserName) then begin
Caption := 'User: ' + IntToStr(DataModule1.UserID);
// do something else with DataModule1.UserID's value
end;
Obviously you could (and usually should) add all your db-access components to the datamodule
and USE it in your forms which do db-access.
Hope this is all clear, if not then ask.
i would like to know whether a user inputs the correct combination of Domain, User and Password for his Active Directory user.
I tried to make a very simple program that is not able to connect but by reading the error message i can know if the user/password is correct.
This is trick based (the logic is on reading the Exception message), anyway i testd this prototype on 2 servers and i noticed that the excpetion messages change from server to server so this is not reliable.
uses adshlp, ActiveDs_TLB;
// 3 TEdit and a TButton
procedure TForm4.Button1Click(Sender: TObject);
Var
aUser : IAdsUser;
pDomain, pUser, pPassword : string;
myResult : HRESULT;
Counter: integer;
begin
pDomain := edtDomain.Text;
pUser:= edtUser.Text;
pPassword := edtPwd.Text;
Counter := GetTickCount;
Try
myResult := ADsOpenObject(Format('LDAP://%s',[pDomain]),Format('%s\%s',[pDomain,pUser]),pPassword,
ADS_READONLY_SERVER,
IAdsUser,aUser);
except
On E : EOleException do
begin
if (GetTickCount - Counter > 3000) then ShowMessage ('Problem with connection') else
if Pos('password',E.Message) > 0 then ShowMessage ('wrong username or password') else
if Pos('server',E.Message) > 0 then ShowMessage ('Connected') else
ShowMessage('Unhandled case');
memLog.Lines.Add(E.Message);
end;
end
end;
The reason why i set "Connected" if the message contain "server" is that on my
local machine (on my company ldap server in fact) in case all is fine (domain, user and password) the server replies "The server requires a safer authentication", so the "server" word is in there, while in other cases it says "wrong user or password". SInce this must work on itlian and english servers i set "server" and "pasword" as reliable words. Anyway i tested on another server that gives differente errors.
I started from a reply to this question to do the above.
How can i check if the user set the correct password or not in a more reliable way using a similar technique?
UPDATE (found solution)
Thanks to the replies i managed to write this function that does what i need. It seems quite reliable up to now, I write here to share, hoping it can help others:
// This function returns True if the provided parameters are correct
// login credentials for a user in the specified Domain
// From empirical tests it seems reliable
function UserCanLogin(aDomain, aUser, aPassword: string): Boolean;
var
hToken: THandle;
begin
Result := False;
if (LogonUser(pChar(aUser), pChar(aDomain), pChar(aPassword), LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, hToken)) then
begin
CloseHandle(hToken);
Result := True;
end;
end;
You need to check the error code that ADsOpenObject returns. do not base your error checking on the returned exception messages.
if the function succeeded it will return S_OK, otherwise you need to refer to ADSI Error Codes, specifically, the LDAP error codes for ADSI
When an LDAP server generates an error and passes the error to the
client, the error is then translated into a string by the LDAP client.
This method is similar to Win32 error codes for ADSI. In this example,
the client error code is the WIN32 error 0x80072020.
To Determine the LDAP error codes for ADSI
Drop the 8007 from the WIN32 error code. In the example, the remaining hex value is 2020.
Convert the remaining hex value to a decimal value. In the example, the remaining hex value 2020 converts to the decimal value
8224.
Search in the WinError.h file for the definition of the decimal value. In the example, 8224L corresponds to the error
ERROR_DS_OPERATIONS_ERROR.
Replace the prefix ERROR_DS with LDAP_. In the example, the new definition is LDAP_OPERATIONS_ERROR.
Search in the Winldap.h file for the value of the LDAP error definition. In the example, the value of LDAP_OPERATIONS_ERROR in
the Winldap.h file is 0x01.
For a 0x8007052e result (0x052e = 1326) for example you will get ERROR_LOGON_FAILURE
From your comment:
Since the function always raises an exception i am not able to read
the code
You are getting an EOleException because your ADsOpenObject function is defined with safecall calling convention. while other implementations might be using stdcall. when using safecall Delphi will raise an EOleException and the HResult will be reflected in the EOleException.ErrorCode, otherwise (stdcall) will not raise an exception and the HResult will be returned by the ADsOpenObject function.
i would like to know whether a user inputs the correct combination of
Domain, User and Password for his Active Directory user.
You can use LogonUser function to validate user login e.g. :
if (LogonUser(pChar(_Username), pChar(_ADServer), pChar(_Password), LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, hToken)) then
begin
CloseHandle(hToken);
//...
//DoSomething
end
else raise Exception.Create(SysErrorMessage(GetLastError));
Note: You must use LogonUser within a domain machine to be able to use the domain login or the function will always return The user name or password is incorrect
The alternative is using TLDAPSend e.g. :
function _IsAuthenticated(const lpszUsername, lpszDomain, lpszPassword: string): Boolean;
var
LDAP : TLDAPSend;
begin
Result := False;
if ( (Length(lpszUsername) = 0) or (Length(lpszPassword) = 0) )then Exit;
LDAP := TLDAPSend.Create;
try
LDAP.TargetHost := lpszDomain;
LDAP.TargetPort := '389';
....
LDAP.UserName := lpszUsername + #64 + lpszDomain;;
LDAP.Password := lpszPassword;
Result := LDAP.Login;
finally
LDAP.Free;
end;
end;
How can i check if the user set the correct password or not in a more
reliable way using a similar technique?
Try to use FormatMessage function
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,
nil,
myResult,
LANG_ENGLISH or SUBLANG_ENGLISH_US,
lpMsg,
0,
nil);
MessageBox(0, lpMsg, 'Msg', 0);
I get (HRESULT) 0x8007052e (2147943726) "unknown user name or bad password" when I use a wrong password. And there is no EOleException, try:
hr := ADsOpenObject('LDAP://'+ ADomain + '/OU=Domain Controllers,' + APath,
AUser, APwd,
ADS_SECURE_AUTHENTICATION or ADS_READONLY_SERVER,
IID_IADs, pObject);
if (hr=HRESULT(2147943726)) then ShowMessage ('wrong username or password')
I just started Delphi.
I'm making a simple login form, and the text file (UserInfo.txt) is set up like this
I just want Delphi to run through the TextFile to look for the string, then if that string equals edtUseranme.Text then it must check if that string is equal to the string on the second line.
If it equals then it can continue to the next Form
Here is my code:
for I := 0 to Eof(tFile) do
begin
Readln(tFile, sLine);
Inc(I);
if ReadLn(tFile) = edtUsername.Text then
begin
if edtUsername.Text = then
begin
frmMain.Show;
frmLogin.Hide;
end
else
begin
ShowMessage('INPUT INVALID (Try again)');
end;
end;
end;
At that second if statement, I don't know what to put after =.
Like Ken White suggested, you could redesign your text file and then use a TStringList to parse it. That would certainly make it easier to work with the data. However, if you want to stick with the code you already have, then you could do something more like this instead:
var
tFile: TextFile;
//Declare variables for temporally storing read username and password
sUser, sPass, sDivider: string;
Valid: Boolean;
...
Valid := False;
//use while loop to read every line till the end of file
while not Eof(tFile) do
begin
//Read username into sUser variable
Readln(tFile, sUser);
//Read password into sPass variable
Readln(tFile, sPass);
//Read divider into sDivider variable
Readln(tFile, sDivider);
//Use if clause to perform multiconditional logical comparison to see if
//read username and password equals to those specified in suitable edit
//boxes
//NOTE: Each separate logical condition must be in its own brackets
if (sUser = edtUsername.Text) and (sPass = edtPassword.Text) then
begin
//If conditions are met we set local variable Valid to true
Valid := True;
//and then break the loop
Break;
end;
end;
//If local variable valid was set to True show the main form
if Valid then
begin
frmMain.Show;
frmLogin.Hide;
end else
//else we show message about invalid input entered
ShowMessage('INPUT INVALID (Try again)');
Your file format is poorly designed for what you're wanting to do, I'm afraid.
You'd be better off using a more standard format (realizing, of course, that it's totally insecure to store names and passwords as plain text). Delphi makes it easy to work with name/value pairs using TStringList with text like this:
Name=Value
This would mean your text file would contain the username=password type lines:
Bob=Password
Sue=Bambi
You then use TStringList to load the file and read the value associated with the name, like this:
var
SL: TStringList;
UserName, Password: string;
begin
UserName := Edit1.Text; // Your username control
Password := Edit2.Text;
// You should check to make sure that both the username and password
// were entered here before proceeding. I'll leave that to you to do.
SL := TStringList.Create;
try
SL.LoadFromFile('C:\Temp\UserInfo.txt'); // Replace with your filename
// The next line looks for a name in the stringlist equal to the
// entered username, and if it's found compares its value to the
// password that was provided.
//
// You'll need to handle making sure that you do a case-sensitive
// (or non-sensitive, depending on your needs) comparison is done
// if needed.
if SL.Values[UserName] = Password then
// Login succeeded
else
// Login failed.
finally
SL.Free;
end;
end;
I have a program that allows to login by using the windows login information, and I am trying to get the windows groups members when the user enter his password, I wrote a small function similar to my code :
procedure ShowADSPath(UserName, Password: widestring);
var Group : IADs;
begin
try
OleCheck(ADsOpenObject('WinNT://Server/Group1',
UserName,
Password, ADS_SECURE_AUTHENTICATION, IADs, Group));
if (Group <> nil) and (Group.Class_ = 'Group') then
ShowMessage(Group.ADsPath);
Group.release;
Group:= nil;
except
ShowMessage('NOT ACCESSDE');
end;
end;
so when the entered username and password are right the program returns the path for the group
when wrong 'NOT ACCESSED' appears.
the function works well if I enter the right username and password for the first time, or if I enter wrong username and password data it works fine too.
the problem is when I call the function second time it doesn't work as expected like:
when I run my program and first I enter wrong password and call my function 'NOT ACCESSED' will appear as expected, but if I recall the function even with the right password it returns 'NOT ACCESSED' too.
Also when I run my program and first I enter right password and call my function the groups path appears as expected, but if I recall the function with the wrong password it returns the path too.
it looks like my connection data became saved, and I need to free the memory but I don't know how.
any body can help?
Finally I could find a solution for my issue, which looks like a Microsoft API issue described in this article:
http://support.microsoft.com/kb/218497
actually the API function ADsOpenObject is opening a connection with the server using the credentials you pass but it never close that connection, I tried to close it but it doesn't became close in the session, so I used another API to check the existence of the object first, check out this function it worked for me:
procedure ShowADSPath(UserName, Password: widestring);
function CheckObject(APath: String): IDispatch;
var
Moniker: IMoniker;
Eaten: integer;
BindContext: IBindCtx;
Dispatch: IDispatch;
begin
Result := nil;
OleCheck(CreateBindCtx(0, BindContext));
OleCheck(MkParseDisplayName(BindContext, PWideChar(WideString(APath)),
Eaten, Moniker));
OleCheck(Moniker.BindToObject(BindContext, nil, IDispatch, Dispatch));
Result := Dispatch;
end;
var Group : IADs;
begin
try
if CheckObject('WinNT://Server/Group1,group') <> nil then
OleCheck(ADsOpenObject('WinNT://Server/Group1,group',
UserName,
Password, ADS_SECURE_AUTHENTICATION, IADs, Group));
if (Group <> nil) and (Group.Class_ = 'Group') then
begin
ShowMessage(Group.ADsPath);
Group.release;
Group:= nil;
end;
except
ShowMessage('NOT ACCESSDE');
end;
end;
I have stored a list of items from gridView into the registry as below:
frmPOSConfigSet.tblCatItems.First;
while not frmPOSConfigSet.tblCatItems.Eof do
begin
RegItemSetting.WriteString('stk Group\Candy',
frmPOSConfigSet.tblCatItemsCODE.Text,
frmPOSConfigSet.tblCatItemsSPHOTO.Text);
frmPOSConfigSet.tblCatItems.Next;
end;
In Registry Editor, I have this:
stk Group
- Candy
-> YUPI_GUM_HB , c:\Users\chai\Pictures\POS Item Images\image1.jpg
-> YUPI_GUM_SBKISS , c:\Users\chai\Pictures\POS Item Images\image2.jpg
After I close the form and open it again, all values in gridView are gone. How can I retrieve the ident (eg. YUPI_GUM_HB) and its value (eg.c:\Users\chai\Pictures\POS Item Images\image1.jpg) from the registry and put it in the gridView when I load the form?
It doesn't quite look like you're using TRegistry (too many parameters to WriteString), but if you were, you could use it to get everything back out of the registry. I suspect you're stuck because you want to call ReadString, but you don't know the registry values' names, so you don't know what to pass into ReadString.
You can get a list of all the values' names by calling GetValueNames. Pass it a TStringList (or any other TStrings descendant), and that method will fill the list with all the value names.
var
Names: TStrings;
Name, Data: string;
i: Integer;
begin
RegItemSetting.OpenKeyReadOnly('stk Group\Candy');
Names := TStringList.Create;
try
RegItemSetting.GetValueNames(Names);
for i := 0 to Pred(Names.Count) do begin
Name := Names[i];
Data := RegItemSetting.ReadString(Name);
end;
finally
Names.Free;
end;
end;