#1 Re: mORMot 1 » CrossPlatform Services » 2018-04-18 18:03:45

Any way to use another connection type but with encryption?

#2 Re: mORMot 1 » CrossPlatform Services » 2018-04-18 13:25:26

Thank you for quick answer!

But can you explain also how I can rewrite my windows part of code for cross-platform?
What I should use instead TSQLHttpClientWebsockets and how to use NPM_KEY with connection

  HttpClient := TSQLHttpClientWebsockets.Create(AnsiString(Computer.Value), AnsiString(sPort), TSQLModel.Create([]));
  HttpClient.Model.Owner := HttpClient;
  HttpClient.WebSocketsUpgrade(NPM_KEY);
  if HttpClient.ServerTimeStampSynchronize then begin
    HttpClient.ServiceDefine([IMsgService], sicShared);
    HttpClient.Services.Resolve(IMsgService, MsgSvc);
  end;
  ...
  MsgSvc := nil;
  if Assigned(HttpClient) then
    FreeAndNil(HttpClient);

#3 mORMot 1 » CrossPlatform Services » 2018-04-16 10:44:56

EgorovAlex
Replies: 5

Hello,

This is possible and how to use services on Delphi FMX mobile platform?

#4 Re: mORMot 1 » Record serialization on Mac and Linux » 2018-04-11 07:18:57

This is my current realization for client or this can be faster?:

type
  TRec = record
    type
      TSQLRec = class(TPersistent)
      private
        fName: string;
        fDate: TDateTime;
        fInt : UInt16;
      published
        property Name: string    read fName write fName;
        property Date: TDateTime read fDate write fDate;
        property Int : UInt16    read fInt  write fInt;
      end;
  public
    Name: string;
    Date: TDateTime;
    Int : UInt16;
    procedure FromJSON(const AJSON: string);
    function  ToJSON: string;
  end;

{ TRec }

procedure TRec.FromJSON(const AJSON: string);
var
  lRec: TSQLRec;
begin
  lRec := TSQLRec.Create;
  try
    if JSONToObject(lRec, AJSON) then begin
      Name := lRec.Name;
      Date := lRec.Date;
      Int  := lRec.Int;
    end;
  finally
    lRec.Free;
  end;
end;

function TRec.ToJSON: string;
var
  lRec: TSQLRec;
begin
  lRec := TSQLRec.Create;
  try
    lRec.Name := Name;
    lRec.Date := Date;
    lRec.Int  := Int;
    Result := ObjectToJSON(lRec);
  finally
    lRec.Free;
  end;
end;

#5 Re: mORMot 1 » Record serialization on Mac and Linux » 2018-04-08 15:23:10

Can you provide any short and simle example to do that?
Read about Mustache usage but did not understand how to use it for this purpose

#6 Re: mORMot 1 » Record serialization on Mac and Linux » 2018-04-07 15:10:01

This was typing error. Fixed.
I need this functions for cross-platform delphi compiler

#7 mORMot 1 » Record serialization on Mac and Linux » 2018-04-07 10:22:09

EgorovAlex
Replies: 7

Hello,

Where I can find any examples or documentation for serialization on Delphi for records on Mac, Linux and Android?
This is possible?

I need analogs of RecordLoadJSON and RecordSaveJSON on Windows platform

#8 mORMot 1 » Aliases for record fields » 2015-08-19 07:38:18

EgorovAlex
Replies: 1

Hello,

XSuperObject have useful feature: Alias

I can declare

TRec = record
    [ALIAS('I')]  Id:     UInt32; 
    [ALIAS('U')]  UId:    UInt64; 
    [ALIAS('P')]  Parent: UInt32;
    [ALIAS('C')]  Child:  UInt32;
end;

And after this JSON serialization will use this aliases in text representation instead full names of the fields

Have mORMot such functionality, or I should write my own callbacks CustomReader and CustomWriter?

#9 Re: mORMot 1 » Restrict connections count » 2015-03-11 10:53:09

Will try your file, about 2-3 cases I think can be used your version of ClientSSPIAuthWithPassword

#10 Re: mORMot 1 » Restrict connections count » 2015-03-11 09:43:12

Ok, what we should to do now? Who will add such functionality? This is will be in SSPI Auth?

#11 Re: mORMot 1 » Restrict connections count » 2015-03-11 08:47:34

3. Client computer in domain, but application need to use server (computer) without domain, ClientSSPIAuthWithPassword need to use

#12 Re: mORMot 1 » Restrict connections count » 2015-03-11 05:13:18

Chaa, yes, I think SSPI Auth functionality can be extended to support authentication by password.
And my authentication class can be removed after this. Less amount of classes - more simple to use framework.

P.S. Думаю тут всё можно решить
Another russian customer smile

#13 Re: mORMot 1 » Custom REST server authentication method » 2015-03-11 04:16:26

Try this:

TSQLRestServerAuthenticationHttpToken.ClientSetUser(aClient, 'login', 'password');

#14 Re: mORMot 1 » Restrict connections count » 2015-03-10 14:21:05

Currently I don't know the password check function more useful than above (ADCheckPassword)

#15 Re: mORMot 1 » Restrict connections count » 2015-03-10 12:19:55

DigDiver, you are completely right! Unfortunately this function always return true, this is a bad news.
Good news, that I have correct function to check password, but I don't know where in mORMot is a right place
to put this function (this function used in my project for many years without any problems, but this is can't check domain password from non domain computer, I think this is not a big restriction):

function ADCheckPassword(const sUserName, sDomainName, sPassword: string): Boolean;
const
  SEC_WINNT_AUTH_IDENTITY_ANSI       = $01;
  SEC_WINNT_AUTH_IDENTITY_UNICODE    = $02;
  SEC_WINNT_AUTH_IDENTITY_MARSHALLED = $04;     // all data is in one buffer
  SEC_WINNT_AUTH_IDENTITY_ONLY       = $08;     // these credentials are for identity only - no PAC needed
  SECPKG_CRED_INBOUND          = $00000001;
  SECPKG_CRED_OUTBOUND         = $00000002;
  SECPKG_CRED_BOTH             = $00000003;
  SECPKG_CRED_DEFAULT          = $00000004;
  SECPKG_CRED_RESERVED         = $F0000000;

  SECBUFFER_VERSION            = 0;

  SECBUFFER_EMPTY              = 0;  // Undefined, replaced by provider
  SECBUFFER_DATA               = 1;  // Packet data
  SECBUFFER_TOKEN              = 2;  // Security token
  SECBUFFER_PKG_PARAMS         = 3;  // Package specific parameters
  SECBUFFER_MISSING            = 4;  // Missing Data indicator
  SECBUFFER_EXTRA              = 5;  // Extra data
  SECBUFFER_STREAM_TRAILER     = 6;  // Security Trailer
  SECBUFFER_STREAM_HEADER      = 7;  // Security Header
  SECBUFFER_NEGOTIATION_INFO   = 8;  // Hints from the negotiation pkg
  SECBUFFER_PADDING            = 9;  // non-data padding
  SECBUFFER_STREAM             = 10; // whole encrypted message

  SECBUFFER_ATTRMASK           = $F0000000;
  SECBUFFER_READONLY           = $80000000;  // Buffer is read-only
  SECBUFFER_RESERVED           = $40000000;

  SECURITY_NATIVE_DREP         = $00000010;
  SECURITY_NETWORK_DREP        = $00000000;

  SEC_I_CONTINUE_NEEDED        = $00090312;
  SEC_I_COMPLETE_NEEDED        = $00090313;
  SEC_I_COMPLETE_AND_CONTINUE  = $00090314;

type
  TSecWinntAuthIdentity = packed record
    User : PChar;
    UserLength : DWORD;
    Domain : PChar;
    DomainLength : DWORD;
    Password : PChar;
    PasswordLength : DWORD;
    Flags : DWORD
  end;
  PSecWinntAuthIdentity = ^TSecWinntAuthIdentity;

  TSecHandle = packed record
    dwLower : DWORD;
    dwUpper : DWORD
  end;
  PSecHandle = ^TSecHandle;

  TSecBuffer = packed record
    cbBuffer : DWORD;
    BufferType : DWORD;          // Type of the buffer (below)
    pvBuffer : pointer;
  end;
  PSecBuffer = ^TSecBuffer;

  TSecBufferDesc = packed record
    ulVersion,
    cBuffers : DWORD;            // Number of buffers
    pBuffers : PSecBuffer
  end;
  PSecBufferDesc = ^TSecBufferDesc;

  TCredHandle = TSecHandle;
  PCredHandle = PSecHandle;

  TCtxtHandle = TSecHandle;
  PCtxtHandle = PSecHandle;

  TAuthSeq = packed record
    _fNewConversation : BOOL;
    _hcred : TCredHandle;
    _fHaveCredHandle : BOOL;
    _fHaveCtxtHandle : BOOL;
    _hctxt : TSecHandle;
  end;
  PAuthSeq = ^TAuthSeq;

  PNode = ^TNode;
  TNode = record
    dwKey : DWORD;
    pData : pointer;
    pNext : PNode
  end;

  TSecPkgInfo = record
    fCapabilities : DWORD;        // Capability bitmask
    wVersion : WORD;              // Version of driver
    wRPCID : WORD;                // ID for RPC Runtime
    cbMaxToken : DWORD;           // Size of authentication token (max)   Name : PChar;
    Comment : PChar;              // Comment
  end;
  PSecPkgInfo = ^TSecPkgInfo;

  TSecurityStatus = LongInt;

  ENUMERATE_SECURITY_PACKAGES_FN  = function(var cPackages: DWORD; var PackageInfo: PSecPkgInfo): TSecurityStatus; stdcall;
  QUERY_SECURITY_PACKAGE_INFO_FN  = function(packageName: PChar; var info: PSecPkgInfo): TSecurityStatus; stdcall;
  QUERY_CREDENTIALS_ATTRIBUTES_FN = function(phCredential: pCredHandle; ulAttribute: DWORD; buffer: pointer): TSecurityStatus; stdcall;
  EXPORT_SECURITY_CONTEXT_FN      = function(hContext: pCtxtHandle; flags: DWORD; pPackedContext: PSecBuffer; var token : pointer): TSecurityStatus; stdcall;
  SEC_GET_KEY_FN                  = procedure(Arg, Principal: pointer; KeyVer: DWORD; var Key: pointer; var status: TSecurityStatus); stdcall;
  ACQUIRE_CREDENTIALS_HANDLE_FN   = function(pszPrincipal: PChar; pszPackage: PChar; fCredentialUse: DWORD; pvLogonID: pointer; pAuthData: pointer; pGetKeyFn: SEC_GET_KEY_FN;
                                      pvGetKeyArgument: pointer; var phCredential: TCredHandle; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  FREE_CREDENTIALS_HANDLE_FN      = function(credHandle: PCredHandle): TSecurityStatus; stdcall;
  INITIALIZE_SECURITY_CONTEXT_FN  = function(phCredential: PCredHandle; phContent: PCtxtHandle; pszTargetName: PChar; fContextReq, Reserved1, TargetDataRep: DWORD; pInput: PSecBufferDesc;
                                      Reserved2: DWORD; phNewContext: PCtxtHandle; pOutput: PSecBufferDesc; var pfContextAttr: DWORD; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  ACCEPT_SECURITY_CONTEXT_FN      = function (phCredential: PCredHandle; phContext: PCtxtHandle; pInput: PSecBufferDesc; fContextReq, TargetDataRep: DWORD;
                                      phNewContext: PCtxtHandle; pOutput: PSecBufferDesc; var pfContextAttr: DWORD; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  COMPLETE_AUTH_TOKEN_FN          = function (phContext: PCtxtHandle; pToken: PSecBufferDesc): TSecurityStatus; stdcall;
  DELETE_SECURITY_CONTEXT_FN      = function (phContext: PCtxtHandle): TSecurityStatus; stdcall;
  APPLY_CONTROL_TOKEN_FN          = function (phContext: PCtxtHandle; pInput: PSecBufferDesc): TSecurityStatus; stdcall;
  QUERY_CONTEXT_ATTRIBUTES_FN     = function (phContext: PCtxtHandle; alAttribute: DWORD; pBuffer: pointer): TSecurityStatus; stdcall;
  IMPERSONATE_SECURITY_CONTEXT_FN = function (phContext: PCtxtHandle): TSecurityStatus; stdcall;
  REVERT_SECURITY_CONTEXT_FN      = function (phContext: PCtxtHandle): TSecurityStatus; stdcall;
  MAKE_SIGNATURE_FN               = function (phContext: PCtxtHandle; fQOP: DWORD; pMessage: PSecBufferDesc; MessageSeqNo: DWORD): TSecurityStatus; stdcall;
  VERIFY_SIGNATURE_FN             = function (phContext: PCtxtHandle; pMessage: PSecBufferDesc; MessageSeqNo: DWORD; var fQOP: DWORD): TSecurityStatus; stdcall;
  FREE_CONTEXT_BUFFER_FN          = function (contextBuffer: pointer): TSecurityStatus; stdcall;
  IMPORT_SECURITY_CONTEXT_FN      = function (pszPackage: PChar; pPackedContext: PSecBuffer; Token: pointer; phContext: PCtxtHandle): TSecurityStatus; stdcall;
  ADD_CREDENTIALS_FN              = function (hCredentials: PCredHandle; pszPrincipal, pszPackage: PChar; fCredentialUse: DWORD; pAuthData: pointer;
                                      pGetKeyFn: SEC_GET_KEY_FN; pvGetKeyArgument: pointer; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  QUERY_SECURITY_CONTEXT_TOKEN_FN = function (phContext: PCtxtHandle; var token: pointer): TSecurityStatus; stdcall;
  ENCRYPT_MESSAGE_FN              = function (phContext: PCtxtHandle; fQOP: DWORD; pMessage: PSecBufferDesc; MessageSeqNo: DWORD): TSecurityStatus; stdcall;
  DECRYPT_MESSAGE_FN              = function (phContext: PCtxtHandle; pMessage: PSecBufferDesc; MessageSeqNo: DWORD; fQOP: DWORD): TSecurityStatus; stdcall;

  TSecurityFunctionTable = record
    dwVersion : LongInt;
    EnumerateSecurityPackages  : ENUMERATE_SECURITY_PACKAGES_FN;
    QueryCredentialsAttributes : QUERY_CREDENTIALS_ATTRIBUTES_FN;
    AcquireCredentialsHandle   : ACQUIRE_CREDENTIALS_HANDLE_FN;
    FreeCredentialHandle       : FREE_CREDENTIALS_HANDLE_FN;
    Reserved2                  : FARPROC;
    InitializeSecurityContext  : INITIALIZE_SECURITY_CONTEXT_FN;
    AcceptSecurityContext      : ACCEPT_SECURITY_CONTEXT_FN;
    CompleteAuthToken          : COMPLETE_AUTH_TOKEN_FN;
    DeleteSecurityContext      : DELETE_SECURITY_CONTEXT_FN;
    ApplyControlToken          : APPLY_CONTROL_TOKEN_FN;
    QueryContextAttributes     : QUERY_CONTEXT_ATTRIBUTES_FN;
    ImpersonateSecurityContext : IMPERSONATE_SECURITY_CONTEXT_FN;
    RevertSecurityContext      : REVERT_SECURITY_CONTEXT_FN;
    MakeSignature              : MAKE_SIGNATURE_FN;
    VerifySignature            : VERIFY_SIGNATURE_FN;
    FreeContextBuffer          : FREE_CONTEXT_BUFFER_FN;
    QuerySecurityPackageInfo   : QUERY_SECURITY_PACKAGE_INFO_FN;
    Reserved3                  : FARPROC;
    Reserved4                  : FARPROC;
    ExportSecurityContext      : EXPORT_SECURITY_CONTEXT_FN;
    ImportSecurityContext      : IMPORT_SECURITY_CONTEXT_FN;
    AddCredentials             : ADD_CREDENTIALS_FN;
    Reserved8                  : FARPROC;
    QuerySecurityContextToken  : QUERY_SECURITY_CONTEXT_TOKEN_FN;
    EncryptMessage             : ENCRYPT_MESSAGE_FN;
    DecryptMessage             : DECRYPT_MESSAGE_FN;
  end;
  PSecurityFunctionTable = ^TSecurityFunctionTable;

  var
    Head: TNode;

  function GetEntry(dwKey: DWORD; var pData: pointer): boolean;
  var
    pCurrent: PNode;
  begin
    result := False;
    pCurrent := Head.pNext;
    while Assigned (pCurrent) do
    begin
      if pCurrent^.dwKey = dwKey then
      begin
        pData := pCurrent^.pData;
        result := True;
        break
      end;
      pCurrent := pCurrent^.pNext
    end
  end;

  function AddEntry(dwKey: DWORD; pData: pointer): boolean;
  var
    pTemp: PNode;
  begin
    GetMem(pTemp, sizeof(TNode));
    if Assigned(pTemp) then
    begin
      pTemp^.dwKey := dwKey;
      pTemp^.pData := pData;
      pTemp^.pNext := Head.pNext;
      Head.pNext := pTemp;
      result := True
    end
    else
      result := False
  end;

  function DeleteEntry(dwKey: DWORD; var ppData: pointer): boolean;
  var
    pCurrent, pTemp: PNode;
  begin
    result := False;
    pTemp := @head;
    pCurrent := Head.pNext;
    while pCurrent <> Nil do begin
      if dwKey = pCurrent^.dwKey then begin
        pTemp^.pNext := pCurrent^.pNext;
        ppData := pCurrent^.pData;
        FreeMem (pCurrent);
        result := True;
        break
      end
      else begin
        pTemp := pCurrent;
        pCurrent := pCurrent^.pNext
      end
    end
  end;

  function InitSession(dwKey: DWORD): boolean;
  var
    pAS: PAuthSeq;
  begin
    result := False;
    GetMem(pAS, sizeof(TAuthSeq));
    if Assigned(pAS) then
    try
      pAS^._fNewConversation := TRUE;
      pAS^._fHaveCredHandle := FALSE;
      pAS^._fHaveCtxtHandle := FALSE;
      if not AddEntry (dwKey, pAS) then
        FreeMem (pAS)
      else
        result := True
    except
      FreeMem (pAS);
      raise
    end
  end;

  function InitPackage(var cbMaxMessage: DWORD; var funcs:PSecurityFunctionTable): THandle;
  type
    INIT_SECURITY_ENTRYPOINT_FN = function: PSecurityFunctionTable;
  var
    pInit: INIT_SECURITY_ENTRYPOINT_FN;
    ss: TSecurityStatus;
    pkgInfo: PSecPkgInfo;
  begin
    result := LoadLibrary('security.dll');  // *** Secur32.dll for Windows 95
    if result <> 0 then
    try
      pInit := GetProcAddress(result, 'InitSecurityInterfaceW');
      if not Assigned(pInit) then
        raise Exception.CreateFmt('Couldn''t get sec init routine: %d', [GetLastError]);
      funcs := pInit;
      if not Assigned(funcs) then
        raise Exception.Create('Couldn''t init package');
      ss := funcs^.QuerySecurityPackageInfo('NTLM', pkgInfo);
      if ss < 0 then
        raise Exception.CreateFmt('Couldn''t query package info for NTLM, error %d\n', [ss]);
      cbMaxMessage := pkgInfo^.cbMaxToken;
      funcs^.FreeContextBuffer(pkgInfo)
    except
      if result <> 0 then
        FreeLibrary(result);
      raise
    end
  end;

  function GenClientContext(funcs: PSecurityFunctionTable; dwKey: DWORD; Auth: PSecWINNTAuthIdentity;
    pIn: PBYTE; cbIn: DWORD; pOut: PBYTE; var cbOut: DWORD; var fDone: boolean): boolean;
  var
    ss: TSecurityStatus;
    lifeTime: TTimeStamp;
    OutBuffDesc: TSecBufferDesc;
    OutSecBuff: TSecBuffer;
    InBuffDesc: TSecBufferDesc;
    InSecBuff: TSecBuffer;
    ContextAttributes: DWORD;
    pAS: PAuthSeq;
    phctxt: PCtxtHandle;
    pBuffDesc: PSecBufferDesc;
  begin
    result := False;
    if GetEntry(dwKey, pointer (pAS)) then
    try
      if pAS^._fNewConversation then begin
        ss := funcs^.AcquireCredentialsHandle(nil{principal}, 'NTLM', SECPKG_CRED_OUTBOUND, nil{LOGON id},
                Auth{auth data}, nil{get key fn}, nil{get key arg}, pAS^._hcred, Lifetime);
        if ss < 0 then
          raise Exception.CreateFmt ('AquireCredentials failed %d', [ss]);
        pAS^._fHaveCredHandle := TRUE
      end;

      OutBuffDesc.ulVersion := 0;
      OutBuffDesc.cBuffers := 1;
      OutBuffDesc.pBuffers := @OutSecBuff;

      OutSecBuff.cbBuffer := cbOut;
      OutSecBuff.BufferType := SECBUFFER_TOKEN;
      OutSecBuff.pvBuffer := pOut;

      // prepare input buffer
      if not pAS^._fNewConversation then begin
        InBuffDesc.ulVersion := 0;
        InBuffDesc.cBuffers := 1;
        InBuffDesc.pBuffers := @InSecBuff;

        InSecBuff.cbBuffer := cbIn;
        InSecBuff.BufferType := SECBUFFER_TOKEN;
        InSecBuff.pvBuffer := pIn
      end;

      if pAS^._fNewConversation then begin
        pBuffDesc := nil;
        phctxt := nil
      end
      else begin
        phctxt := @pAS^._hctxt;
        pBuffDesc := @InBuffDesc
      end;

      ss := funcs^.InitializeSecurityContext(@pAS^._hcred, phctxt, 'AuthSamp', 0{context requirements},
              0{reserved1}, SECURITY_NATIVE_DREP, pBuffDesc, 0{reserved2}, @pAS^._hctxt, @OutBuffDesc,
              ContextAttributes, Lifetime);
      if ss < 0 then
        raise Exception.CreateFmt ('Init context failed: %d', [ss]);

      pAS^._fHaveCtxtHandle := TRUE;

      if (ss = SEC_I_COMPLETE_NEEDED) or (ss = SEC_I_COMPLETE_AND_CONTINUE) then begin
        if Assigned (funcs^.CompleteAuthToken) then begin
          ss := funcs^.CompleteAuthToken (@pAS^._hctxt, @OutBuffDesc);
          if ss < 0 then
            raise Exception.CreateFmt ('Complete failed: %d', [ss])
        end;
      end;

      cbOut := OutSecBuff.cbBuffer;

      if pAS^._fNewConversation then
        pAS^._fNewConversation := FALSE;

      fDone := (ss <> SEC_I_CONTINUE_NEEDED) and (ss <> SEC_I_COMPLETE_AND_CONTINUE);

      result := True
    except
    end
  end;

  function GenServerContext(funcs: PSecurityFunctionTable; dwKey: DWORD; pIn: PByte; cbIn: DWORD; pOut: PByte;
    var cbOut: DWORD; var fDone: boolean) : boolean;
  var
    ss: TSecurityStatus;
    Lifetime:  TTimeStamp;
    OutBuffDesc, InBuffDesc: TSecBufferDesc;
    InSecBuff, OutSecBuff: TSecBuffer;
    ContextAttributes: DWORD;
    pAS: PAuthSeq;
    phctxt: PCtxtHandle;
  begin
    result := False;
    if GetEntry(dwKey, pointer (pAS)) then
    try
      if pAS^._fNewConversation then begin
        ss := funcs^.AcquireCredentialsHandle(nil{principal}, 'NTLM', SECPKG_CRED_INBOUND, nil{LOGON id},
                nil{auth data}, nil{get key fn}, nil{get key arg}, pAS^._hcred, Lifetime);
        if ss < 0 then
          raise Exception.CreateFmt ('AcquireCreds failed %d', [ss]);
        pAS^._fHaveCredHandle := TRUE
      end;

      // prepare output buffer
      OutBuffDesc.ulVersion := 0;
      OutBuffDesc.cBuffers := 1;
      OutBuffDesc.pBuffers := @OutSecBuff;

      OutSecBuff.cbBuffer := cbOut;
      OutSecBuff.BufferType := SECBUFFER_TOKEN;
      OutSecBuff.pvBuffer := pOut;

      // prepare input buffer
      InBuffDesc.ulVersion := 0;
      InBuffDesc.cBuffers := 1;
      InBuffDesc.pBuffers := @InSecBuff;

      InSecBuff.cbBuffer := cbIn;
      InSecBuff.BufferType := SECBUFFER_TOKEN;
      InSecBuff.pvBuffer := pIn;

      if pAS^._fNewConversation then
        phctxt := Nil
      else
        phctxt := @pAS^._hctxt;

      ss := funcs^.AcceptSecurityContext(@pAS^._hcred, phctxt, @InBuffDesc, 0{context requirements},
              SECURITY_NATIVE_DREP, @pAS^._hctxt, @OutBuffDesc, ContextAttributes, Lifetime);
      if ss < 0 then begin
        Result := false;
        Exit;
      end
      else
        pAS^._fHaveCtxtHandle := TRUE;

      // Complete token -- if applicable
      if (ss = SEC_I_COMPLETE_NEEDED) or (ss =SEC_I_COMPLETE_AND_CONTINUE) then
        if Assigned (funcs^.CompleteAuthToken) then begin
          ss := funcs^.CompleteAuthToken (@pAS^._hctxt, @OutBuffDesc);
          if ss < 0 then
            raise Exception.CreateFmt ('complete failed: %d', [ss]);
        end
        else
          raise Exception.Create ('Complete not supported.');

      cbOut := OutSecBuff.cbBuffer;

      if pAS^._fNewConversation then
        pAS^._fNewConversation := FALSE;

      fDone := (ss <> SEC_I_CONTINUE_NEEDED) and (ss <> SEC_I_COMPLETE_AND_CONTINUE);

      result := True
    except
    end;
  end;

  function TermSession(funcs: PSecurityFunctionTable; dwKey: DWORD): boolean;
  var
    pAS: PAuthSeq;
  begin
    result := False;
    if DeleteEntry(dwKey, pointer (pAS)) then begin
      if pAS^._fHaveCtxtHandle then
        funcs^.DeleteSecurityContext (@pAS^._hctxt);
      if pAS^._fHaveCredHandle then
        funcs^.FreeCredentialHandle (@pAS^._hcred);
      freemem(pAS);
      result := True
    end
  end;

var
  done : boolean;
  cbOut, cbIn : DWORD;
  AuthIdentity : TSecWINNTAuthIdentity;
  session0OK, session1OK : boolean;
  packageHandle : THandle;

  pClientBuf : PByte;
  pServerBuf : PByte;
  cbMaxMessage : DWORD;
  funcs : PSecurityFunctionTable;
begin
  Result := False;
  try
    done := False;

    session1OK := False;
    packageHandle := 0;
    pClientBuf := Nil;
    pServerBuf := Nil;
    cbMaxMessage := 0;

    ZeroMemory(@Head, SizeOf(TNode));

    session0OK := InitSession(0);
    try
      session1OK := InitSession(1);
      packageHandle := InitPackage (cbMaxMessage, funcs);

      if session0OK and session1OK and (packageHandle <> 0) then begin
        GetMem(pClientBuf, cbMaxMessage);
        GetMem(pServerBuf, cbMaxMessage);
        FillChar(AuthIdentity, SizeOf(AuthIdentity), 0);

        if sUserName <> '' then begin
          AuthIdentity.User := PChar(sUserName);
          AuthIdentity.UserLength := Length(sUserName);
        end;

        if sDomainName <> '' then begin
          AuthIdentity.Domain := PChar(sDomainName);
          AuthIdentity.DomainLength := Length(sDomainName)
        end;

        if sPassword <> '' then begin
          AuthIdentity.Password := PChar(sPassword);
          AuthIdentity.PasswordLength := Length(sPassword)
        end
        else begin
          AuthIdentity.Password := PChar ('');
          AuthIdentity.PasswordLength := 0;
        end;
        // original C code contains check for UNICODE strings.
        AuthIdentity.Flags := SEC_WINNT_AUTH_IDENTITY_UNICODE;

        // Prepare client message (negotiate).
        cbOut := cbMaxMessage;

        if not GenClientContext(funcs, 0, @AuthIdentity, pServerBuf, 0, pClientBuf, cbOut, done) then
          raise Exception.Create('GenClientContext Failed');

        cbIn := cbOut;
        cbOut := cbMaxMessage;
        if not GenServerContext(funcs, 1, pClientBuf, cbIn, pServerBuf, cbOut, done) then
          raise Exception.Create('GenServerContext Failed');
        cbIn := cbOut;

        // Prepare client message (authenticate).
        cbOut := cbMaxMessage;
        if not GenClientContext(funcs, 0, @AuthIdentity, pServerBuf, cbIn, pClientBuf, cbOut, done) then
          raise Exception.Create('GenClientContext failed');
        cbIn := cbOut;

        // Prepare server message (authentication).
        cbOut := cbMaxMessage;
        Result := GenServerContext(funcs, 1, pClientBuf, cbIn, pServerBuf, cbOut, done)
      end;
    except
    end;
    // close session
    if Session0OK then
      TermSession(funcs, 0);
    if Session1OK then
      TermSession(funcs, 1);
    if packageHandle <> 0 then
      FreeLibrary (PackageHandle);
    if pClientBuf <> nil then
      FreeMem (pClientBuf);
    if pServerBuf <> nil then
      FreeMem (pServerBuf);
  except
  end;
end;

#16 Re: mORMot 1 » Restrict connections count » 2015-03-10 10:29:30

Which Windows version do you use for test?

9 this is LOGON32_LOGON_NEW_CREDENTIALS works on all my test computers

And this is works even to check domain password from non domain computer

#17 Re: mORMot 1 » Restrict connections count » 2015-03-10 06:38:08

Files for example 14 with Active Directory authentication below
(this is impossible to attach files?)

Project14ClientMain.pas
Added authorization line:     
3:   TSQLRestServerAuthenticationActiveDirectory.ClientSetUser(Client,'Computer\User','Password');

unit Project14ClientMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, mORMot, mORMotHttpClient,
  Project14Interface;

type
  TForm1 = class(TForm)
    edtA: TEdit;
    edtB: TEdit;
    lblA: TLabel;
    lblB: TLabel;
    btnCall: TButton;
    btnCancel: TButton;
    lblResult: TLabel;
    ComboProtocol: TComboBox;
    procedure btnCancelClick(Sender: TObject);
    procedure btnCallClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ComboProtocolChange(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Model: TSQLModel;
    Client: TSQLRestClientURI;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{$R Vista.res}

procedure TForm1.btnCancelClick(Sender: TObject);
begin
  Close;
end;

procedure TForm1.btnCallClick(Sender: TObject);
var a,b: integer;
    err: integer;
    I: ICalculator;
begin
  val(edtA.Text,a,err);
  if err<>0 then begin
    edtA.SetFocus;
    exit;
  end;
  val(edtB.Text,b,err);
  if err<>0 then begin
    edtB.SetFocus;
    exit;
  end;
  if Client=nil then begin
    if Model=nil then
      Model := TSQLModel.Create([],ROOT_NAME);
    case ComboProtocol.ItemIndex of
    0,2,3: Client := TSQLHttpClient.Create('localhost','888',Model);
    1: Client := TSQLRestClientURINamedPipe.Create(Model,APPLICATION_NAME);
    else exit;
    end;
    if not Client.ServerTimeStampSynchronize then begin
      ShowMessage(UTF8ToString(Client.LastErrorMessage));
      exit;
    end;
    case ComboProtocol.ItemIndex of
    2:   TSQLRestServerAuthenticationNone.ClientSetUser(Client,'User','');
	     // Here possible to use 'ComputerName\User' or 'DomainName\User'
    3:   TSQLRestServerAuthenticationActiveDirectory.ClientSetUser(Client,'Computer\User','Password');
    else Client.SetUser('User','synopse');
    end;
    Client.ServiceDefine([ICalculator],sicShared);
  end;
  if Client.Services['Calculator'].Get(I) then
    lblResult.Caption := IntToStr(I.Add(a,b));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Client.Free;
  Model.Free;
end;

procedure TForm1.ComboProtocolChange(Sender: TObject);
begin
  FreeAndNil(Client);
end;

end.

Project14ClientMain.dfm
Added 'Active Directory HTTP / TCP-IP' dropdown item to TComboBox

object Form1: TForm1
  Left = 334
  Top = 330
  Caption = 'Form1'
  ClientHeight = 242
  ClientWidth = 306
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 16
  object lblA: TLabel
    Left = 56
    Top = 50
    Width = 17
    Height = 16
    Caption = 'A='
  end
  object lblB: TLabel
    Left = 56
    Top = 98
    Width = 16
    Height = 16
    Caption = 'B='
  end
  object lblResult: TLabel
    Left = 76
    Top = 200
    Width = 184
    Height = 16
    Caption = 'Enter numbers, then Call Server'
  end
  object edtA: TEdit
    Left = 80
    Top = 48
    Width = 153
    Height = 24
    TabOrder = 0
  end
  object edtB: TEdit
    Left = 80
    Top = 96
    Width = 153
    Height = 24
    TabOrder = 1
  end
  object btnCall: TButton
    Left = 56
    Top = 152
    Width = 97
    Height = 25
    Caption = 'Call Server'
    TabOrder = 2
    OnClick = btnCallClick
  end
  object btnCancel: TButton
    Left = 168
    Top = 152
    Width = 97
    Height = 25
    Caption = 'Quit'
    TabOrder = 3
    OnClick = btnCancelClick
  end
  object ComboProtocol: TComboBox
    Left = 80
    Top = 16
    Width = 153
    Height = 24
    Style = csDropDownList
    TabOrder = 4
    OnChange = ComboProtocolChange
    Items.Strings = (
      'HTTP / TCP-IP'
      'Named Pipe'
      'Weak HTTP / TCP-IP'
      'Active Directory HTTP / TCP-IP')
  end
end

Project14ServerHttpActiveDirectory.dpr

/// this server will use Active Directory authentication with TSQLRestServerFullMemory over HTTP
// demonstrated how to change default passwords for default users
program Project14ServerHttpWeak;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
  SynLog,
  mORMot,
  mORMotSQLite3,
  mORMotHttpServer,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;

function TServiceCalculator.Add(n1, n2: integer): integer;
begin
  result := n1+n2;
end;

procedure RegisterAuthenticationActiveDirectory(Server: TSQLRestServer; EnableDefaultUser: Boolean);

  procedure ChangeDefaultPassword(LogonName, Password: RawUTF8);
  var
    U: TSQLAuthUser;
  begin
    U := Server.SQLAuthUserClass.Create(Server, 'LogonName=?', [LogonName]);
    try
      if U.ID > 0 then begin
        U.PasswordPlain := Password;
        Server.Update(U);
      end;
    finally
      U.Free;
    end;
  end;

var
  Auth: TSQLRestServerAuthentication;
  G: TSQLAuthGroup;
  U: TSQLAuthUser;
  UserID: PtrInt;
begin
  Auth := Server.AuthenticationRegister(TSQLRestServerAuthenticationActiveDirectory);
  Server.CreateMissingTables;
  // Change default passwords
  ChangeDefaultPassword('Admin', 'AdminPass');
  ChangeDefaultPassword('Supervisor', 'SupervisorPass');
  ChangeDefaultPassword('User', 'UserPass');
  if EnableDefaultUser then
    // Enable work with user '*'
    Auth.Options := Auth.Options + [saoHandleUnknownLogonAsStar];
    // Get ID of group with User access rights
    G := Server.SQLAuthGroupClass.Create(Server, 'Ident=?', ['User']);
    try
      UserId := G.ID;
    finally
      G.Free;
    end;
    if UserId > 0 then begin
      // Create default Active Directory user '*' - settings for all authenticated users
      U := Server.SQLAuthUserClass.Create(Server, 'LogonName=?', ['*']);
      try
        if U.ID = 0 then begin
          U.LogonName        := '*';
          U.DisplayName      := U.LogonName;
          U.PasswordHashHexa := '';
          U.GroupRights      := TSQLAuthGroup(UserID);
          Server.Add(U,true);
        end;
      finally
        U.Free;
      end;
    end;
end;

var
  aModel: TSQLModel;
  aServer: TSQLRestServer;
  aHTTPServer: TSQLHttpServer;
begin
  // define the log level
  with TSQLLog.Family do begin
    Level := LOG_VERBOSE;
    EchoToConsole := LOG_VERBOSE; // log all events to the console
  end;
  // create a Data Model (including TSQLAuthGroup,TSQLAuthUser here)
  aModel := TSQLModel.Create([TSQLAuthGroup,TSQLAuthUser],ROOT_NAME);
  try
    // initialize a TObjectList-based database engine
    aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,false);
    try
	  // register TSQLRestServerAuthenticationActiveDirectory
	  // enable default user '*' for authenticated users
      RegisterAuthenticationActiveDirectory(aServer,true);
      // register our ICalculator service on the server side
      aServer.ServiceDefine(TServiceCalculator,[ICalculator],sicShared);
      // launch the HTTP server
      aHTTPServer := TSQLHttpServer.Create(PORT_NAME,[aServer],'+',useHttpApiRegisteringURI);
      try
        aHTTPServer.AccessControlAllowOrigin := '*'; // for AJAX requests to work
        writeln(#10'Background server is running.'#10);
        writeln('Press [Enter] to close the server.'#10);
        readln;
      finally
        aHTTPServer.Free;
      end;
    finally
      aServer.Free;
    end;
  finally
    aModel.Free;
  end;
end.

#18 Re: mORMot 1 » History settings » 2015-03-09 21:11:59

Hi again, this idea can be implemented?
Also I need functionality to store in history the user name, which perform corresponding operation
where the right place to put this code?

Edit: or similar question - how to determine session (and user name) from OnUpdateEvent

#19 Re: mORMot 1 » Restrict connections count » 2015-03-09 20:37:24

Also I can prepare sample server code which can be included in your 14 sample with Active Directory (and local computer) authentication, I think this can be useful for many customers, do you need this?

#20 Re: mORMot 1 » Restrict connections count » 2015-03-09 20:35:32

Hello again,

please change your code in mORMot.pas to this improved functions, current implementation is unusable:

function TSQLRestServerAuthenticationActiveDirectory.Auth(Ctxt: TSQLRestServerURIContext): boolean;
function CheckPassword(const UserName,Password: RawUTF8): Boolean;
var Domain,Login: RawUTF8;
    hToken: THandle;
begin
  split(UserName,'\',Domain,Login);
  result := LogonUser(pointer(UTF8ToString(Login)),pointer(UTF8ToString(Domain)),
    pointer(UTF8ToString(Password)),9,LOGON32_PROVIDER_WINNT50,hToken);
  if result then
    CloseHandle(hToken);
end;
var aUserName, aHashedPassWord, aPassword, aSalt: RawUTF8;
    User: TSQLAuthUser;
begin
  result := true;
  if AuthSessionRelease(Ctxt) then
    exit;
  aUserName := Ctxt.InputUTF8OrVoid['UserName'];
  aHashedPassWord := Base64ToBin(Ctxt.InputUTF8OrVoid['Password']);
  if (aUserName<>'') and (aHashedPassWord<>'') then begin
    User := GetUser(Ctxt,aUserName);
    if User<>nil then
    try
      User.PasswordHashHexa := ''; // not needed, especially if from LogonName='*'
      aPassword := TAESCFB.SimpleEncrypt(aHashedPassWord,Nonce(false)+AD_SALT,false);
      split(aPassword,'\',aSalt,aPassword);
      if aSalt<>AD_SALT then begin
        aPassword := TAESCFB.SimpleEncrypt(aHashedPassWord,Nonce(true)+AD_SALT,false);
        split(aPassword,'\',aSalt,aPassword);
        if aSalt<>AD_SALT then
          exit; // current and previous server nonce did not success
      end;
      if not CheckPassword(aUserName,aPassWord) then
        exit; // Active Directory password incorrect
      // now client is authenticated -> create a session
      SessionCreate(Ctxt,User);
    finally
      User.Free;
    end;
  end else
    if aUserName<>'' 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;

class function TSQLRestServerAuthenticationActiveDirectory.ClientComputeSessionKey(Sender: TSQLRestClientURI;
  User: TSQLAuthUser): RawUTF8;
var aServerNonce: RawUTF8;
begin
  result := '';
  if User.LogonName='' then
    exit;
  aServerNonce := Sender.CallBackGetResult('Auth',['UserName',User.LogonName]);
  if aServerNonce='' then
    exit;
  result := Sender.CallBackGetResult('Auth',['UserName',User.LogonName,
    'Password',BinToBase64(TAESCFB.SimpleEncrypt(
    AD_SALT+'\'+User.PasswordHashHexa,aServerNonce+AD_SALT,true))]);
end;

#21 Re: mORMot 1 » Restrict connections count » 2015-03-06 17:04:25

Your variant has more convenient format, but this is a bad idea, I think, to invoke internal function CheckPassword twice, on some system this can have some problems with locking of account. This is reason that was used different params to send nonce and password separately

Edit: In my variant the Password parameter was used to compare Nonce,
Data parameter (which was encrypted, but too easy) used to send password

Edit2:
40906 line:
if aPassworKind<>passClear then
    raise ESecurityException.CreateUTF8('%.ClientSetUser() expects passClear',[self]);
  result := inherited ClientSetUser(Sender,aUserName,aPassword,passHashed);

passHashed should be changed to passClear, i think?
otherwise we will have unreadable hash of password which impossible to check

#22 Re: mORMot 1 » Restrict connections count » 2015-03-06 11:01:49

User authentication by Active Directory Login\Password

Usage on the server side:

  aModel := TSQLModel.Create([TSQLAuthGroupAD,TSQLAuthUser],ROOT_NAME);
  aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,false);
  aServer.AuthenticationRegister(TSQLRestServerAuthenticationAD);

On the client side:

  TSQLRestServerAuthenticationAD.ClientSetUser(Client,'Domain\Login','Password'); 

Actually this is possible to use without active dirctory using format 'ComputerName\Login'
Here ComputerName should be name of computer where server is started
 
 
TSQLAuthGroupAD contain one default user '*'
settings from this user will be applied to any authenticated user which not contain record with restrictions
in other words all authenticated users will have 'User' access

Your comments will be highly appreciated

type
  TSQLRestServerAuthenticationAD = class(TSQLRestServerAuthenticationSignedURI)
  protected
    function CheckPassword(Ctxt: TSQLRestServerURIContext;
      const aClientNonce, aUserName, aPassWord, aData: RawUTF8): boolean; virtual;
    class function ClientComputeSessionKey(Sender: TSQLRestClientURI; User: TSQLAuthUser): RawUTF8; override;
  public
    function Auth(Ctxt: TSQLRestServerURIContext): boolean; override;
    class function ClientSetUser(Sender: TSQLRestClientURI; const aUserName, aPassword: RawUTF8;
      aPassworKind: TSQLRestServerAuthenticationClientSetUserPassword=passClear): boolean; override;
  end;
  
  TSQLAuthGroupAD = class(TSQLAuthGroup)
    class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8;
      Options: TSQLInitializeTableOptions); override;
  end;


{ TSQLRestServerAuthenticationAD }

function TSQLRestServerAuthenticationAD.Auth(Ctxt: TSQLRestServerURIContext): boolean;
var aUserName, aPassWord, aClientNonce, aData: RawUTF8;
    sData, sLen: string;
    User: TSQLAuthUser;
begin
  result := true;
  if AuthSessionRelease(Ctxt) then
    exit;
  aUserName := Ctxt.InputUTF8OrVoid['UserName'];
  aClientNonce := Ctxt.InputUTF8OrVoid['ClientNonce'];
  if (aUserName<>'') and (aClientNonce<>'') then begin
    User := GetUser(Ctxt,aUserName);
    if User=nil then begin
      User := GetUser(Ctxt,'*');
      if User<>nil then begin
        User.LogonName := aUserName;
        User.DisplayName := aUserName;
      end;
    end;
    if User<>nil then
    try
      User.PasswordHashHexa := '';
      aPassWord := Ctxt.InputUTF8OrVoid['Password'];
      aData := Ctxt.InputUTF8OrVoid['Data'];
      aData := AESSHA256(aData, TSQLAUTHUSER_SALT+aUserName,false);
      sData := UTF8ToString(aData);
      sLen := sData.Substring(3, 3);
      aData := StringToUTF8(sData.Substring(6, StrToInt(sLen)));
      if not CheckPassword(Ctxt,aClientNonce,aUserName,aPassWord,aData) then
        exit;
      // now client is authenticated -> create a session
      SessionCreate(Ctxt,User);
    finally
      User.Free;
    end;
  end else
    if aUserName<>'' 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;

function ADCheckPassword(const sUserName, sDomainName, sPassword: PChar): Boolean;
var
  hToken: THandle;
begin
  Result := LogonUser(sUserName, sDomainName, sPassword,
              9{LOGON_TYPE_NEW_CREDENTIALS}, LOGON32_PROVIDER_WINNT50, hToken);
  if Result then
    CloseHandle(hToken);
end;

function TSQLRestServerAuthenticationAD.CheckPassword(Ctxt: TSQLRestServerURIContext;
  const aClientNonce, aUserName, aPassWord, aData: RawUTF8): boolean;
var aSalt: RawUTF8;
    sUserName, sDomainName: string;
    N: Integer;
begin
  aSalt := TSQLAUTHUSER_SALT+aClientNonce+aUserName;
  result := (aPassWord=SHA256(fServer.Model.Root+Nonce(false)+aSalt)) or
            // if current nonce failed, tries with previous 5 minutes nonce
            (aPassWord=SHA256(fServer.Model.Root+Nonce(true)+aSalt));
  if result then begin
    sDomainName := string(aUserName);
    N := sDomainName.IndexOf('\');
    if N > -1 then begin
      sUserName := sDomainName.Substring(0, N);
      sDomainName := sDomainName.Substring(N + 1, sDomainName.Length);
      result := ADCheckPassword(PChar(sUserName), PChar(sDomainName), PChar(UTF8ToString(aPassWord)));
    end
    else
      result := false;
  end;
end;

class function TSQLRestServerAuthenticationAD.ClientComputeSessionKey(Sender: TSQLRestClientURI;
  User: TSQLAuthUser): RawUTF8;
var aServerNonce, aClientNonce: RawUTF8;
begin
  result := '';
  if User.LogonName='' then
    exit;
  aServerNonce := Sender.CallBackGetResult('Auth',['UserName',User.LogonName]);
  if aServerNonce='' then
    exit;
  aClientNonce := Nonce(false);
  result := Sender.CallBackGetResult('Auth',['UserName',User.LogonName,'Password',
     Sha256(Sender.Model.Root+aServerNonce+TSQLAUTHUSER_SALT+aClientNonce+User.LogonName),
     'ClientNonce',aClientNonce,'Data',User.Data]);
end;

class function TSQLRestServerAuthenticationAD.ClientSetUser(Sender: TSQLRestClientURI; const aUserName,
  aPassword: RawUTF8; aPassworKind: TSQLRestServerAuthenticationClientSetUserPassword=passClear): boolean;
var U: TSQLAuthUser;
begin
  result := false;
  if Sender=nil then
    exit;
  try
    Sender.SessionClose;
    U := TSQLAuthUser.Create;
    try
      U.LogonName := trim(aUserName);
      U.DisplayName := U.LogonName;
      U.PasswordHashHexa := '';
      // Encrypt password AesSha256(LengthOfPassword(00N)+aPassword+Sha256(Random(99999999)))
      U.Data := AESSHA256(StringToUTF8(Format('%.3d%.3d', [Random(999), Length(aPassword)]))+
                          aPassword+Sha256(StringToUTF8(Format('%.8d', [Random(99999999)]))),
                  TSQLAUTHUSER_SALT+U.LogonName, True);
      result := Sender.SessionCreate(self,U,ClientComputeSessionKey(Sender,U));
    finally
      U.Free;
    end;
  finally
    if Assigned(Sender.OnSetUser) then
      Sender.OnSetUser(Sender); // always notify of user change, even if failed
  end;
end;


{ TSQLAuthGroupAD }

class procedure TSQLAuthGroupAD.InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8;
  Options: TSQLInitializeTableOptions);
var G: TSQLAuthGroup;
    U: TSQLAuthUser;
    UserID: PtrInt;
begin
  inherited InitializeTable(Server, FieldName, Options);
  if (Server<>nil) and (FieldName='') then
    if Server.HandleAuthentication then begin
      // create default AD user '*' - settings for all users
      G := TSQLAuthGroupAD.Create(Server,'Ident=?',['User']);
      try
        UserId := G.ID;
      finally
        G.Free;
      end;
      if UserId>0 then begin
        U := Server.fSQLAuthUserClass.Create;
        try
          U.LogonName := '*';
          U.PasswordHashHexa := '';
          U.DisplayName := U.LogonName;
          U.GroupRights := TSQLAuthGroup(UserID);
          Server.Add(U,true);
        finally
          U.Free;
        end;
      end;
    end;
end;

#23 Re: mORMot 1 » Restrict connections count » 2015-03-02 10:14:07

All files was downloaded this morning, but after downloading mORMot ans SQLite obj files now this sample works, possible was error with copying some files

#24 Re: mORMot 1 » Restrict connections count » 2015-03-02 09:40:56

Still trying to understand, can't use the sample 14 Server External, whe run got exception:

20150302 13352207 SQL       TSQLDBSQLite3Statement(0211C3E8) Project14ServerExternal.db PRAGMA table_info(`AuthGroup`)
20150302 13352207 SQL       TSQLDBSQLite3Statement(0211C5A0) Project14ServerExternal.db PRAGMA index_list(`AuthGroup`)
20150302 13352207 debug     TSQLRestStorageExternal(022691B0) GetFields=[]
20150302 13352402 EXCOS     EAccessViolation (C0000005) at 00406D43  stack trace API 005345F0 00534618 00409E94
20150302 13352402 SQL       TSQLDBSQLite3Statement(0211C3E8) Error {"EAccessViolation(020F9390)":{}} on Project14ServerExternal.db for "CREATE TABLE AuthGroup (ID INTEGER NOT NULL PRIMARY KEY,Ident TEXT COLLATE SYSTEMNOCASE NOT NULL UNIQUE,SessionTimeout INTEGER,AccessRights TEXT COLLATE SYSTEMNOCASE)" as "CREATE TABLE AuthGroup (ID INTEGER NOT NULL PRIMARY KEY,Ident TEXT COLLATE SYSTEMNOCASE NOT NULL UNIQUE,SessionTimeout INTEGER,AccessRights TEXT COLLATE SYSTEMNOCASE)"
20150302 13352402 EXCOS     EAccessViolation (C0000005) at 00406D43  stack trace API 005345F0 00534618 00409E94
20150302 13352500 EXC       ESQLite3Exception {"ErrorCode":0,"Message":"Error SQLITE_ERROR (1) - \"no such table: AuthGroup\" extended_errcode=1"} at 005CA6B3  stack trace API 005345F0 00534618 00409E94
20150302 13352544 EXC       EORMException {"Message":"TSQLRestStorageExternal.Create: TSQLAuthGroup: unable to create external missing field AuthGroup.Ident - SQL=\"ALTER TABLE AuthGroup ADD Ident TEXT COLLATE SYSTEMNOCASE NOT NULL UNIQUE\""} at 005E97C9  stack trace API 005345F0 00534618 00409D0C 7795DF53 7795DDBB 005E97C9 005B0497 005D0D1C 0063606E 0063640B 006162B8 0060F47A 0060F593 005CA059 005C8D7A 005C8E27 005C63E7 005CD221 0065A168 7772495D 779698EE 779698C4

Edit: this occured on the line 43:         CreateMissingTables;
As I can see is needed AuthGroup, but I think this sample should works out of the box, without any additionals from customer?

#25 Re: mORMot 1 » Restrict connections count » 2015-03-01 20:01:47

Can you clarify, why in 14 Example Server Weak if I change

    aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,false);
to
    aServer := TSQLRestServerDB.Create(aModel, ExeVersion.ProgramFilePath + 'Test.db3', False, '');
    aServer.CreateMissingTables;

after this I receive in 04 Client: 403 Forbidden
(aServer type changed to TSQLRestServerDB in var section)

#26 Re: mORMot 1 » Restrict connections count » 2015-03-01 18:00:45

Hm... I'm confused. I make the same steps as for 14 Example with Weak authentication, but my client received 'Forbidden' error from server.
Read sample line by line, make the same... Where I'm wrong? Something should I add to TSQLAuthUser? But in sample I can't found any code which do it

Edit: found initialization by default for user 'User' but still don't understand reason for Forbidden in my client

Edit2: fixed, this is my error: was used HTTP_DEFAULT_MODE for http server, with useHttpApiRegisteringURI works fine

#27 Re: mORMot 1 » Restrict connections count » 2015-02-28 16:27:08

I'm ready to create, but first I should understand how it works for already created classes smile

#28 Re: mORMot 1 » Restrict connections count » 2015-02-28 15:35:23

Thanks again for your answer, as usual happen - many questions and misunderstanding for the first project in new environment

#29 Re: mORMot 1 » Restrict connections count » 2015-02-28 14:55:27

If I understand correct with mORMot per user authentication I should use files with users and groups and server should know passwords (or hashes).
In my situation server don't know passwords, he will send password check request to the domain controller or this method supports too?

Additional description: in my situation customer type login/password in my program, this is login/password from active directory, but this can differ from currently logged user and can be changed at any time, one customer can login with different logins to get different access rights for the program content

#30 Re: mORMot 1 » Restrict connections count » 2015-02-28 12:14:17

Currently I need feature to limit max sessions with database - this is different licenses.
Client should establish connection with server using selected port number if available free sessiong,
send login and password (encrypted), server should check this user/password pair and authorize client
user/password - windows domain\login and password.
Client computer may be not in domain and this is reason to check user/password on the server side.

#31 Re: mORMot 1 » Restrict connections count » 2015-02-28 11:11:56

This was my second question about anonimous access and how to restrict it. Try to research docs

#32 Re: mORMot 1 » Restrict connections count » 2015-02-28 10:26:57

Thanks, please correct me if I'm wrong:

  DB := TSQLRestServerDB.Create(Model, sFile, False, 'NPM_5.x_Version');
  DB.CreateMissingTables;
  DB.TrackChanges([TSQLRec]);
  DB.ServiceRegister(TFullRecs, [TypeInfo(IFullRecs)], sicShared);
  DB.OnSessionCreate := OnSessionCreate;
  DB.OnSessionClosed := OnSessionClosed;
  Server := TSQLHttpServer.Create(SERVER_PORT, [DB], '+', HTTP_DEFAULT_MODE);
  Server.AccessControlAllowOrigin := '*';

I'm connect from client and work, but events OnSessionCreate and OnSessionClosed never fired

On client side:
    Client := TSQLHttpClient.Create('localhost', AnsiString(SERVER_PORT), Model);
    Result := Client.ServerTimeStampSynchronize;
    if Result then begin
      (Client as TSQLHttpClientGeneric).KeepAliveMS := 30000;
      (Client as TSQLHttpClientGeneric).Compression := [hcSynShaAes];
      Client.ServiceRegister([TypeInfo(IFullRecs)], sicShared);
    end

#33 Re: mORMot 1 » History settings » 2015-02-27 15:11:38

Question 3 is about if I setup TrackChanges to 100 history depth, and after this to 10 depht.
In this situation if I change record wich have more than 10 records in history, will be deleted all except last 10?

#34 mORMot 1 » Restrict connections count » 2015-02-27 09:21:04

EgorovAlex
Replies: 42

Hello,

This is possible to resctict maximum connections to the server?

#35 mORMot 1 » History settings » 2015-02-27 08:31:28

EgorovAlex
Replies: 4

Hello again,

Questions about history:
1. How to delete some history records?
2. If TSQLRecord was deleted from database, how I can determine it ID to get history for this record?
3. If database was opened first to store 100 rows, after this if I open it to store 10 rows in history
what occured with previously saved records, if they count already more than 10?
4. This is possible to change history depth on the fly for database?
5. This is possible to clear history depth for whole database for example up to 10 history item per record?
6. Not actual for me now, but for the future: if I add property in my TSQLRecord than I lost all previous history?
How to prevent this? How to store this history manually before I will lost it?

#36 mORMot 1 » Questions about TSQLRecord » 2015-02-26 12:22:10

EgorovAlex
Replies: 1

As usual some questions from beginner of mORMot:

1. I have TSQLRec with property Parent: TID;
Every record have parent record (tree structure)
and most searches will be by Parent,
should I mark this Parent field as "stored AS_UNIQUE" to improve speed of search?
2. If I do that with already created database, will be added index automatically?
3. Also my records have property Flags: set of (some flags from enum type);
This is possible to make search for this field and how?
Or I should declare it using another notation?

#37 Re: mORMot 1 » Filter or callback on the server side » 2015-02-26 11:22:03

If I undersnand right - I can use one interface IInvokable with many methods,
which is the best way - one interface with full server functionality
or interfaces per specific job?
Which way more faster and correct?

#38 Re: mORMot 1 » Filter or callback on the server side » 2015-02-25 17:55:01

I found, that this is possible with DTO, thanks to your comment above

and now I have this classes:

  TSQLMyRec = class(TSQLRecord)

  TDTOItem = class(TCollectionItem)

  TDTOCollection = class(TInterfacedCollection)

In my situation TDTOItem is exact copy of TSQLMyRec
there is no way to avoid duplication and use single code for this classes?

#39 Re: mORMot 1 » Filter or callback on the server side » 2015-02-25 16:06:56

This is possible to return from Service the TObjectList?

My idea - send request to the service with parameters and service should return list of TSQLRecord

#40 Re: mORMot 1 » Filter or callback on the server side » 2015-02-24 07:16:25

Thank you, I need hide some data and try now to research available ways.

Now I try to test Sample 20, but when start server from this sample I'm got error:
HttpSetServiceConfiguration failed: Access denied (5)

This error occured in unit SynCrtSock:
class function THttpApiServer.AddUrlAuthorize(const aRoot, aPort: RawByteString;
  Https: boolean; const aDomainName: RawByteString; OnlyDelete: boolean): string;

Oh, with admin right this is works, thanks )

#41 mORMot 1 » Filter or callback on the server side » 2015-02-23 18:27:13

EgorovAlex
Replies: 8

Hello,

My question: this is possible to filter search result on the server side and return to the client only records
which satisfy the conditions of the server logic.

In other word this is like per record authentication

#42 Re: mORMot 1 » TestSQL3 project failed some tests, why? » 2015-02-21 19:08:50

Thanks, now i have for test version from 21 Feb 2015 and have only one issue with TestSQL3:

1.4. Synopse PDF:
!  - TPdfDocument: 1 / 4 FAILED  6.63ms
  - TPdfDocumentGDI: 8 assertions passed  765.71ms
  Total failed: 1 / 12  - Synopse PDF FAILED  773.19ms

This is normal?

#43 mORMot 1 » TestSQL3 project failed some tests, why? » 2015-02-18 10:18:04

EgorovAlex
Replies: 9

Just install your latest version of mORMot, after starting TestSQL3 I'm have this log:
What I should to do to fix this problems?


   Synopse mORMot Framework Automated tests
  ------------------------------------------


1. Synopse libraries

1.1. Low level common:
  - System copy record: 22 assertions passed  115us
  - TDynArray: 519,427 assertions passed  119.86ms
  - TDynArrayHashed: 1,200,629 assertions passed  108.36ms
  - Fast string compare: 7 assertions passed  254us
!  - IdemPropName: 1 / 10 FAILED  647us
  - Url encoding: 105 assertions passed  1.02ms
  - IsMatch: 599 assertions passed  210us
  - Soundex: 35 assertions passed  124us
  - Numerical conversions: 783,638 assertions passed  125.50ms
  - Curr64: 20,039 assertions passed  974us
  - CamelCase: 5 assertions passed  181us
  - Bits: 4,614 assertions passed  204us
  - Ini files: 7,004 assertions passed  22.19ms
  - Unicode - Utf8: 60,081 assertions passed  847.21ms
  - Iso8601 date and time: 24,000 assertions passed  3.46ms
  - Url decoding: 1,100 assertions passed  284us
  - TSynTable: 873 assertions passed  3.28ms
  - TSynCache: 404 assertions passed  519us
  - TSynFilter: 1,005 assertions passed  2.10ms
  - TSynValidate: 677 assertions passed  742us
  - TSynLogFile: 42 assertions passed  505us
  Total failed: 1 / 2,624,316  - Low level common FAILED  1.24s

1.2. Low level types:
  - RTTI: 34 assertions passed  512us
  - Url encoding: 200 assertions passed  688us
  - Encode decode JSON: 250,232 assertions passed  82.67ms
  Total failed: 0 / 250,466  - Low level types PASSED  84.85ms

1.3. Big table:
  - TSynBigTable: 19,254 assertions passed  48.72ms
  - TSynBigTableString: 16,209 assertions passed  26.55ms
  - TSynBigTableMetaData: 384,060 assertions passed  974.13ms
  - TSynBigTableRecord: 452,185 assertions passed  2.21s
  Total failed: 0 / 871,708  - Big table PASSED  3.26s

1.4. Cryptographic routines:
  - Adler32: 1 assertion passed  303us
  - MD5: 1 assertion passed  306us
  - SHA1: 5 assertions passed  278us
  - SHA256: 5 assertions passed  224us
  - AES256: 6,372 assertions passed  54.04ms
  - Base64: 11,994 assertions passed  78.47ms
  Total failed: 0 / 18,378  - Cryptographic routines PASSED  135.15ms

1.5. Compression:
  - In memory compression: 12 assertions passed  173.20ms
  - Gzip format: 19 assertions passed  325.80ms
  - Zip format: 36 assertions passed  597.01ms
  - SynLZO: 3,006 assertions passed  63.20ms
  - SynLZ: 13,016 assertions passed  176.10ms
  Total failed: 0 / 16,089  - Compression PASSED  1.33s

1.6. Synopse PDF:
!  - TPdfDocument: 1 / 4 FAILED  2.39ms
  - TPdfDocumentGDI: 6 assertions passed  5.05ms
  Total failed: 1 / 10  - Synopse PDF FAILED  7.98ms


2. mORMot

2.1. Basic classes:
  - TSQLRecord: 47 assertions passed  235us
  - TSQLRecordSigned: 200 assertions passed  3.10ms
  - TSQLModel: 3 assertions passed  231us
  Total failed: 0 / 250  - Basic classes PASSED  4.21ms

2.2. File based:
  - Database direct access: 10,138 assertions passed  155.69ms
  - Virtual table direct access: 12 assertions passed  3.51ms
  - TSQLTableJSON: 19,030 assertions passed  33.30ms
  - TSQLRestClientDB: 599,030 assertions passed  5.64s
  Total failed: 0 / 628,210  - File based PASSED  5.83s

2.3. File based WAL:
  - Database direct access: 10,138 assertions passed  134.34ms
  - Virtual table direct access: 12 assertions passed  1.24ms
  - TSQLTableJSON: 19,030 assertions passed  28.67ms
  - TSQLRestClientDB: 599,030 assertions passed  5.57s
  Total failed: 0 / 628,210  - File based WAL PASSED  5.73s

2.4. Memory based:
  - Database direct access: 10,136 assertions passed  114.64ms
  - Virtual table direct access: 12 assertions passed  1.00ms
  - TSQLTableJSON: 19,030 assertions passed  25.71ms
  - TSQLRestClientDB: 667,323 assertions passed  6.02s
  Total failed: 0 / 696,501  - Memory based PASSED  6.16s

2.5. Client server access:
  - TSQLite3HttpServer: 2 assertions passed  14.07ms
     using THttpServer
!  - TSQLite3HttpClient: 1 / 1 FAILED  128.00s
!  - Http client keep alive: 50 / 84 FAILED  1251.99s
     first in 1187.99s,
!  - Http client multi connect: 50 / 84 FAILED  1279.99s
     first in 1215.99s,
  - Named pipe access: 3,085 assertions passed  701.46ms
     first in 85.47ms, done in 213.72ms i.e. 4678/s, aver. 213us, 21.8 MB/s
  - Local window messages: 3,084 assertions passed  61.03ms
     first in 3.69ms, done in 51.68ms i.e. 19346/s, aver. 51us, 90.4 MB/s
  - Direct in process access: 3,052 assertions passed  39.34ms
     first in 500us, done in 38.56ms i.e. 25931/s, aver. 38us, 121.2 MB/s
  Total failed: 101 / 9,392  - Client server access FAILED  2660.82s

2.6. Service oriented architecture:
  - Weak interfaces: 56 assertions passed  491us
  - Service initialization: 127 assertions passed  4.99ms
  - Direct call: 596,163 assertions passed  21.87ms
  - Server side: 596,173 assertions passed  21.55ms
  - Client side REST: 596,175 assertions passed  265.12ms
  - Client side JSONRPC: 596,173 assertions passed  295.46ms
  - Client side synchronized REST: 596,173 assertions passed  2.12s
  - Security: 135 assertions passed  856us
  - Custom record layout: 596,173 assertions passed  276.04ms
  Total failed: 0 / 3,577,348  - Service oriented architecture PASSED  3.01s

2.7. External database:
  - External records: 1 assertion passed  183us
  - Auto adapt SQL: 168 assertions passed  6.75ms
  - Crypted database: 253,272 assertions passed  160.77ms
  - External via REST: 243,436 assertions passed  503.36ms
  - External via virtual table: 243,436 assertions passed  872.39ms
  Total failed: 0 / 740,313  - External database PASSED  1.54s


Synopse framework used: 1.17
SQlite3 engine used: 3.7.14
Generated with: Delphi XE7 compiler

Time elapsed for all tests: 2689.22s
Tests performed at 2/18/2015 10:30:42 AM

Total assertions failed for all test suits:  103 / 10,061,191

! Some tests FAILED: please correct the code.
Done - Press ENTER to Exit

Board footer

Powered by FluxBB