Introduction
One of the first architectural concerns during the design of a multitier system is security. Systems communicating over the Internet are especially vulnerable to different kinds of malicious attacks. The security spans all layers of a system design from transport layer to fine-grained, selective access to the actual system functionality based on roles. For example in a system that manages client orders one could imagine two types of access rights: a lower level access where authenticated user can only browse the information without a possibility to make any changes and a higher level access where authenticated user is allowed to make modification to the underlying data.
This type of fine-grained security is typically achieved in two steps.
- Authentication is the process of verifying the identity of a user based on username and password
- Authorization is the process of granting or denying the authenticated user access to server-side resources
The DataSnap framework implements support for authentication and authorization through the “TDSAuthenticationManager” component introduced in RAD Studio XE.
Building the Server
Click on the “File – New – Other” menu and inside the “New Items” dialog double-click on the “DataSnap Server” wizard in “Delphi Projects – DataSnap Server” category to create a standalone Delphi DataSnap server application.

In the first tab of the wizard keep the default project type “VCL Forms Application” and on the second tab make sure to check “Authentication” and “Authorization” checkboxes.

Keep all the default values on the remaining wizard screens and click on “Finish” to generate the DataSnap server project.
Select “File – Save All” to save all the files in the new project generated by the wizard. In my case I am going to create a new “C:\DataSnapLabs\AuthenticationAndAuthorization\” folder, give the main unit of the project “FormServerUnit” name, keep default names for other units (typically “ServerMethodsUnit1” and “ServerContainerUnit1”) and name project “SecureDSServer”.
Double-click on the server container unit in the Project Manager to display the form designer. Notice that the “DataSnap Server” wizard generated additionally to normal server components also “DSAuthenticationManager1” component.

Notice also that “DSTCPServerTransport1.AuthenticationManager” property is now pointing to this new component.
If you switch to the code editor, for example pressing “F12”, you will also see that the wizard generated almost empty implementations of “OnUserAuthenticate” and “OnUserAuthorize” events.
procedure TServerContainer2.DSAuthenticationManager1UserAuthenticate(
Sender: TObject; const Protocol, Context, User, Password: string;
var valid: Boolean; UserRoles: TStrings);
begin
valid := True;
end;
procedure TServerContainer2.DSAuthenticationManager1UserAuthorize(
Sender: TObject; EventObject: TDSAuthorizeEventObject;
var valid: Boolean);
begin
valid := True;
end;
The minimal responsibility of the programmer is to set the “valid” parameter to true or false. Note the “var” modifier, which tells us, that the value that we assign to this parameter will persist beyond the local scope of these procedures. If we set “valid” to “false” in the authentication event no client will be able to connect to our DataSnap server. By default both implementations set “valid” to “true”, which effectively does not implement any restrictions on what clients can connect to our server and what server methods can be called. It is like there is no security - all clients have full access to all server methods.
Let’s start from adding authenticated users to roles. To keep the implementation simple, we are only going to check the value of “User” parameter passed to “Authenticate” event. If the “User” property is empty, then we set “valid” parameter to “false” preventing user from being able to call any server methods, otherwise we set “valid” parameter to “True”. If you want to call any of the server methods, you need to provide a non-empty username.
That’s the first step. The second step is to add authenticated users to roles. We are going to define a special role: “admins”. Any authenticated user will be added to the “admins” role and if the username is “admin”.
The last objective is to remove the “DSAuthenticationManager1UserAuthenticate” event implementation, which would effectively override our security settings and authorize calls to arbitrary server method for any authenticated user. Just remove the body of the event, which is just one line: “value := True;” and save. All empty events will automatically disappear.
I have removed “DSAuthenticationManager1UserAuthorize” event and modified the authentication events as follows:
procedure TServerContainer2.DSAuthenticationManager1UserAuthenticate(
Sender: TObject; const Protocol, Context, User, Password: string;
var valid: Boolean; UserRoles: TStrings);
begin
valid := User <> '';
if User = 'admin' then
UserRoles.Add('admins');
end;
There are two ways to implement role-based access to server methods. You can either use “DSAuthenticationManager.Roles” property or set permissions at design-time using Object Inspector, or you can do the same thing in code decorating server methods class and server methods with “TRoleAuth” custom attribute.
In this example we are going to use the later approach and use custom attributes in code.
Open server methods unit in the editor and add to the interface “uses” clause the “DSAuth” unit, where the “TRoleAuth” custom attribute is implemented. Now you can decorate individual server methods (or the whole server class) with this attribute. The first parameter of the “TRoleAuth” attribute takes a string with “AuthorizedRoles” and the second, optional parameter contains “DeniedRoles”. For demo purposed let’s restrict access to “ReverseString” method, so only users that are “admins” can call this method.
Here is my modified version of server methods unit interface section. The implementation remains the same.
unit ServerMethodsUnit2;
interface
uses
SysUtils, Classes, DSServer, DSAuth;
type
TServerMethods2 = class(TComponent)
private
public
function EchoString(Value: string): string;
[TRoleAuth('admins']
function ReverseString(Value: string): string;
end;
implementation
uses StrUtils;
function TServerMethods2.EchoString(Value: string): string;
begin
Result := Value + ' ' + Value;
end;
function TServerMethods2.ReverseString(Value: string): string;
begin
Result := StrUtils.ReverseString(Value);
end;
end.
Our DataSnap server with role-based security is now ready. It is time to implement the client.
Right-click on the project group node in the Project Manager and select “Add New Project”. Select “VCL Forms Application” from “Delphi Projects” category and “Save All”. It is best to save new files in the same folder as server project – in my case in “C:\DataSnapLabs\AuthenticationAndAuthorization\” folder. You can name the client’s main form unit “FormClientMain”, the whole project “SecureDSClient” and project group: “SecureDS”.
Before we can implement the client, the server has to be running. Right-click on the project group and select “Build All”. Make sure that the server project is active (its name should be displayed in “bold” in Project Manager) and select “Run -> Run Without Debugging”. Minimize the server window. The server needs to be running during the client development.
Building the Client
At this stage our project group consists of a server and a client project. The server is running and we need to implement the client. Let’s start from adding a “DataSnap Client Module” to our client.
Make sure that client project is active in the Project Manager and click on the “File – New – Other” menu and select “DataSnap Client Module” from the “Delphi Projects -> DataSnap Server” category.

On the first screen of the wizard keep the default “localhost” as DataSnap server location.
On the second screen keep the default “DataSnap standalone server” as the server project type.
On the third screen keep the default “tcp/ip” as the connection protocol.
On the last, fourth screen of the wizard we can test the connection to the server. By default the username and password are empty. If you click on the “Test Connection” button with an empty username, you would get the following error:

This is exactly what we are expecting! Users with empty “user name” are not authenticated to connect to our server.
Let’s enter any user name (but not “admin”!), for example “guest”, and click “Test Connection” again.
This time we should be able to successfully connect to the server.

Click on “Finish” to complete the wizard. Select “Save All” from “File” menu and keep default names of two new units added by the wizard: typically “ClientClassesUnit1” and “ClientModuleUnit1”.
Now it is time to build a simple user interface for our client application.
Drop two buttons and two edits on the client form. Name them “ButtonEcho”, “EditEcho”, “ButtonReverse” and “EditReverse”.
Select “File -> Use Unit” and add both files generated by the wizard to the client form unit and implement “OnClick” event handlers for both buttons.
My implementation of the client form looks like this:
implementation
uses ClientClassesUnit1, ClientModuleUnit1;
procedure TForm3.Button1Click(Sender: TObject);
begin
EditReverse.Text :=
ClientModule1.ServerMethods2Client.ReverseString(EditReverse.Text);
end;
procedure TForm3.ButtonEchoClick(Sender: TObject);
begin
EditEcho.Text :=
ClientModule1.ServerMethods2Client.EchoString(EditEcho.Text);
end;
end.
Run the client and test its functionality.
If you click on the “Echo” button, you should see the result of the call displayed in the “echo” edit.
If you click on the “Reverse” button, you should receive the error saying that the user named – in my case – “guest” is not authorized to perform the requested operation.

Again this is exactly what we were expecting!
During the process of adding a DataSnap Client Module to our client application, the wizard has embedded the username and password in the generated code.
Go to the ClientModuleUnit1 and select “SQLConnection1” component in the form designer. In the Object Inspector open its “Params” property and you can see the port number, username and password stored there.

If you change the value of “DSAuthenticationUser” to “admin” and rerun the client you should be able to call both server methods.
Summary
Connect with Us