You are not logged in.
Thanks edwin. I can do a wrapper function and then just use that when calling via ajax and use the real function for delphi clients. However, I suspect this will add a lot of overhead and will need to be done throughout.
Any suggestions what would be the most efficient way of converting the json string to the array?
Unfortunately posting the values causes a 406 Not Acceptable error.
var dataString = {"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]};
jQuery.ajax({
type: "POST",
dataType: "json",
url: MAIN_URL + '/Staff/FilterStaff',
data: dataString,
timeout: 2000
});What would be the correct format to send a named dynamic array to an interface function?
I declared the following as a test:
TFilterType = record
name: string;
value: string;
comparetype: string;
end;
TFilterList = array of TFilterType;
...
function TStaff.FilterStaff(const filters: TFilterList): TFilterList;
begin
Log('Received filters: '+IntToStr(length(filters)) );
setlength(result, 2);
result[0].name := 'name1';
result[0].value := 'value1';
result[0].comparetype := '>';
result[1].name := 'name2';
result[1].value := 'value2';
result[1].comparetype := '=';
end;The function correctly returns
{"result":[[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype"
:"="}]]}But when sending
var dataString = {"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]};
...
http://localhost:8080/root/Staff/FilterStaff?filters%5B0%5D%5Bname%5D=name1&filters%5B0%5D%5Bvalue%5D=value1&filters%5B0%5D%5Bcomparetype%5D=%3E&filters%5B1%5D%5Bname%5D=name2&filters%5B1%5D%5Bvalue%5D=value2&filters%5B1%5D%5Bcomparetype%5D=%3D&session_signature=3eaeb14d00379c791c58bcc3an empty array is received.
I guess it can't be done.
Thanks, what would be the correct way of setting the session timeout?
I am not using orm, just my own services, so will have to set it manually.
The 403 issue is confusing me. Not sure where to place breakpoints anymore. From somewhere, mormot gets an Admin user. When using Admin as username with whatever I set the Result.PasswordPlain in GetUser, it works and allows my function calls. Using any other username, such as that set in Result.LogonName in GetUser, it seems to authenticate ok, but then doesn't allow my function calls. Are there any hardcoded usernames somewhere?
The access violation happens when setting SessionTimeout in the GetUser function. It looks like Result.GroupRights is not accessible when trying to set the value. Nothing really that I can debug at that stage.
function TMyAuthentication.GetUser(Ctxt: TSQLRestServerURIContext; const aUserName: RawUTF8): TSQLAuthUser;
begin
Result := TSQLAuthUser.Create;
Result.IDValue := 10;
Result.LogonName := 'johnny';
Result.DisplayName := Result.LogonName;
Result.PasswordPlain := 'abc';
Result.GroupRights := TSQLAuthGroup(2); // for 'Admin'
Result.GroupRights.SessionTimeout := 1;
end;Perfect, thanks. I now no longer get the 500 error, but noticed something strange. Given the example above, after logging in using any string as a password, the sesion authenticates and I get the message that I successfully logged in. but using any functions returns 403 Forbidden.
However, using abc as a password, I still get the message that I successfully authenticated, but my functions are able to be called. So it looks like, at some stage when checking if a function can be executed, my checkpassword function is ignored and a direct compare is done between the PasswordPlain value set and that sent to log in. So does that mean that I need to also override another function to compute the hash, or should I expect the example above to work as-is?
Does the implimentation you provided above also mean that I can no longer set the session timeout value? Trying to set Result.GroupRights.SessionTimeout results in an access violation.
Hi AB
When using custom authentication, I get 500 Internal Server Error when the authentication succeeds, however when it fails, it works as expected. It looks like it happens at SessionCreate and can be because of groups, but I'm not sure what I do wrong (just tested on latest nightly build):
TMyAuthentication = class(TSQLRestServerAuthenticationDefault)
private
function CheckPassword(Ctxt: TSQLRestServerURIContext; User: TSQLAuthUser; const aClientNonce, aPassWord: RawUTF8): boolean; override;
function GetUser(Ctxt: TSQLRestServerURIContext; const aUserName: RawUTF8): TSQLAuthUser; override;
protected
end;
aRestServer := TSQLRestServerFullMemory.Create(aModel, false); // authentication=false
aRestServer.AuthenticationRegister([TMyAuthentication]);
function TMyAuthentication.CheckPassword(
Ctxt: TSQLRestServerURIContext; User: TSQLAuthUser; const aClientNonce,
aPassWord: RawUTF8): boolean;
begin
result := true; //aPassword = 'abc';
end;
function TMyAuthentication.GetUser(Ctxt: TSQLRestServerURIContext; const aUserName: RawUTF8): TSQLAuthUser;
begin
Result := TSQLAuthUser.Create;
Result.IDValue := 10;
Result.LogonName := 'johnny';
Result.DisplayName := Result.LogonName;
Result.PasswordPlain := 'abc';
Result.GroupRights := TSQLAuthGroup.Create;
Result.GroupRights.SessionTimeout := 20;
Result.GroupRights.AccessRights := 'Admin';
end;Do you have anything to point me in the right direction?
Thanks. As per my test code, I already use TSQLRestServerFullMemory. How does that bring the connection to the interfaces? Is there a property that I need to access in the interface's functions?
I tried creating a class from which all my instances can inherit, but it doesn't look as if the connection is available when used in the TStaff class. Would this be the correct way to access a specific created instance? The Get call currently generates an access violation.
TMyInterface = class(TInterfacedObject)
private
public
DBConnection: TSQLDBConnectionProperties;
end;
TStaff = class(TMyInterface, IStaff)
private
public
function GetNickname(const aUserID: string):string;
function WhoAmI: string;
end;
function TStaff.GetNickname(const aUserID: string): string;
var
Res : ISQLDBRows;
begin
Result := '';
Res := DbConnection.Execute('select nickname from tblstaff where UserID=? ', [aUserID] );
if Res.Step then
Result := Res['nickname'];
end;//This looks like it creates a copy of the interface and does not return the instance in the server,
//so setting properties here leave them nil in the server instance
if aRestServer.Services['Staff'].Get(obj) then
begin
TMyInterface(obj).DBConnection := aConnection;
showmessage(TStaff(obj).GetNickname('0')); //this works, but not when calling from ajax, since dbconnection then is nil
end;
//this does not bring the server instance back either, just give an AV
for I := 0 to aRestServer.Services.Count -1 do
if aRestServer.Services.Index(I).Get(obj) then //Access Violation here
TMyInterface(obj).DBConnection := aConnection
//this works, but requires me to do for every service, no for loop
TMyInterface(aRestServer.Service<IStaff>).DBConnection := aConnection;Now a more basic question remains: how to end a session from ajax? I currently do the auth? call to authenticate, but what do I call to log out and ensure that a session can't be re-used? This always returns 400 Bad Request but does seem to kill the session (all calls after that returns 403 Forbidden):
http://localhost:8080/root/auth?session=184464650&UserName=admin&session_signature=0afeb50a0003f9f9d88a6677Is there a different way of passing the connection on to the TInterfacedObjects?
Yes, squirrels and marmots often share food ![]()
To pass on the connection, I followed something done by a user in one of the forum posts and created this little test:
aConnection := TODBCConnectionProperties.Create('',ConnectionStr,'','');
aRestServer := TSQLRestServerFullMemory.Create(aModel, false);
aRestServer.AuthenticationRegister([TMyAuthentication]);
aRestServer.ServiceDefine(TSales.Create(aConnection),[ISales]);
aRestServer.ServiceDefine(TStaff,[IStaff], sicShared);My auth for this test is also currently as simple as possible:
TMyAuthentication= class(TSQLRestServerAuthenticationDefault)
protected
function CheckPassword(Ctxt: TSQLRestServerURIContext; User: TSQLAuthUser; const aClientNonce, aPassWord: RawUTF8): boolean; override;
end;
function TMyAuthentication.CheckPassword(Ctxt: TSQLRestServerURIContext; User: TSQLAuthUser; const aClientNonce, aPassWord: RawUTF8): boolean;
begin
result := aPassword = 'abc';
end;I then defined a WhoAmI function in Both TSales and TStaff. When calling the Sales.WhoAmI function, it always returns 403 Forbidden, but Staff.WhoAmI always works as expected, depending on whether the authentication passed or failed. The WhoAmI function is also basic and doesn't perform any logic:
function TStaff.WhoAmI: string;
begin
result := 'I am Somebody';
end;Not sure if this is relevant, but I'm testing using Ajax, but will use the exact same function calls from a Delphi client.
Any advice would be super helpful. I'm still finding my feet, but the more I see, the more I like it.
Hi
I've decided to not go the orm route so I can reuse some of our legacy queries. For now, I decided to have a simple scenario where a function call will execute the TSQLDBStatement and return the results as json using FetchAllToJSON.
How does the connection pools work then? Do I need to create a new connection in every function, or should I declare the TSQLDBConnectionProperties.NewConnection globally so that NewConnection.NewStatement can be used in every function? This does not sound thread safe, is there an alternative?
Given the complexities of the existing legacy code, I'll also have to do custom authentication, which will mean that the login query will run before a session is created and the normal queries after a session is created. Will this affect the connection pool, or will this work the same as the normal queries? The TSQLHttpServer is created in the Main thread.
Sorry if I don't express my questions well. If anything is unclear I'll try to restate.
1) For future readers: It seems that the field that replaces ID is not allowed to be part of the TSQLRecord definition. If it is, it correctly creates the table and then try to add it on again after creating the table. After removing the TeamId field from the TSQLRecord class it works as expected.
2) Are there any options for statically linking the 64bit in the exe as well? Do 64bit obj files need to be generated, or is this not possible / available?
Thanks for the quick response.
1) This is a blank database. If I don't call the Mapfield line, the table is successfully created. I drop table manually after every test.
2) Does that mean that I need to distribute an unreleased and unsupported version of SqlLite with all my applications and keep it up to date for something we don't use? I am a bit confused about this. I've searched my hard drive and don't have the 32bit version of SqlLite anywhere on it, yet the sample works. But rebuilding the same code in 64bit gives the error. Is the 64bit dll you distribute altered or different from the standard dll to make it a requirement?
3) I was able to resolve this, thanks
Good day
I am hoping to use the mORMot framework as a middle layer between our legacy databases and external applications (both Delphi and Web). Could you please assist with the following questions?
1) Is it possible to use ORM and have an integer primary key that is called anything other than ID? eg Change the primary key name to TeamID or CustomerID? All tables already have autoincrementing integer primary keys. Edit: I assumed it would be by doing something like this:
aModel.Props[TSQLRecordTblTeams].ExternalDB.MapField('ID','TeamID');but this results in the following error when calling CreateMissingTables:
Error SQLITE_ERROR (1) using 3.8.11.1 - ''TSQLRestStorageExternal.Create: TSQLRecordTblTeams: unable to create external missing field TblTeams.TeamID - SQL="ALTER TABLE TblTeams ADD TeamID bigint"'' extended_errcode=1
2) When building example 28 in 32 bit using a 32bit mysql dsn connection, the example works perfectly. Building the same code in 64bit and using a 64bit dsn connection, I get the error:
No SQLite3 libray available: you shall either add SynSQLite3Static to your project uses clause or run sqlite3 := TSQLite3LibraryDynamic.Create(..).
I do not intend using SqlLite at all, only Mysql and only in 64bit. Is this possible?
3) When trying to use the full connection string instead of a dsn, the error
Data source name not found and no default driver specified (0) is returned.
Is there anything specific that needs to be specified as part of the connection string? Sample code from example 28 altered with mysql connection string:
aProps := TODBCConnectionProperties.Create('','DRIVER=MySQL ODBC 5.3 Unicode Driver;SERVER=localhost;DATABASE=testdb;USER=root;PASSWORD=testpasswd;Option=3;Port=3306','','');Thanks for the excellent examples you distribute with the code. If it wasn't for them using the framework wouldn't have been possible.