#1 2014-01-30 19:30:54

foncci
Member
Registered: 2013-11-15
Posts: 53

User authentication proposal

Hi,

What about if authentication is accomplished using an INTERFACE instead of a class (TSQLAuthUser). It means that any class which implements that interface could be used by TSQLRestServer.Auth to authenticate User and Password. The advantages are pretty obvious, we could authenticate against a text file, ldap server a "non supported" database server or whatever we want.

Regards,

Al

Offline

#2 2014-01-30 21:42:31

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,662
Website

Re: User authentication proposal

TSQLAuthUser is just a container, without any method.
I do not understand why an interface may replace it.

You can already authenticate against anything, a text file, ldap server a "non supported" database server or whatever we want, by sub-classing the following class:

  /// abstract class used to implement server-side authentication in TSQLRestServer
  // - inherit from this class to implement expected authentication scheme
  TSQLRestServerAuthentication = class

Offline

#3 2014-01-31 00:19:34

foncci
Member
Registered: 2013-11-15
Posts: 53

Re: User authentication proposal

TSQLAuthUser inherits from TSQLRecord so it is not just a container and the idea behind it is to support authentication against the ORM. Today Mormot is much more than an ORM.

I understand you point but sub-classing TSQLRestServerAuthentication class is a lot of meaningless work. My point is that I want to use TSQLRestServerAuthenticationDefault exactly as it is implemented except for the way it "compares the username and password against the database". That's why I'm thinking on an Interface that implements a method for get username, password and privilege from another source. It will decouple the authentication process from the ORM or at least make things easier for non ORM users like me. With it I can still using TSQLRestServerAuthenticationDefault without any changes. Authentication method will be independent of the way we get the credentials.
Something like this would be good:

IAuthUser = interface
  function GetUser(aUser : RawUTF8) : IAuthUser; // If the user doesn't exist raise except
  function GetUser(aUser : RawUTF8) : boolean; // If you don't want an exception
  function LogonName : RawUTF8; 
  function PasswordHashHexa: RawUTF8; 
  function Privileges; 
end; 

function TSQLRestServerAuthenticationDefault.Auth(Ctxt: TSQLRestServerURIContext): boolean;
var aUserName, aPassWord, aClientNonce, aSalt: RawUTF8;
    User: IAuthUser;
    Session: TAuthSession;
begin
  result := true;
  if AuthSessionRelease(Ctxt) then
    exit else
  if UrlDecodeNeedParameters(Ctxt.Parameters,'PassWord,ClientNonce') then begin
    // GET ModelRoot/auth?UserName=...&PassWord=...&ClientNonce=... -> handshaking
    while Ctxt.Parameters<>nil do begin
      UrlDecodeValue(Ctxt.Parameters,'USERNAME=',aUserName);
      UrlDecodeValue(Ctxt.Parameters,'PASSWORD=',aPassWord);
      UrlDecodeValue(Ctxt.Parameters,'CLIENTNONCE=',aClientNonce,@Ctxt.Parameters);
    end;
    User := AuthClassX(aUserName);
     try
      if User.fID=0 then
        exit; // unknown user name
      // check if match TSQLRestClientURI.SetUser() algorithm
      aSalt := aClientNonce+User.LogonName+User.PasswordHashHexa;
      if (aPassWord<>SHA256(fServer.Model.Root+Nonce(false)+aSalt)) and
         // if didn't try with current nonce, try with previous 5 minutes nonce
         (aPassWord<>SHA256(fServer.Model.Root+Nonce(true)+aSalt)) then
        exit;
      // now client is authenticated -> create a session
      fServer.SessionCreate(User,Ctxt,Session);
      if Session<>nil then
        Ctxt.Returns(['result',Session.fPrivateSalt,'logonname',Session.User.LogonName]);
    finally
    end;
  end else
    if PosChar(Ctxt.Parameters,'&')=nil then
      // only UserName=... -> return hexadecimal nonce content valid for 5 minutes
      Ctxt.Results([Nonce(false)]) else
      // parameters does not match any expected layout
      result := false;
end;

Last edited by foncci (2014-01-31 03:16:12)

Offline

#4 2014-01-31 12:37:58

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,662
Website

Re: User authentication proposal

In all cases, you will never re-write TSQLRestServerAuthenticationDefault as you did.
But write a dedicated class.

You can run the TSQLRestServer without any backend DB, e.g. with TSQLRestServerFullMemory.
Therefore, TSQLAuthUser can already be pure in-memory objects.

And you can also write your own Auth() overriden method which does not have any TSQLAuthUser storage, but create one temporary in-memory instance.
Then it will work, even with TSQLRestServer.SessionGetUser(), which will make a copy of this temporary instance.

Using an Interface won't allow to implement the ORM part of TSQLAuthUser directly.

Offline

#5 2014-01-31 16:29:06

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,662
Website

Re: User authentication proposal

I've made a code refactoring of TSQLRestServerAuthentication class to add some new GetUser / SessionCreate / ClientComputeSessionKey methods, to avoid redundant code and allow better granularity of operation.
See http://synopse.info/fossil/info/b46ea7bb0c

You can know easily:
- Change the password check method;
- Create a TSQLAuthUser instance on the fly, without relying on the corresponding table in the ORM.

Hope it helps.

Offline

#6 2014-01-31 18:03:55

foncci
Member
Registered: 2013-11-15
Posts: 53

Re: User authentication proposal

ab, these are great!!! news. I understand you love ORM and you create mORMot thinking of ORM but today mORMot IS BETTER THAN REMONJECTS SDK and I was a Rem Object SDK User so I have compared both frameworks before deciding for mORMot. I suggest that you think a way to decouple as much as possible the ORM from the Rest FrameWork as RemObject did with SDK and DataAbstract(it is not exactly and ORM but it tries to behave like one). mORMot is faster easier and more understandable than RemObject, so it is time to have a Rest-ORM-Independent FrameWork. With this you will get hundreds of users from RemObject, RealThinClient and other frameworks.
IMHO
Thanks, Al

Offline

#7 2014-01-31 22:18:28

foncci
Member
Registered: 2013-11-15
Posts: 53

Re: User authentication proposal

Doing something like this is not working. The problem raises in TAuthSession.Create(aCtxt: TSQLRestServerURIContext; aUser: TSQLAuthUser); because it is expecting a group in if User.GroupRights.fID<>0 then begin ... and I don't know a "good" way to create a group on the fly. It should be nice to have a new constructor in TSQLAuthUser to do that and create a group with privileges that I can store in my own users table.

type
  TIntegradorAuthenticationDefault = class(TSQLRestServerAuthenticationDefault)
   protected
    function GetUser(Ctxt: TSQLRestServerURIContext; const aUserName: RawUTF8): TSQLAuthUser; override;
  end;

function TIntegradorAuthenticationDefault.GetUser(Ctxt: TSQLRestServerURIContext; const aUserName: RawUTF8): TSQLAuthUser;
begin
  Result := TSQLAuthUser.Create;
  Result.ID := 100; // Tiene q tener un valor para que pase x una condición en TAuthSession.Create
  Result.LogonName := 'alfonso';
  Result.PasswordPlain := 'probando';
end;

Also I have a question, Do I must unregister "standards" authentications before using mine? If not, TSQLRestServerAuthenticationDefault will handle the authentication instead mine (TIntegradorAuthenticationDefault)

  aServer.AuthenticationUnregister([TSQLRestServerAuthenticationDefault, TSQLRestServerAuthenticationSSPI]);
  aServer.AuthenticationRegister([TIntegradorAuthenticationDefault]);

Last edited by foncci (2014-02-01 01:24:13)

Offline

#8 2014-02-01 13:05:02

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,662
Website

Re: User authentication proposal

You have to set a group ID in User.GroupRights.
For instance:

Result.GroupRights := TSQLAuthGroup(1);

Several authentication classes can indeed coexist.
But all authentication will be tried in the order in which they were registered.
The first which authenticates will win.
smile
So it is better to let only the mandatory classes to be registered.

Offline

#9 2014-02-01 16:36:34

foncci
Member
Registered: 2013-11-15
Posts: 53

Re: User authentication proposal

ok, I'll try it now but It should be a good idea to make a "TSQLRecord-Independent" class just for Authentication. In the close future I think it will be an strong requirement. That's why I encourage you to use Interfaces because any class implementing that interface will be able to Authenticate and it won't break the actual code logic.
About the authentication registering order, it should be good to handle it like an stack, Last In-First try. If I register a new authentication class it is because I want to use it first and if I have an authentication-chain and the first doesn't work, it will try the second and so on.

Offline

#10 2014-02-02 04:00:32

foncci
Member
Registered: 2013-11-15
Posts: 53

Re: User authentication proposal

Perfect, Result.GroupRights := TSQLAuthGroup(1); works. Thanks.

Offline

#11 2014-07-12 19:34:02

avista
Member
Registered: 2014-01-06
Posts: 63

Re: User authentication proposal

I'm confused by this statement:

Result.GroupRights := TSQLAuthGroup(1);

After examining the code, I can see it being used the same way here:

TSQLAuthGroup.InitializeTable(Server: TSQLRestServer;
  const FieldName: RawUTF8);
var G: TSQLAuthGroup;
    A: TSQLAccessRights;
    U: TSQLAuthUser;
    AuthUserIndex, AuthGroupIndex: integer;
    AdminID, SupervisorID, UserID: PtrInt;
begin
[...]

   A := FULL_ACCESS_RIGHTS;
   G.Ident := 'Admin';
   G.SQLAccessRights := A;
   G.SessionTimeout := 10;
   AdminID := Server.Add(G,true); <--------------

[...]

  U.GroupRights := TSQLAuthGroup(AdminID); <--------------

Since 'AdminID' is an integer, and U.GroupRights is a class, how does this work?  Is there somewhere in the documentation where this behavior is described?  I could understand if U.GroupRights was an integer...

Thanks

Offline

#12 2014-07-12 20:22:31

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,662
Website

Re: User authentication proposal

Take a look at the SAD PDF 1.18.

Published properties of TSqlRecord type doesn't contain a true instance but the transtyped ID.

Offline

#13 2014-07-12 21:36:31

avista
Member
Registered: 2014-01-06
Posts: 63

Re: User authentication proposal

Thanks, I found a discussion about this in section '5.4.1'

Last edited by avista (2014-07-13 01:55:23)

Offline

Board footer

Powered by FluxBB