About this Article Article

This article covers the following platforms

Delphi

More Articles about Data Abstract

DA00 - Article ROadmap for Data Abstract

DA01 - Data Abstract Overview

DA02 - Easy Steps to Create a Data...

DA03 - Introduction to Schemas

DA04 - Dynamic Method Binding

DA05 - Advanced options for controlling...

DA06 - Strongly Typed DataTables and...

DA08 - No-Code n-Tier Applications

DA09 - Why Data Abstract?

DA10 - The Business Processor in Depth

DA11 - Business Rules in Depth

DA13 - Using the NexusDB database with...

DA16 - Client-side Business Rules via...

DA17 - Data Abstract 3.0 New Features

DA19 - Master Detail using Params and...

DA20 - Data Abstract Adapters

DA21 - Authentication and application...

DA22 - ASTA to RemObjects Conversion

DA23 - Introduction to Data Abstract...

DA24 - Authentication and application...

DA25 - ASP.NET Support in Data Abstract

DA26 - Upgrading to Data Abstract...

DA28 - Easy Steps to Creating a Data...

DA29 - How to Configure Additional...

DA30 – Enhancements to Data Retrieval...

DA31 - Simplifying Permissions

DA32 - Master Detail using Where...

DA33 - Introduction to Dynamic Select...

DA34 - Introduction to the Reconcile...

DA35 - Building a Data Abstract Client...

DAL02 - Easy Steps to Creating a Data...

WP01 - Why Multi-Tier, why Data...

WP02 - Cross Platform Development with...

Articles for this product are grouped in:

Ready for 'Vinci'

Introduction

First Steps

Interoperability

Data Access

Architecture

Intermediate

Advanced

Contributions

Extensibility SDK

Features

Whitepaper

Update Pending

Legacy

Search Articles


Advanced Search...

DA31 - Simplifying Permissions

by Nathanial Woolls

Note from RemObjects: although we believe the content of this article to be correct, we do not warrant that it is so. We thank Nathanial very much for producing this, which we are pleased to publish here.

A Technique for Automated Permissions in Data Abstract

One of the most important aspects of any remotely accessed service framework is security. This is no different with the RemObjects SDK and Data Abstract. Luckily, the RemObjects SDK and Data Abstract do most of the heavy lifting for us. By providing a session management framework in the RemObjects SDK and an easy-to-use validation system in Data Abstract, security is ready to go (with some work from the developer) out of the box. And, with the small amount of code discussed in this article, we can have nearly all of our authentication and permission needs taken care of for us with almost no additional coding (after a small amount of initial work).

While the concepts presented in this article are straight-forward, and the code written fairly brief, it is suggested that you have a basic understanding of how the RemObjects SDK’s session managers work. You can read about session managers in the RemObjects SDK here. Another suggested article goes into further depth on session managers and how to use them with Data Abstract, and can be found here.

The idea behind the technique presented in this article is to first extend the session object in the RemObjects SDK to natively hold a list of permissions. While we could probably use the Values property of the regular TROSession object to store this, it is easy to extend the session object to cleanly hold a separate list of values for permissions, and this will be our first step. Then, we will utilize a seldom advertised feature in Data Abstract called Custom Attributes to automate the pairing and checking of permissions when accessing datasets and commands in Data Abstract.

Session Management

To use our own session object, we will use a TROEventSessionManager.

It should be noted that this technique is also possible with the TROInMemorySessionManager, which only requires using OnCustomCreateSession. We examine TROEventSessionManager here for completeness, as extra code is necessary to use our own session object with this component. The events we are interested in are OnCustomCreateSession and OnFindSession.

If we look at these events, you'll notice that the Session parameter's are passed as var's and out's. This allows us to subclass TROSession, creating our own session object with a Permissions property. We can then use our session object within the RemObjects SDK’s session management framework.

We'll start by defining our session object:

unit uAuthSession;

interface

uses uROSessions, Classes;

type

  TAuthSession = class(TROSession)
  private
    FPermissions: TStrings;
  public
    constructor Create(const aSessionID : TGUID); override;
    destructor Destroy; override;
    property Permissions: TStrings read FPermissions;
  end;

implementation

uses SysUtils;

{ TAuthSession }

constructor TAuthSession.Create(const aSessionID: TGUID);
begin
  inherited;
  FPermissions := TStringList.Create;
end;

destructor TAuthSession.Destroy;
begin
  FreeAndNil(FPermissions);
  inherited;
end;

end.

Now, we'll handle the two events we mentioned above to use our own session object instead of the regular TROSession object (again, only CustomCreateSession need be handled with the in-memory session manager):

procedure TSessionsController.SessionManagerCustomCreateSession(const
    aSessionID: TGUID; var Session: TROSession);
begin
  Session := TAuthSession.Create(aSessionID);
end;

procedure TSessionsController.SessionManagerFindSession(const aSessionID:
    TGUID; out aSession: TROSession);
var
  Session: TAuthSession;
begin
  aSession := nil;

  ... if code to find session here succeeds
  begin
    Session := TAuthSession.Create(aSessionID);
    aSession := Session;
	 ...
  end
end;

And that's it! From now on, when you would normally access your session object in your RemObjects SDK and Data Abstract services, you can now access them as a TAuthSession (with proper casting). Now, in our service's login method, we can use this to store permissions:

function TXXXXServices.Login(const Username, Password: string): Boolean;
var
  Session: TAuthSession;
begin
  Result := False;

  ...if login is successful
  begin
    Session := TAuthSession(Self.Session);
    Session.Permissions.Add(...);
    Session.Permissions.Add(...);
    Session.Permissions.Add(...);
  else
    DestroySession;
end;

Combining all of this code means that we have a clean place to hold a list of permissions in our session object, and can now use that list of permissions to validate remote service calls as well as dataset retrieval.

Data Access Validation

Data Abstract modules have three events that help simplify the validation of a user before allowing access to Data Abstract elements. These three events are:

  • ValidateCommandExecution
  • ValidateDatasetAccess
  • ValidateDirectSQLAccess

These events have method signatures like the following:

procedure TXXXXServices.DataAbstractServiceValidateDatasetAccess(
             Sender: TObject; const aConnection: IDAConnection;
             const aDatasetName: string; const aParamNames: array of string;
             const aParamValues: array of Variant;
             aSchema: TDASchema; var Allowed: Boolean);

As you can see, by handling these events you can inspect the request being made through Data Abstract, evaluate any permissions or conditions, and then set the Allowed parameter accordingly.

Now, at this point it would be trivial to simply check our session object's permissions in these Validate events, check the dataset or command name, do some custom lookup to see what permissions were needed to access that dataset or command, and act accordingly. The downside to this is that, as we add commands and datasets, we must continue to update the code in these Validate methods to keep checking our new Data Abstract elements for authorization.

Tying it All Together

Enter one of the least mentioned features of Data Abstract: Custom Attributes. This is, quite simply, a property, CustomAttributes, with a type of TStrings, which is found on Data Abstract Data Tables, Commands, and Fields. The use of this property, like Tag, is totally up to the developer. The CustomAttributes property can be accessed at design time through the Data Abstract components and also through Schema Modeler:

Armed with this knowledge, we can use the following simple code to tie all of the pieces together:

procedure TXXXXServices.DataAbstractServiceValidateDatasetAccess(Sender:
    TObject; const aConnection: IDAConnection; const aDatasetName: string;
    const aParamNames: array of string; const aParamValues: array of
    Variant; aSchema: TDASchema; var Allowed: Boolean);
var
  I: Integer;
  Session: TAuthSession;
  Dataset: TDADataset;
begin
  Session := TAuthSession(Self.Session);
  Dataset := MySchema.Datasets.DatasetByName(aDatasetName);
  for I := 0 to Dataset.CustomAttributes.Count - 1 do
  begin
    if Session.Permissions.IndexOf(Dataset.CustomAttributes[I]) = -1 then
    begin
      Allowed := False;
      Break;
    end;
  end;
end;

With this code in place for datasets and commands, in Schema Modeler we simply need to list the permissions needed to access our datasets and commands in the Custom Attributes area. After this, Data Abstract and the RemObjects SDK’s session management, along with our custom session object, take care of the rest! Trying to access a dataset for which our session object does not contain a Permissions entry results in a nice EDADatasetNotAccessible exception from Data Abstract.