Delphi XE2 DataSnap - Access REST connection properties in server methods module - delphi

I'm building an XE2 DataSnap server which will serve connections from REST clients. My DSServerClass LifeCycle property is set to 'Invocation'. The REST connection properties will include username and password, which are handled through the DSAuthenticationManager UserAuthenticate() event. What I need to know is how can I access the username and password within the server methods class? I want to be able to know which REST username/password launched the object instance of my server class.

You can use DSServerClass.OnPrepare for that:
procedure TServerContainerTest.DSServerClass1Prepare(
DSPrepareEventObject: TDSPrepareEventObject);
begin
// Add username property to TServerMethodsTest
if DSPrepareEventObject.MethodInstance is TServerMethodsTest then
TServerMethodsTest(DSPrepareEventObject.MethodInstance).Username := DSPrepareEventObject.UserName;
end;
There's is no password available.
Don't use Server LifeCycle for this!

Related

Is it possible to enumerate all available DDE servers with `TDdeClientConv` class?

I was curious about all the running DDE servers, tried TDdeClientConv class but got confused with it (may be just didn't figured out how) and finally rolled my own low-level (normally applications should use DDEML abstraction layer) "client":
procedure TForm6.FormClick(Sender: TObject);
begin
{ initiate DDE conversation with all top-level windows }
SendMessage(
HWND_BROADCAST,
WM_DDE_INITIATE,
Handle,
MakeLParam(
0, // all services
0 // all topics
)
);
end;
procedure TForm6.WMDDE_Ack(var Message: TWMDDE_Ack);
begin
{ this message handler receives acknowledgements }
{ and prints service-topic pairs to console }
Writeln('"' + GetAtom(Message.App) + '"', #9, '"' + GetAtom(Message.Topic) + '"');
end;
Question: is it possible to do the same with TDdeClientConv class, that is, initiate a DDE conversation with all available services and receive multiple acknowledgements? Or TDdeClientConv merely represents client endpoint of DDE conversation and thus my scenario is out of the scope?
TDdeClientConv does not use any window messages, it uses the Dynamic Data Exchange Management Library (DDEML) instead. TDdeClientConv can connect only to a single server that implements a specified Service and/or Topic, as it establishes its connection using the DDEML DdeConnect() function:
Establishes a conversation with a server application that supports the specified service name and topic name pair. If more than one such server exists, the system selects only one.
DDEML's DdeConnectList() function, on the other hand, can establish a conversation with multiple servers supporting a given Service and/or Topic.
Establishes a conversation with all server applications that support the specified service name and topic name pair. An application can also use this function to obtain a list of conversation handles by passing the function an existing conversation handle. The Dynamic Data Exchange Management Library removes the handles of any terminated conversations from the conversation list. The resulting conversation list contains the handles of all currently established conversations that support the specified service name and topic name.
You can enumerate that list using the DdeQueryNextServer() and DdeQueryConvInfo() functions.

Sharing ADOConnectionString accross multiple applications-Delphi

I am working on Delphi 7, SQL Server 2008 R2 and i am using ADO to connect to database.
My Connection String:
Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TESTDataBase;Data Source=TestDataSource
when i make my TADOconnection.Connected to True Connectin string is changing it to
Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TESTDataBase;Data Source=TestDataSource; Use Procedure for Prepare=1;
Auto Translate=True;Packet Size=4096;Workstation ID=MYMachineName;Use Encryption for Data=False;Tag with column collation when possible=False
There are more than 10 applications and i am planning to use the same connection string in all applications.
so when i connect to FirstApplcation i am creating ADO Connection string, saving it in registry or some where so i can use the same connection string for other applications also.
Same user can run more than one application(it can be 2 or 3 or 5 applications).
Here i have 3 questions
1)when i need to share connection?
i mean create the connection string and share the same Connection string(write to registry or some where)
or
create the connection string , connect to database and share the same connection string(write to registry or some where)
2) In my application when i get the shared connection string do i need to connect to database again(TADOConnection.Connected= true)? i think i should connect it again, since it is different application :)
3) For all my applications i open connection at application(main form) form show and i will be closing connection at application(main form) form close.So connection will get close only when application closes and TADOCOnnection.KeepConnection = True. is this create performance issues or burden on database server?
A shared UDL file can be good for sharing a connection. Use the required properties only for your connection string, i.e:
Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=TESTDataBase;Data Source=TestDataSource
Don't worry about the extra properties that are added by the ADO after you connect. it's the default properties for the specific provider you use (SQLOLEDB.1). The same properties will be set in all your other applications if you use the exact connection string or UDL file (with a common provider).
ADO connections to SQL Server are pooled by default.
You can simply set e.g.
MyConnection.ConnectionString := 'FILE NAME=c:\my_shared_path\shared.udl';
MyConnection.Open;
A good place to hold a shared connection object (TADOConnection) is in a shared Data Module for the entire application.
Open it in the data module or the main form OnCreate event.
The connection is automatically closed once the TADOConnection is destroyed.

Kbmmw. Time out message "Anonymous user not authorized"

I made a new topic about my issue.
KIM told\
Anonymous requests typically means that it does not find a
username/password not a token in the clients request. Remember the
Token you get on your first request should be reused for all
subsequent requests by all client code (all kbmMWSimpleClient,
kbmMWClientQuery, kbmMWClientResolver etc). On way to centralize that
is to put a TkbmMWSimpleClient on the datamodule and specify all the
client query components to use this simple client instance as a
template. Then as the first thing before anything else in your client
application, make an initial "login" request call via the
simpleclient.
I changed ServerSideQueryAllClick on the client app. I copied Token from server side to client Edit1.text.
procedure TForm1.btnNamedServerSideQueryAllClick(Sender: TObject);
begin
// Gets all records from server event table.
If Length(Trim(Edit1.Text)) > 0 then
Begin
kbmMWSimpleClient1.Disconnect;
kbmMWSimpleClient1.Username:= CB1.Text; // Login -> Franz
kbmMWSimpleClient1.Password:= CB2.Text; // Password -> FranzPassword
kbmMWSimpleClient1.Token := Edit1.Text; // Token from server
kbmMWSimpleClient1.Connect;
End;
if qServerSide.Active then qServerSide.Close;
// Use named query.
qServerSide.Query.Text:='#ALL_EVENTS';
qServerSide.Open;
end;
It dowsn't work.
How to make relogin?
The Authorization demo shows how the client has a simpleclient that is used as template for all the client query components (by setting their Client property to point at the simpleclient instance).
When setting the token, you specifically do not want to set the username or password, and similarly if you are setting username and password, do not set the token.
Also make sure that qServerSide.Client points on your simpleclient.
Doing that you generally only need to setup username/password once on the simpleclient before anything is opened, then for example open the query component, after which the simpleclient.token value will automatically have been updated from the server with the assigned login token.

Delphi XE2 DataSnap Server - Log client connection user/properties

I'm building an XE2 DataSnap server which will serve connections from REST clients. My DSServerClass LifeCycle property is set to 'Invocation'. What I want to do is to log the details of all client connections to the server, including the following details : username, IP address, protocol, application name. I can currently get these details using the following events :
DSAuthenticationManager - UserAuthenticate() : username, protocol (using the standard parameters passed in)
DSServer - Connect() : protocol, IP address, application name (using DSConnectEventObject.ChannelInfo.ClientInfo)
What I want to do is just log once for all details, but it seems I can't get all the details I need in one event. I tried using a shared private variable in the class but as expected this gave inconsistent results - the wrong IP address against the wrong username. Is there another way to achieve what I want?
Jonathan
procedure TServerContainer1.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);
begin
Form1.Memo1.Lines.Add(Format('Conn->UserName=%s, Password=%s', [
DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName],
DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password]
]));
// 取 Client 端的IP 和 Port
Form1.Memo1.Lines.Add('IP =' + DSConnectEventObject.ChannelInfo.ClientInfo.IpAddress + ':'
+ DSConnectEventObject.ChannelInfo.ClientInfo.ClientPort);
end;
You can use TDSServer.OnConnect event (which is called after TDSAuthenticationManager.OnUserAuthenticate). There you have access to ChannelInfo.ClientInfo as you've discovered and also ConnectProperties from which you can read property values like this:
Scheme := ConnectProperties.Values[TDBXPropertyNames.DSAuthenticationScheme];
UserName := ConnectProperties.Values[TDBXPropertyNames.DSAuthenticationUser];
Password := ConnectProperties.Values[TDBXPropertyNames.DSAuthenticationPassword];

How can my program log in to a Web site programmatically with Indy?

I am trying to log in to fileserve.com from my Delphi application.
I used the LiveHTTPHeader Firefox addon to see HTTP post data. I found
&autoLogin=on&recaptcha_response_field=&recaptcha_challenge_field=&recaptcha_shortencode_field=&loginFormSubmit=Login
I tried in my application like this:
Str := TStringList.Create;
Str.Add('loginUserName='+edit1.Text);
Str.Add('loginUserPassword='+edit2.Text);
Str.Add('autoLogin=on');
Str.Add('recaptcha_response_field=');
Str.Add('recaptcha_challenge_field=');
Str.Add('recaptcha_shortencode_field=');
Str.Add('loginFormSubmit=Login');
s:= IdHTTP1.Post('http://www.fileserve.com/login.php', Str);
FreeAndNil(str);
s1 := IdHTTP1.Get('http://www.fileserve.com/dashboard.php');
memo1.lines.add(s1);
In my my memo, it's not giving the data after I logged in. It just displays the source of the main site. Why doesn't it recognize that I logged in? (I used a working ID and password while testing.)
I am using Delphi 7 and Indy 9; The IdHTTP.HandleRedirect property is set to true.
The site you're logging in to probably sends a cookie in its response to the login request. You need to remember that cookie and send it back during all subsequent requests. Indy should have some sort of TIdCookieManager object that you can hook up to your TIdHTTP object to make it remember cookies automatically.

Resources