Milan Vydareny
Milan Vydareny

Reputation: 121

How can I obtain the authenticated Username and Password in a Datasnap Server Methods unit?

Datasnap authentication is pretty straightforward once you use the correct parameter names (explained in Delphi Datasnap Server User Authentication). The next problem is to be able to use those same credentials when using a FireDAC Database Connection. The answer seems to be implied in Most efficient way to pass SQL Login credentials to Delphi Datasnap servers? although "simply forwarded" doesn't really explain how to accomplish the code. Further, these credentials should be authenticated as the same credentials used to log on to the Datasnap server. This will prevent impersonation at the database level.

So far I haven't been able to discover any way to obtain the current Datasnap user credentials programmatically from within the Server Methods unit. For example, in a BeforeConnect event. The code I'm working on is a standalone server built with Delphi XE7 using a Session lifecycle.

Here is a description of the events that take place when connecting to a Datasnap server and requesting data:

Let me explain further:

By using a few ShowMessage instructions I can trace the flow of Datasnap as I connect and make a data request. Running the server under Test allows me to display the contents of the various parameters that accompany the events. Once set up, running a client that connects to the server and requests data results in the following:

Client Login Button

Client GetData Button

Data is returned to client (At this time the database credentials are hard coded into the TFDConnection ConnectionDefinition just to get it to work.)

OnUserAuthorize presents Sender parameter with a nil value; also presented is a TDSAuthorizeEventObject that doesn't contain any references that I have been able to use to find the ServerMethods instance, and, finally, a parameter named Valid, a boolean value used to authorize the user.

Note that TDSAuthorizeEventObject contains a reference to TDSServerMethodUserEventObject that DOES contain the Username along with Roles, Authorized Roles and Denied Roles. However, so what? This brings me back to my original question: How do I communicate this to code in the ServerMethodsUnit?

Upvotes: 3

Views: 4066

Answers (2)

Milan Vydareny
Milan Vydareny

Reputation: 121

After some time I stumbled on the exact methodology to do this in a white paper by Bob Swart. There are several methods that will enable you to persist information during a Datasnap session. These belong to TDSSessionManager.GetThreadSession and are GetData, PutData, RemoveData, HasData, GetObject, PutObject, RemoveObject and HasObject. These methods actually manage two dictionaries that are part of the Session. So for example, in the UserAuthentication event you might store the username as follows:

TDSSessionManager.GetThreadSession.PutData('entrykey', UserName);

Later, in a OnBeforeConnect event for a database connection, you can retrieve these values for use in the connection:

DBUserName := TDSessionManager.GetThreadSession.GetData('entrykey');

In practice I find the "object" forms of greater value but either form offers a powerful way to persist your own data for the life of a session without resorting to external media.

Upvotes: 7

mjn
mjn

Reputation: 36664

In your linked question, the TServerContainer1.DSAuthenticationManager1UserAuthenticate method has User and Password parameters. Your server side code can retrieve the credentials from the request parameters, validate them, and re-use them for authentication with the database.

procedure TServerContainer1.DSAuthenticationManager1UserAuthenticate(
  Sender: TObject; const Protocol, Context, User, Password: string;
  var valid: Boolean; UserRoles: TStrings);

if UserService.isUserValid(User, Password) then
begin
  // use User and Password ... 

end; 

Upvotes: 0

Related Questions