#1 mORMot 1 » ECC public key export » 2018-07-17 12:14:50

Sabbiolina
Replies: 2

Hi, yesterday I tried to write this export for ECC keys generated by the library to be able to use jwt signed with ES256.

But something does not work, the DER is correct, but the tests say the points are not on the curve.

does anyone have any ideas?

some links:
https://8gwifi.org/PemParserFunctions.jsp
try:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYjcE4dwT3NRf8Zd5Edl7zih3O8JJeXjdevwI3HaJmgO2VsSx2Lq7+FDpEghPKj1xNqU4jPMTz7apH5inrNYi6Q==
-----END PUBLIC KEY-----

answer:
unable to convert key pair: encoded key spec not recognized: Invalid point coordinates


asn.1 inspector:
https://lapo.it/asn1js/



function TECCCertificateSecret.SaveToDER64:RawByteString;
const DER_SEQUENCE = $30;
      DER_INTEGER  = $02;
      DER_BIT       =$03;
      DER_OBJECT   = $06;
var RPrefix,SPrefix: integer;
    P,PU: PByteArray;
    Point: TEccPoint;
begin
  SetLength(result,91);
  fillchar(result[1],91,0);

  P := pointer(result);
  P[0]:=DER_SEQUENCE;
  P[1]:=$59; // total length
  p[2]:=DER_SEQUENCE;
  p[3]:=$13; // Length OIDs

  p[4]:=DER_OBJECT;
  p[5]:=$07; // length OID 1.2.840.10045.2.1ecPublicKey(ANSI X9.62 public key type)

  p[6]:=$2A;
  p[7]:=$86;
  P[8]:=$48;
  P[9]:=$CE;
  P[10]:=$3D;
  P[11]:=$02;
  P[12]:=$01;

  p[13]:=DER_OBJECT;
  p[14]:=$08;  // length OID 1.2.840.10045.3.1.7prime256v1(ANSI X9.62 named elliptic curve)

  p[15]:=$2A;
  p[16]:=$86;
  P[17]:=$48;
  P[18]:=$CE;
  P[19]:=$3D;
  P[20]:=$03;
  P[21]:=$01;
  P[22]:=$07;

  p[23]:=DER_BIT;   // bit sequence
  p[24]:=$42; // length Bit string

  p[25]:=$00; // Fixed
  p[26]:=$04; // Fixed
  inc(PByte(P),26+1);

  _clear(Point.x);
  _clear(Point.y);
  EccPointDecompress(Point,@fContent.Signed.PublicKey); // extract point x.y

  MoveFast(Point,p[0],ECC_BYTES*2); // on buffer

  result:='-----BEGIN PUBLIC KEY-----'+#13#10+binToBase64(result)+#13#10+'-----END PUBLIC KEY-----'+#13#10;
end;

#2 Re: mORMot 1 » WinHTTP, http.sys and WebSockets » 2016-05-27 13:38:10

I'm trying SynWinWebSockets.TWinHTTPWebSocketClient.

How to set https ?

#3 Re: mORMot 1 » WinHTTP, http.sys and WebSockets » 2016-05-27 06:33:19

I'd like to contribute to this development.
32-bit is fine for development
You wrote a simple example client-server (all in Delphi)?

#4 Re: mORMot 1 » WinHTTP, http.sys and WebSockets » 2016-05-26 14:31:09

I use two Win 8.1 with delphi 10 seattle, with Project31WinHTTPEchoServer (x64) no problem

but others samples hangs.

#5 Re: mORMot 1 » WinHTTP, http.sys and WebSockets » 2016-05-26 09:23:41

I dowload from fossil the source, but simple chat example hangs (64 bit compiled)

I'm wrong ?

#6 mORMot 1 » Allow configuring Proxy Username/Password on TWinHTTP » 2016-05-17 08:47:51

Sabbiolina
Replies: 0

Hi,
I noticed that the part of the user / pass authentication for HTTP proxy is not present for TwinHttp.

I had made these changes a few months ago:


unit SynCrtSock;


add  proxy_username and proxy_password

  THttpRequestExtendedOptions = record
    /// let HTTPS be less paranoid about SSL certificates
    // - IgnoreSSLCertificateErrors is handled by TWinHttp and TCurlHTTP
    IgnoreSSLCertificateErrors: Boolean;

    proxy_username,proxy_password:ansistring;  // add this
    /// allow HTTP authentication to take place at connection
    // - Auth.Scheme and UserName/Password properties are handled
    // by the TWinHttp class only by now
    Auth: record
      UserName: SynUnicode;
      Password: SynUnicode;
      Scheme: THttpRequestAuthentication;
    end;
  end;

THttpRequest

add these properties:

    property Proxy_username: ansistring // ansistring is
      read fExtendedOptions.Proxy_username
      write fExtendedOptions.Proxy_username;

    property Proxy_password: ansistring
      read fExtendedOptions.Proxy_password
      write fExtendedOptions.Proxy_password;

go to :
procedure TWinHTTP.InternalSendRequest(const aData: SockString);

add consts

const
  WINHTTP_OPTION_PROXY_USERNAME = $1002;
  WINHTTP_OPTION_PROXY_PASSWORD = $1003;

Add calls here:

procedure TWinHTTP.InternalSendRequest(const aData: SockString);
var L: integer;
    winAuth: DWORD;
begin
  with fExtendedOptions do
  if AuthScheme<>wraNone then begin
    case AuthScheme of
    wraBasic: winAuth := WINHTTP_AUTH_SCHEME_BASIC;
    wraDigest: winAuth := WINHTTP_AUTH_SCHEME_DIGEST;
    wraNegotiate: winAuth := WINHTTP_AUTH_SCHEME_NEGOTIATE;
    else raise EWinHTTP.CreateFmt('Unsupported AuthScheme=%d',[ord(AuthScheme)]);
    end;
    if not WinHttpSetCredentials(fRequest,WINHTTP_AUTH_TARGET_SERVER,
       winAuth,pointer(AuthUserName),pointer(AuthPassword),nil) then
      RaiseLastModuleError(winhttpdll,EWinHTTP);
	  
// add this:	  
   if PROXY_USERNAME<>'' then
   begin
    if not WinHttpSetOption( fRequest, WINHTTP_OPTION_PROXY_USERNAME, PWidechar(WideString(PROXY_USERNAME)), length(PROXY_USERNAME)) then
     RaiseLastModuleError(winhttpdll,EWinHTTP);
    end;

   if PROXY_PASSWORD<>'' then
   begin
    if not WinHttpSetOption( fRequest, WINHTTP_OPTION_PROXY_PASSWORD, PWidechar(WideString(PROXY_PASSWORD)), length(PROXY_PASSWORD)) then
    RaiseLastModuleError(winhttpdll,EWinHTTP);
   end;	 
  end;
 // end add
  
  if fHTTPS and IgnoreSSLCertificateErrors then
    if not WinHttpSetOption(fRequest, WINHTTP_OPTION_SECURITY_FLAGS,
       @SECURITY_FLAT_IGNORE_CERTIFICATES, SizeOf(SECURITY_FLAT_IGNORE_CERTIFICATES)) then
      RaiseLastModuleError(winhttpdll,EWinHTTP);
  L := length(aData);
  if not WinHttpSendRequest(fRequest, nil, 0, pointer(aData), L, L, 0) or
     not WinHttpReceiveResponse(fRequest,nil) then
    RaiseLastModuleError(winhttpdll,EWinHTTP);
end;

#7 mORMot 1 » MongoDb Version >=3.0 » 2016-05-13 09:38:57

Sabbiolina
Replies: 1

Hello, I realized that with version 3.0 you can not receive the collection list using this method:

constructor TMongoDatabase

[..]

  if colls.Init(Client.Connections[0].GetBSONAndFree(
     TMongoRequestQuery.Create(aDatabaseName+'.system.namespaces',null,'name',maxInt))) then
    // e.g. [ {name:"test.system.indexes"}, {name:"test.test"} ]
    while colls.Next do begin
      full := colls.item.DocItemToRawUTF8('name');
      if full<>'' then begin
        split(full,'.',db,coll);
        if db<>aDatabaseName then
          raise EMongoConnectionException.CreateUTF8(
            '%.Create: invalid "%" collection name for DB "%"',
            [self,full,aDatabaseName],Client.Connections[0]);
        fCollections.AddObject(coll,TMongoCollection.Create(self,coll));
      end;
    end;

I made a change to use the new method (listCollections):

   Client.Connections[0].RunCommand(aDatabaseName,'listCollections',Vresponse);

   if not varisnull(Vresponse) then
   begin
    for t:=0 to Vresponse.cursor.firstBatch._count-1 do
    begin
     v:=Vresponse.cursor.firstBatch._(t);
     coll:=V.name;
     if coll<>'' then
      fCollections.AddObject(coll,TMongoCollection.Create(self,coll));
    end;
   end;

#8 mORMot 1 » Bson Problem » 2016-05-02 20:46:06

Sabbiolina
Replies: 1

Hi.

In a MongoDB collection I have this :

{
    "_id" : "FBB5F6O2ZE2EVJ6AG5JHORWBQA",
    "EmptyArray" : []
}


I do this:

var
ADocs: TVariantDynArray

Coll.FindDocs(ADocs, '', 0, 0, []);

And I get:

[
{  "_id" : "FBB5F6O2ZE2EVJ6AG5JHORWBQA",
    "EmptyArray" : null   <------------------------------------------NULL ?
}
]


So I play with debugger and I make this change:

// used by TBSONElement.ToVariant() method and BSONToDoc() procedure
procedure BSONItemsToDocVariant(Kind: TBSONElementType; BSON: PByte;
  var Doc: TDocVariantData; Option: TBSONDocArrayConversion);
const OPTIONS: array[TBSONDocArrayConversion] of TDocVariantOptions =
    ([],[dvoReturnNullForUnknownProperty],
        [dvoValueCopiedByReference,dvoReturnNullForUnknownProperty]);
var k: TDocVariantKind;
    i,n,cap: integer;
    items: array[0..63] of TBSONElement;
begin // very fast optimized code
  if not (Kind in [betDoc,betArray]) then
    VarCastError;
  if (BSON=nil) {or (BSON^=byte(betEOF))} then // I remove this, a empty array is not null (I think)
    TVarData(Doc).VType := varNull else begin
    if Kind=betDoc then
      k := dvObject else
      k := dvArray;
    Doc.Init(OPTIONS[Option],k);
    cap := 0;
    repeat
      n := 0;
      while items[n].FromNext(BSON) do begin
        inc(n);
        if n=length(items) then
          break;
      end;
      if n=0 then
        break;
      inc(cap,n);
      if cap<512 then
        Doc.Capacity := cap else
        if Doc.Capacity<cap then
          Doc.Capacity := cap+cap shr 3; // faster for huge arrays
      for i := 0 to n-1 do begin
        if Kind=betDoc then
          SetString(Doc.Names[i+Doc.Count],PAnsiChar(items[i].Name),items[i].NameLen);
        items[i].ToVariant(Doc.Values[i+Doc.Count],Option);
      end;
      Doc.SetCount(Doc.Count+n);
    until (BSON=nil) or (BSON^=byte(betEOF));
  end;
end;

I do some tests, and all go well.

#9 mORMot 1 » Maybe a bug or not » 2016-04-30 15:01:31

Sabbiolina
Replies: 1

HI,

Look at this array:

[{"Name":"Paul","born":isodate("1969-12-31")}]

I discovered that function below can not read it, so I made a small change.




function GotoNextJSONObjectOrArrayInternal(P,PMax: PUTF8Char; EndChar: AnsiChar): PUTF8Char;
label Prop;
begin
  result := nil;
  repeat
    case P^ of
    '{','[': begin
      if PMax=nil then
        P := GotoNextJSONObjectOrArray(P) else
        P := GotoNextJSONObjectOrArrayMax(P,PMax);
      if P=nil then exit;
    end;
    ':': if EndChar<>'}' then exit else inc(P); // syntax for JSON object only
    ',': inc(P); // comma appears in both JSON objects and arrays
    '}': if EndChar='}' then break else exit;
    ']': if EndChar=']' then break else exit;
    '"': begin
      P := GotoEndOfJSONString(P);
      if P^<>'"' then
        exit;
      inc(P);
    end;
    '-','+','0'..'9': // '0123' excluded by JSON, but not here
      repeat
        inc(P);
      until not (P^ in DigitFloatChars);
    't': if PInteger(P)^=TRUE_LOW then inc(P,4) else goto Prop;
    'f': if PInteger(P)^=FALSE_LOW then inc(P,5) else goto Prop;
    'n': if PInteger(P)^=NULL_LOW then inc(P,4) else goto Prop;
    '''': begin
      repeat inc(P); if P^<=' ' then exit; until P^='''';
      repeat inc(P) until not(P^ in [#1..' ']);
      if P^<>':' then exit;
    end;
    '/': begin
      repeat // allow extended /regex/ syntax
        inc(P);
        if P^=#0 then
          exit;
      until P^='/';
      repeat inc(P) until not(P^ in [#1..' ']);
    end;
    else
    begin
Prop: if not (P^ in ['_','A'..'Z','a'..'z','0'..'9','$']) then
        exit; // expect e.g. '{age:{$gt:18}}'
      repeat
        inc(P);
       until not (P^ in ['_','A'..'Z','a'..'z','0'..'9','.']);
       while P^ in [#1..' '] do inc(P);
/////////////////////////////////////////////////////////////////////// start
       if P^ ='(' then
       begin
        inc(p);
        while P^ in [#1..' '] do inc(P);
        if p^='"' then
        begin
         P := GotoEndOfJSONString(P);
         if P^<>'"' then
          exit;
        end;
        inc(p);
        while P^ in [#1..' '] do inc(P);
        if P^<>')' then
          exit;
        inc(p);
       end
       else 
/////////////////////////////////////////////////////////////////////// end
        if P^<>':' then
        exit;
    end;
    end;
    if P^ in [#1..' '] then repeat inc(P) until not(P^ in [#1..' ']);
    if (PMax<>nil) and (P>=PMax) then
      exit;
  until P^=EndChar;
  result := P+1;
end;

#10 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-29 09:51:46

I looked at the use of passwords in mormot.pas

As you say, it is difficult to unify control of password authentication for all systems.

Especially for the use of passwordhexA as SALT communication.
Let me say that your constant 'salt' used between client and server is cryptographically poor, and hides a big problem in my opinion:

if you had a real cryptographic salt for each user how to connect a client to the server?
the client should already know the SALT.


Indeed passwordhexA = sha256 ('salt' + password);

perhaps think of a signature property in user with a separate management of the password might be interesting.

#11 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-28 23:19:48

What's the matter?
have a simple checkpassword violates the principles?

canUserLog instead?

#12 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-28 21:03:18

Tomorrow I'll try to improve the function checkpassword to meet the needs of existing authentication systems.

The endeavor to create a new class that inherits from authattpbasic just for that single detail (which by the way would be useful to everybody and would not break compliance with the HTTP Basic Auth standard) just seems to me like an unjustified waste of time...

#13 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-27 19:22:32

Everything can be improved
I think it's the spirit of open source.

Leaving aside the usefulness or not to rewrite a new class of authentication httpbasic, I think the CheckCredential, as has been thought, is more efficient function that I have proposed and implemented long ago: CanUserLog.

The checkCredential, without changing a single line of mormot.pas, allows a developer to have full control, if he likes it, the authentication process could easily integrate with any variables derived from object tsqlauthuser.

While canUserLog, acting perhaps a little late in the authentication process, removing the ability to implement custom rules.

#14 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-26 06:54:19

we are almost there ...

but, for the http basic need a small patch

function TSQLRestServerAuthenticationHttpBasic.Auth(Ctxt: TSQLRestServerURIContext): boolean;
var userPass,user,pass,expectedPass: RawUTF8;
    U: TSQLAuthUser;
    Session: TAuthSession;
begin
  if Ctxt.InputExists['UserName'] then begin
    result := false; // allow other schemes to check this request
    exit;
  end;
  result := true; // this authentication method is exclusive to any other
  if GetUserPassFromInHead(Ctxt,userPass,user,pass) then begin
    U := GetUser(Ctxt,user);
    if U<>nil then begin
    try
      expectedPass := U.PasswordHashHexa;
      U.PasswordPlain := pass; // override with SHA-256 hash from HTTP header
      if U.PasswordHashHexa=expectedPass then begin
        fServer.SessionCreate(U,Ctxt,Session);
        if Session<>nil then begin
          // see TSQLRestServerAuthenticationHttpAbstract.ClientSessionSign()
          Ctxt.SetOutSetCookie((COOKIE_SESSION+'=')+CardinalToHex(Session.IDCardinal));
          Ctxt.Returns(['result',Session.IDCardinal,'logonname',Session.User.LogonName]);
          exit; // success
        end;
      end;
    finally U.Free; end
   end;
   Ctxt.AuthenticationFailed; //<---------------------------  moved
   exit;
  end;
  Ctxt.Call.OutHead := 'WWW-Authenticate: Basic realm="mORMot Server"';;
  Ctxt.Error('',HTML_UNAUTHORIZED); // will popup for credentials in browser
end;

I moved the call Ctxt.AuthenticationFailed,
because in the case of existing user with the wrong password, the sesionfail was not called

#15 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-24 14:26:01

unfortunately it does not work.

I write a little cli/srv program, so you can debug.

ServerHB.pas

program ServerHB;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  SynCommons, SynLog, mORMot,MormotHTTPserver,
  mORMotSQLite3, SynSQLite3Static;

const
 SERVER_PORT='888';

type
  ICalculator = interface(IInvokable)
    ['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
    function Add(n1,n2: integer): integer;
  end;

  Tmysess = class
   function myerror(Sender: TSQLRestServer; Session: TAuthSession; Ctxt: TSQLRestServerURIContext): boolean;
  end;

  TMYAuthUser = class(TSQLAuthUser)
  private
   Fenabled:boolean;
  published
   property enabled: Boolean read Fenabled write Fenabled; // abilitato o no

   function CanUserLog(Ctxt: TSQLRestServerURIContext): boolean; override;
  end;

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

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

function TMYAuthUser.CanUserLog(Ctxt: TSQLRestServerURIContext): boolean;
begin
 result:=true;
end;

function Tmysess.myerror(Sender: TSQLRestServer; Session: TAuthSession; Ctxt: TSQLRestServerURIContext): boolean;
var
 tmp:rawutf8;
begin
 tmp:=ctxt.Call.InHead;
 writeln('fail_inhead:'+tmp);
 tmp:=ctxt.InHeader['Authorization'];
 writeln('fail_auth:'+tmp);
 result:=false;
end;

var
  aModel: TSQLModel;
  Restsrv:TSQLRestServerDB;
  u:TMYAuthUser;
  aHttpServer:TSQLHttpServer;
  mySess:Tmysess;
begin
  with TSQLLog.Family do begin
    Level := LOG_VERBOSE;
    EchoToConsole := LOG_VERBOSE; // log all events to the console
  end;
  TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator)]);
  mysess:=Tmysess.Create;
  aModel := TSQLModel.Create([TMYAuthUser,TSQLauthGroup]);
  try
    Restsrv:=TSQLRestServerDB.Create(aModel,':memory:',false);
    try
      restsrv.CreateMissingTables; // we need AuthGroup and AuthUser tables
      restsrv.AuthenticationRegister(TSQLRestServerAuthenticationHttpBasic);

      u:=TMYAuthUser.Create;
      u.LogonName:='myuser';
      u.DisplayName:='Test User';
      u.PasswordPlain:='test';
      u.enabled:=true;
      u.GroupRights:=TSQLAuthGroup(3);
      RestSrv.Add(U,true);
      u.Free;
      if not RestSrv.TableHasRows(TSQLAuthGroup) then
       TSQLAuthGroup.InitializeTable(RestSrv,'',[itoNoIndex4ID, itoNoIndex4UniqueField, itoNoIndex4NestedRecord, itoNoIndex4RecordReference]);

      restsrv.ServiceDefine(TServiceCalculator,[ICalculator],sicpersession);
      RestSrv.OnSessionFailed:=mysess.myerror;
      aHttpServer:=TSQLHttpServer.Create(SERVER_PORT,[RestSrv],'+',useHttpApiRegisteringURI);
      aHttpServer.AccessControlAllowOrigin:='*'; // allow cross-site AJAX queries

      write('Press [Enter] to close the server.');
      readln;
    finally
      restsrv.Free;
    end;
  finally
    aModel.Free;
  end;
end.

ClientHB.pas

program ClientHB;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  SynCommons, SynLog, mORMot,MormotHTTPserver,
  mORMotSQLite3, SynSQLite3Static,mORMotHttpClient;

const
 CLIENT_PORT='888';
 CLIENT_ADDR='localhost';

type
  ICalculator = interface(IInvokable)
    ['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
    function Add(n1,n2: integer): integer;
  end;


var
 aModel:TSQLModel;
 aClient:TSQLHttpClient;
 I:ICalculator;
begin
  with TSQLLog.Family do begin
    Level := LOG_VERBOSE;
//    EchoToConsole := LOG_VERBOSE; // log all events to the console
  end;
   TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator)]);
 aModel := TSQLModel.Create([TSQLAuthUser]);
 aClient := TSQLHttpClientWinHTTP.Create(CLIENT_ADDR,CLIENT_PORT,aModel);

 writeln('auth WRONG test:');
 if TSQLRestServerAuthenticationHttpBasic.ClientSetUser(aClient,'myuser','test2',passClear) then
 begin
  writeln('auth OK');
  aClient.ServiceDefine([ICalculator],sicpersession);
  if aClient.Services['Calculator'].Get(I) then
   writeln('result: '+ intToStr(I.Add(2,3)));
 end
 else Writeln('auth KO');
 writeln('');
 writeln('auth RIGHT test:');
 if TSQLRestServerAuthenticationHttpBasic.ClientSetUser(aClient,'myuser','test',passClear) then
 begin
  writeln('auth OK');
  aClient.ServiceDefine([ICalculator],sicpersession);
  if aClient.Services['Calculator'].Get(I) then
   writeln('result: '+ intToStr(I.Add(2,3)));
 end
 else Writeln('auth KO');

 write('Press [Enter] to close the client.');
 readln;
end.

#16 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-22 21:44:58

I rewrote the function, as I think should be its logic


function TSQLRestServerURIContext.Authenticate: boolean;
var aSession: TAuthSession;
    i: integer;
begin
  if Server.HandleAuthentication then
  begin
   result := false;
   Session := CONST_AUTHENTICATION_SESSION_NOT_STARTED;
   Server.fSessions.Lock;
   try
    aSession := nil;
    if Server.fSessionAuthentication<>nil then
    begin
     for i := 0 to length(Server.fSessionAuthentication)-1 do
     begin
      aSession := Server.fSessionAuthentication[i].RetrieveSession(self);
      if aSession<>nil then
      begin
       {$ifdef WITHLOG}
       log.Log(sllUserAuth,'%/%',[aSession.User.LogonName,aSession.ID],self);
       {$endif}
       SetString(fSessionAccessRights,PAnsiChar(@aSession.fAccessRights),
       sizeof(TSQLAccessRights)); // override access rights
       Call^.RestAccessRights := pointer(fSessionAccessRights);

       result:=true;
       exit;
      end;
     end;
    end;
   finally
    Server.fSessions.UnLock;
   end;
   if ((Service<>nil) and Service.ByPassAuthentication) or
      ((MethodIndex>=0) and Server.fPublishedMethod[MethodIndex].ByPassAuthentication) then
   begin
    result:=true;
    exit;
   end;
  end
  else
  begin// default unique session if authentication is not enabled
    Session := CONST_AUTHENTICATION_NOT_USED;
    result := true;
  end;
end;


with my test program **NOT** works, but you have to see if it does what it needs

#17 Re: mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-22 21:26:28

there is something wrong...

      // 2. handle security
      if (not Ctxt.Authenticate) or
         ((Ctxt.Service<>nil) and
           not (reService in Call.RestAccessRights^.AllowRemoteExecute)) then
        Ctxt.AuthenticationFailed else

Ctxt.AuthenticationFailed never fire.

I suspect that the function ctxt.authenticate returns true when it should return false.

I have no patch available now

#18 Re: mORMot 1 » multipart/form-data not implemented yet » 2015-04-18 09:48:25

Hi AB,

there is a small fix for the multipart feature:

the last boundary ends with '-'

function MultiPartFormDataDecode(const MimeType,Body: RawUTF8;
  var MultiPart: TMultiPartDynArray): boolean;
var boundary,EndBoundary: RawUTF8;
    i,j: integer;
    P: PUTF8Char;
    part: TMultiPart;
begin
  result := false;
  i := PosEx('boundary=',MimeType);
  if i=0 then
    exit;
  boundary := '--'+trim(copy(MimeType,i+9,200))+#13#10;
  Endboundary := '--'+trim(copy(MimeType,i+9,200))+'--'+#13#10;
  i := PosEx(boundary,Body);
  if i<>0 then
  repeat
    inc(i,length(boundary));
    if i=length(body) then
      exit; // reached the end
    P := PUTF8Char(Pointer(Body))+i-1;
    Finalize(part);
    repeat
      if IdemPCharAndGetNextItem(P,
         'CONTENT-DISPOSITION: FORM-DATA; NAME="',part.Name,'"') then
        IdemPCharAndGetNextItem(P,'; FILENAME="',part.FileName,'"') else
      if IdemPCharAndGetNextItem(P,'CONTENT-TYPE: ',part.ContentType) or
         IdemPCharAndGetNextItem(P,'CONTENT-TRANSFER-ENCODING: ',part.Encoding) then;
      GetNextLineBegin(P,P);
      if P=nil then
        exit;
    until PWord(P)^=13+10 shl 8;
    i := P-PUTF8Char(Pointer(Body))+3; // i = just after header
    j := PosEx(boundary,Body,i);
    if j=0 then
    begin          // try last bondary
     j := PosEx(endboundary,Body,i);
     if j=0 then
      exit;
    end;
    part.Content := copy(Body,i,j-i-2); // -2 to ignore latest #13#10
    {$ifdef UNICODE}
    if (part.ContentType='') or (PosEx('-8',part.ContentType)>0) then
      SetCodePage(part.Content,CP_UTF8,false) else // ensure raw field value is UTF-8
    {$endif}
    if IdemPropNameU(part.Encoding,'base64') then
      part.Content := Base64ToBin(part.Content);
    // note: "quoted-printable" not yet handled here
    SetLength(MultiPart,length(MultiPart)+1);
    MultiPart[high(MultiPart)] := part;
    result := true;
    i := j;
  until false;
end;

#20 Re: mORMot 1 » Receive Json by Calculator Sample » 2015-04-15 16:35:20

Tnxs

but then there are disadvantages to the client mormot?

#21 Re: mORMot 1 » Receive Json by Calculator Sample » 2015-04-15 08:29:12

hello, AB

I reopen this old 3d because something escapes me ...

I Mormot the server with a method similar interface:

Download function (json: RAWutf8): TserviceCustomAnswer;

if I call (from Mormot client)

POST /BSWPZNOCNDTUG6DU63Z5V7LNFE/object.Download HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: application/json; charset=UTF-8
Accept: */*
Accept-Encoding: synlz
Authorization: Basic xxxxxx
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows; Synopse mORMot 1.18.1204 TWinHTTP)
Content-Length: 99
Host: test:888
Cookie: mORMot_session_signature=4DDC024C; mORMot_session_signature=4DDC024C

["{\"ID\":\"directory\",\"file\":\"filename\",\"seek\":0}"]


this works

but, reading this 3d
I try, from NON mormot client:

[{"ID":"directory","file":"filename","seek":0}]

but fails, also

{"ID":"directory","file":"filename","seek":0}

fails again

ps my model is sicPerSession

#23 mORMot 1 » [TSQLRestServerAuthenticationHttpBasic] and others » 2015-04-11 09:05:32

Sabbiolina
Replies: 26

Hi AB,

I was looking for a way to be able to manage access prohibited.

There is' a way to know on the server when a call is prohibited?

I would love to be able to have all the call to extract who is logging, that url was calling, his ip.

All this in order to realize two things: managing the attacks (ip ban with external firewall) may notify the user attempts wrong.

Tnx

#24 mORMot 1 » [MongoDB] MongoDB V3 is out » 2015-03-05 20:11:56

Sabbiolina
Replies: 2

...and works well with mORMot !

#25 Re: mORMot 1 » [HTTP PROXY] how to provide the credentials? » 2015-01-21 19:41:47

I only want to enable my application, not the entire computer.

There is no API to call?

#26 mORMot 1 » [HTTP PROXY] how to provide the credentials? » 2015-01-21 10:38:00

Sabbiolina
Replies: 4

Hi AB,

Today I found myself in the situation of having to set up an http proxy to surf outside the local network.

I did not find documentation on how to provide credentials.

The program (win32) use this:
var
  aclient: TSQLHttpClient

begin
  aclient: TSQLHttpClientWinHTTP.Create = (addr, SERVER_PORT, Amodel);

I changed in:

   aclient: TSQLHttpClientWinHTTP.Create = (addr, SERVER_PORT, Amodel, false, 'proxy: 8080', 'localhost');

and works with an authenticated proxy-based ip origin, but if authentication is on user / password does not work.

but how do I add the user and password?

#28 Re: mORMot 1 » [MONGODB] self-generation of _ID » 2015-01-15 19:15:18

I found the problem!

These two functions are not equivalent:
1) function TMongoCollection.AggregateCall(const pipelineJSON: RawUTF8; var reply,res: variant): boolean;
2) function TMongoCollection.AggregateCall(const pipelineArray: variant; var reply,res: variant): boolean;

The first works well.
The second one does not insert properly the pipeline as an array.

Temporary patch:

function TMongoCollection.AggregateDoc(const pipelineArray: variant): variant;
var reply: variant;
begin
  if AggregateCall(RAWutf8(pipelineArray),reply,result) then   // I added RAWutf8 to force the use of the first
    TDocVariant.GetSingleOrDefault(result,result,result) else
    SetVariantNull(result);
end;

#30 Re: mORMot 1 » [MONGODB] self-generation of _ID » 2015-01-15 09:44:56

I made a test project.
The program adds a user randomly at each start, the first works, but the second does not work.
I get the same result with xe2 and XE7, mongodb 2.6.1 and 2.6.6.


Demo

#31 mORMot 1 » [MONGODB] self-generation of _ID » 2015-01-15 00:57:21

Sabbiolina
Replies: 6

Hi AB,


I'm trying to add users to authuser.
At the second start of the program I have four, but I can not add the fifth.

No error or exception, but in the end I found the answer in the log:

There is a problem with the self-generation of _ID

function TSQLRestStorageMongoDB.EngineNextID: TID;
procedure ComputeMax_ID;
var res: variant;
begin
  if not fIndexesCreated then
    CreateIndexes;
  res := fCollection.AggregateDoc('{$group:{_id:null,max:{$max:"$_id"}}}'); <----- WRONG, works only in shell
  if DocVariantType.IsOfType(res) then
    fEngineLastID := VariantToInt64Def(res.max,0);
  {$ifdef WITHLOG}
  fOwner.LogFamily.SynLog.Log(sllInfo,'Computed EngineNextID=%',[fEngineLastID],self);
  {$endif}
end;

Error from log

20150115 01380705 SQL   {collection:"MAINHEAD.$cmd",opCode:"opQuery",requestID:5,query:{aggregate:"AuthUser",pipeline:"{$group:{_id:0,max:{$max:\"$_id\"}}}"},numberToReturn:1}
20150115 01380746 DB    {ReplyHeader:{ResponseFlags:8,RequestID:49,ResponseTo:5,CursorID:0,StartingFrom:0,NumberReturned:1,ReplyDocuments:[{errmsg:"exception: wrong type for field (pipeline) 2 != 4",code:13111,ok:0}]}


Looking around I found these:
http://docs.mongodb.org/manual/referenc … op._S_type
http://stackoverflow.com/questions/1239 … 3111-error

#32 Re: mORMot 1 » [NAMEDPIPE] avoid double server » 2015-01-14 09:45:02

I'm sorry, I forgot a part of the post ...

I wondered if that flag could help to avoid the risk of starting two servers with the same namedpipe.

Only the first working properly, the second does not detect any error.
I'm not very expert namedpipe, I wondered then if that flag could help in better manage the situation.

#33 mORMot 1 » [NAMEDPIPE] avoid double server » 2015-01-13 23:04:30

Sabbiolina
Replies: 2

Hi AB,

I'm looking at the function of creating the namedpipe:

Why are you using this flag (PIPE_UNLIMITED_INSTANCES)?

Would not it be helpful to use FILE_FLAG_FIRST_PIPE_INSTANCE to prevent the creation of the same pipe?

#34 Re: mORMot 1 » [SESSIONS] proposal for improvement » 2015-01-12 19:14:24

So I like it!

You propose to add new features that maintain the consistent hash table, and let the generic ones for the use you've always rightly, ie to create a hash table only at the time of the search.

Or to decide in the creation of the object hash:
LiveHash / onDemandHash

I think that the benefit to the whole system is great, and also help to keep performing the rest of the programming.

For searches that are done on a list in memory always think it's always better to the help of the hash table to always have the results on time. Too many times you create bottlenecks for the lists that grow dramatically, beyond what you thought was right in development of the software.

#35 Re: mORMot 1 » [SESSIONS] proposal for improvement » 2015-01-12 17:37:33

certainly if you add values definitely unique gains on research on a set of data that does not change.

is a bit like adding a million records to a database without an index and then create it in the end.

but in the case of the sessions, or other data sets that change frequently,
continue to rehash all the elements lose the advantage of instant search.

  HL:=TRawUTF8ListHashed.Create(true);

  for t: = 1 to 50000 do
  begin
   Rec: = TmyOBJ.create;
   Rec.id:='id_'+inttostr(t);
   Rec.msg: = 'Msg';
   HL.AddObjectIfNotExisting (Rec.id, Rec);
  end;

41 seconds on I7-3820

#36 Re: mORMot 1 » [SESSIONS] proposal for improvement » 2015-01-12 16:00:37

I was watching the various procedures of hash in mormot, but I can not find the procedure for the single insertion or deletion.
It seems to me that you make extensive use of rehash, I'm wrong?

#37 mORMot 1 » [SESSIONS] proposal for improvement » 2015-01-11 18:03:47

Sabbiolina
Replies: 6

Hello AB,
  I'm curious to know why you have chosen to manage the session list by scrolling and not through a hashlist.

I also noticed that with each new call you test the timeout.
I propose to save the minimum value (the next timeout) to the first test of the entire list by saving the timeout closer. So the next time you call, if the minimum is not reached, you can avoid doing the test on all elements.

#39 mORMot 1 » [INTERFACED] autoreconnect » 2015-01-08 21:58:26

Sabbiolina
Replies: 3

Hi AB.

I'm making several calls to the server rest, so I keep saved interface (MAINshd).

I would like to repeat the call automatically in case of connection failure.

var
  retry: integer;
  done: boolean;
  Modd: TSQLModel;
  CLID: TSQLHttpClient;
begin
[..]

  retry: = 3;
  repeat
   done: = true;
   try
    res: = MAINshd.myInterfacedFunction ();
   except
    dec (retry);
    MAINshd: = connc (SERVER_ADDR, user, pass, modd, CLID); // Rebuild interface
    done:=retry<0;
   end;
  until done;
  [..]

end;

It works, but I was wondering if there was a smarter way.
Keep in mind that I could reconnect to another server mORMot.

#40 Re: mORMot 1 » [JSON] New behavior of _json » 2015-01-07 19:42:27

with 2 type of unmanageable data type we must add this:

function VarIsNullEmpty(const V: Variant): Boolean;
begin
  Result := (FindVarData(V)^.VType = varNull) or (FindVarData(V)^.VType = varEmpty);
end;

@ab
bug is here:
with TdocVariantData(v) do addvalue('day',dayofweek(now));

addvalue must check if V is valid before adding.
do you agree?

#41 Re: mORMot 1 » [JSON] New behavior of _json » 2015-01-07 17:46:03

Here the first bug with emptyvar:

procedure TForm1.Button6Click(Sender: TObject);
var
 V:variant;
 res:RAWutf8;
begin
 res:='{mytime: '+floattostr(now)+'}';
 v:=_json(res);


 with TdocVariantData(v) do
  addvalue('day',dayofweek(now));

 memo1.Lines.Add('Result: '+rawutf8(v))
end;

initialization
if SetThreadLocale($0409) then
  GetFormatSettings; // resets all locale-specific variables to LCID_US

with codepage initialization all is ok:
Result: {"mytime":42011.7791793634,"day":4}


but with italian codepage (comma for decimals):
Result:


With NO EXCEPTION!!!

I think also that it is better to think about the path taken by the compiler:
if a variant there is better to return a null.

a null value will become an exception and will be manageable.

#42 Re: mORMot 1 » [JSON] New behavior of _json » 2015-01-07 15:34:56

But there's null us that there is no result, a mistake.

When you ask for an item that a function, returns null if you know that there is a valid result.

I agree on the speech of _Json ('') but we must remember that in a LATE BINDING case: an element not present is not empty but null.

so I am not sure that you want to change this result in case of error.

I think it's best to use a varIsNull to find fault.
I think having to test alternately varIsnull and / or varIsempty eventually bring more mistakes than good.

Ultimately, also to avoid having to change the past procedures, it would be better that the function returns a null _json on error and not an empty.

#43 mORMot 1 » [JSON] New behavior of _json » 2015-01-07 14:32:39

Sabbiolina
Replies: 9

Hi AB,
in last 2 weeks you changed the function _json:

before _json ('') was returning a null variant
now returns a 'unassigned'


[..]
var
res:RAWutf8;
vres:variant;
begin
res:='';
vres:=_json(res);

if varisnull(vres) then exit; // now don't fire

first if you converted an empty string the variant was null, not anymore.
is the desired result or a bug?

#45 mORMot 1 » [SESSION] how to retrive a link to object » 2015-01-07 07:50:03

Sabbiolina
Replies: 3

Hello AB,
  how can I do to save an object (eg database) in a session to use it in a REST interface?

My aim is to have a link to a specific external database without having to look at every call of the same authenticated session.

Tnxs

#47 Re: mORMot 1 » [mongo] insertjson usage » 2014-12-19 09:37:50

sql:='select * from mysqlitedb';
r.Prepare(demo.DB,sql);

json:=demo.ExecuteJSON(sql,TRUE);

after run:

json:='[{_id:1,name:"test1"},{_id:2,name:"test2"},{_id:3,name:"test3"}]';

after I send it to a rest server:


var
pu:Putf8char;
begin
  pu:=putf8char(json);

  collection.InsertJSON(pu);

end;

collection is a TmongoCollection


the aim was to read a db SqlLite , and add in mongodb.

I thought the json generated is converted into an array, instead he inserirsce an array in an element.

how do I do it ?

#48 mORMot 1 » [mongo] insertjson usage » 2014-12-19 00:55:12

Sabbiolina
Replies: 5

Hi AB,

I'm trying to insert this:

[{_id:1,name:"test1"},{_id:2,name:"test2"},{_id:3,name:"test3"}]

using TmongoCollection.insertJSON


But I find a single line in mongodb:

{
    "_id" : ObjectId("54937698dd6a6a28075ffb63"),
    "0" : {
        "_id" : 1,
        "name" : "test1"
    },
    "1" : {
        "_id" : 2,
        "name" : "test2"
    },
    "2" : {
        "_id" : 3,
        "name" : "test3"
    }
}

Why ?

#49 Re: mORMot 1 » [mongodb] FindDocs with bad syntax » 2014-12-19 00:47:22

How to reset the error (without having to reconnect) ?

#50 mORMot 1 » [mongodb] FindDocs with bad syntax » 2014-12-18 22:23:47

Sabbiolina
Replies: 3

Hi AB,

I ran into this error:

procedure TForm1.Button1Click(Sender: TObject);
var
 t:integer;
 Coll: TMongoCollection;
 Docs: TVariantDynArray;
begin
 log('Start');
 try
  fClient:=TMongoClient.Create('localhost',27017);

  fDB:=fClient.Database['test25'];
  Coll := fDB.CollectionOrCreate['testColl'];

  coll.Insert('{"Age":?,Name: ?}',[1,'test1']);
  coll.Insert('{"Age":?,Name: ?}',[2,'test2']);

  if BADjson.Checked then
  begin
   try
    coll.FindDocs('{Age:{$gt,1}}',[],docs,null); // <------------------------ bad syntax
    for t:=Low(docs) to High(docs) do if not varisnull(docs[t].Name) then Log('Docs:'+docs[t].Name);
   except
    on e: exception do  log('EXC1: '+e.Message);
   end;
  end;

  Coll := fDB.CollectionOrCreate['testColl'];

  try
   coll.FindDocs('{Age:{$gt:1}}',[],docs,null); // <------------------------ right syntax
   for t:=Low(docs) to High(docs) do if not varisnull(docs[t].Name) then Log('Docs:'+docs[t].Name);
  except
   on e: exception do  log('EXC2: '+e.Message);
  end;


 finally
  FreeAndNil(fClient);
  log('end');
 end;
end;

with BADsyntax checked output is:
EXC1: Server reset the connection: probably due to a bad formatted BSON request
EXC2: Server reset the connection: probably due to a bad formatted BSON request



...but the second call is correct.
How to reset the error (without having to reconnect) ?

Board footer

Powered by FluxBB