views:

717

answers:

3

I need to get the current logged on username? I need this to work properly when I call the code from ASP.NET which is working in Windows Authentication mode. i.e. I do not want to get the ASPNET user in that circumstance, but the impersonated user. This is related to my earlier question. Everything I try returns ASPNET.

+2  A: 

In your other question you wrote that you configured ASP.NET to use Windows authentication with impersonation:

 <system.web>
    ...
    <authentication mode="Windows"/>
    <identity impersonate="true"/>
    ...
 </system.web>

Does the ASP.NET application show the correct credentials (user and domain)?

Are you invoking the Delphi function using the correct Identity context, like

WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity;
try
{
    ctx = winId.Impersonate();
    // call Delphi function, passing the identity context
}
catch
{
}
finally
{
    if (ctx != null)
        ctx.Undo();
}

Update:

If the COM abject is called from the code behind for a web form page, you can try to set ASPCOMPAT property of the web form page to true.

See:

The "identity" tag makes sure that the thread executing the request (the MTA thread) will impersonate its security context to the user specified in the tag but our STA COM object eventually was created on the default STA thread which was not impersonate, causing it to get the security context of the process (which was IUSR_XXX – the least powerful user of all).

mjustin
The asp.net application returns the impersonated credentials. So I know that is setup correctly.
Steve
I'm not sure what my Delphi application can or would do with the identity context once it get's it. Remember Delphi is Win32
Steve
How do you call the Delphi application from ASP.NET? Can you show some code?
mjustin
The Delphi application is a simple COM object. I create a reference to it, then create an instance of the object, and call the method. But I can only pass standard COM types as parameters unless I get into custom marshaling.
Steve
If the COM abject is called from the code behind for a web form page, you can try to set ASPCOMPAT property of the web form page to true. See new links in my answer.
mjustin
That worked, although our com object is an MTA COM object and not an STA com object, but obviously something else must be going on. We'll see how that goes. thanks
Steve
It was in fact an STA COM object. So it all makes sense, and the answer is perfect. Thanks
Steve
+1  A: 

Perhaps your IADsWinNTSystemInfo approach (from the linked previous question) returns current process' account information, but ASP.NET is impersonating on a thread level?

Try this:

type
  PTokenUser = ^TTokenUser;
  TTokenUser = packed record
    User: SID_AND_ATTRIBUTES;
  end;

function GetCurrentUserName(out DomainName, UserName: string): Boolean;
var
  Token: THandle;
  InfoSize, UserNameSize, DomainNameSize: Cardinal;
  User: PTokenUser;
  Use: SID_NAME_USE;
  _DomainName, _UserName: array[0..255] of Char;
begin
  Result := False;
  DomainName := '';
  UserName := '';

  Token := 0;
  if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token) then
  begin
    if GetLastError = ERROR_NO_TOKEN then // current thread is not impersonating, try process token
    begin
      if not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token) then
        Exit;
    end
    else
      Exit;
  end;
  try
    GetTokenInformation(Token, TokenUser, nil, 0, InfoSize);
    User := AllocMem(InfoSize * 2);
    try
      if GetTokenInformation(Token, TokenUser, User, InfoSize * 2, InfoSize) then
      begin
        DomainNameSize := SizeOf(_DomainName);
        UserNameSize := SizeOf(_UserName);

        Result := LookupAccountSid(nil, User^.User.Sid, _UserName, UserNameSize, _DomainName, DomainNameSize, Use);

        if Result then
        begin
          SetString(DomainName, _DomainName, StrLen(_DomainName));
          SetString(UserName, _UserName, StrLen(_UserName));
        end;
      end;
    finally
      FreeMem(User);
    end;
  finally
    CloseHandle(Token);
  end;
end;

Example usage:

var
  DomainName, UserName: string;
begin
  if not GetCurrentUserName(DomainName, UserName) then
    RaiseLastOSError;
  Writeln(Format('%s\%s', [DomainName, UserName]));
end;

Hope this helps.

TOndrej
I have tried that as well. Same problem.
Steve
Yes, because the code doesn't seem to work. ;-) I'm trying to find out why.
TOndrej
A: 

This is a part of the code of my LoadProfile tool, it works well in Delphi 2010:

const
  UNLEN = 256; // Maximum user name length

var
  TokenHandle: THandle; // Handle to the Processes' Acces Token
  cbTokenInfo: DWORD; // Size of TokenInfo in Bytes
  pTokenUser: PTOKEN_USER; // Pointer to a TOKEN_USER record

  cchName: DWORD; // Count of characters (length) of the Username array
  cchDomain: DWORD; // Count of characters (length) of the Domainname array
  peUse: DWORD; // Account type for LookupAccountSid

  UserName: array[0..UNLEN] of Char; // Holds the Username
  DomainName: array[0..UNLEN] of Char; // Holds the Domainname
  ComputerName: array[0..UNLEN] of Char; // Hold the Computername


    // Open the Current Process' Token
    OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY or
      TOKEN_IMPERSONATE or TOKEN_DUPLICATE, TokenHandle);

    // Check if we have a valid handle
    if TokenHandle = 0 then
      Exit; 

    { We will use GetTokenInformation to get the user's SID, the first call
      to GetTokenInformation is used to determine how much memory we need to
      allocate }
    GetTokenInformation(TokenHandle, TokenUser, nil, 0, cbTokenInfo);
    // as documented the call should fail with ERROR_INSUFFICIENT_BUFFER
    if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
      Exit;

    // Allocate Memory
    pTokenUser :=  HeapAlloc(GetProcessHeap(), 0, cbTokenInfo);
    if (pTokenUser = nil) then
       Exit;

    // Retrieve the user information from the token.
    if ( not GetTokenInformation(TokenHandle, TokenUser, pTokenUser,
      cbTokenInfo, cbTokenInfo)) then
       Exit;

    cchName := Length(UserName);
    cchDomain := Length(DomainName);
    peUse:= SidTypeUser;


    // Use the SID to find User and Domain Name
    Write('LookupAccountSid... ');
    if not LookupAccountSid(nil, pTokenUser^.User.Sid, UserName, cchName,
      DomainName, cchDomain, peUse) then
      Exit;

    // Cleanup
    if (pTokenUser <> nil) then
      HeapFree(GetProcessHeap(), 0, pTokenUser);

    WriteLn('CloseHandle... OK');
    CloseHandle(TokenHandle);
Remko
I added this as a replacement for TOndrej's code as he's right that impersonation is only for the current thread.
Remko