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];
Related
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.
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.
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!
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.
We use a websense internet filter at my workplace. I have an application that tries to retrieve information from the internet.
On my client machine, I have to authenticate with websense manually (i.e., open firefox and give my username / password) or I'll get an error in my application when it tries to do the download.
The error message is:
HTTP/1.0 302 Moved.
Does anyone know of a way to authenticate with websense from code? Examples in any language are welcome- I am using Delphi and Indy's TIdHTTP component.
Answering my own question; this is what worked for me.
The custom user agent string is only required if you want the authentication to let MSN / Live messenger get through, as described under "notes" at the end of this article.
In a command line application:
uses
... IdHTTP ...;
...
var
httpGetter: TIdHTTP;
...
httpGetter.Request.Username := username;
httpGetter.Request.Password := password;
httpGetter.HandleRedirects := True;
httpGetter.Request.BasicAuthentication := True;
//custom useragent required to let live messenger work
httpGetter.Request.UserAgent := 'MSN Explorer/9.0 (MSN 8.0; TmstmpExt)';
httpGetter.Get(url,MS);
...
I would try HTTP authentication
http://en.wikipedia.org/wiki/Basic_access_authentication