You are not logged in.
Pages: 1
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
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
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
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
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
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
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
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.
So it is better to let only the mandatory classes to be registered.
Offline
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
Perfect, Result.GroupRights := TSQLAuthGroup(1); works. Thanks.
Offline
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
Thanks, I found a discussion about this in section '5.4.1'
Last edited by avista (2014-07-13 01:55:23)
Offline
Pages: 1