We have a class library of some common methods we use throughout multiple web apps and we are having an issue with our Impersonation
class. When using impersonation with LogonUser in each individual application, Environment.UserName
returns the user who is using the application. But when we call it within our class library's impersonation using block, it returns as the AppPool identity.
Returns username of client:
Declare Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Integer
If (LogonUser(Config.AdminUser, Config.AdminDomain, Config.AdminPassword, 9, 0, token) <> 0) Then
Dim newIdentity As WindowsIdentity = New WindowsIdentity(token)
Using impersonatedUser As WindowsImpersonationContext = newIdentity.Impersonate()
name = Environment.UserName
End Using
End If
Returns app pool username:
Imports Class.Library
Using admin As New Impersonation
name = Environment.UserName
End Using
HttpContext.Current.User.Identity.Name
seems to return the username we are looking for but we cannot see why Environment.UserName
works effectively when on the server, but only when it doesn't use our custom class library's dll reference. Here is a look at our impersonation class:
Public Class Impersonation : Implements IDisposable
Private impersonationContext As WindowsImpersonationContext = Nothing
Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Integer
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Boolean
Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean
Declare Auto Function DuplicateToken Lib "advapi32.dll" (ByVal hToken As IntPtr, ByVal impersonationLevel As Integer, ByRef hNewToken As IntPtr) As Integer
Private Shared newIdentity As WindowsIdentity
Private Shared token As New IntPtr(0)
Public Sub New(Optional ByVal userName As String = "", Optional ByVal password As String = "", Optional ByVal domainName As String = Config.AdminDomain)
If userName = "" Then
userName = Config.AdminUser
End If
If password = "" Then
password = Config.AdminPassword
End If
Dim logonType As Integer = 9
impersonationContext = ImpersonateUser(userName, domainName, password, logonType)
End Sub
Private Sub Undo()
If impersonationContext IsNot Nothing Then
impersonationContext.Undo()
End If
End Sub
Private Shared Function ImpersonateUser(ByVal userName As String, ByVal domain As String, ByVal password As String, ByVal logonType As Integer) As WindowsImpersonationContext
Dim res As WindowsImpersonationContext = Nothing
Dim tempWindowsIdentity As WindowsIdentity = Nothing
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
Try
If (RevertToSelf()) Then
If LogonUser(userName, domain, password, logonType, 0, token) Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
Return tempWindowsIdentity.Impersonate()
Else
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Else
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Else
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Finally
If token <> IntPtr.Zero Then
CloseHandle(token)
End If
If tokenDuplicate <> IntPtr.Zero Then
CloseHandle(tokenDuplicate)
End If
End Try
Return res
End Function
Private disposedValue As Boolean = False
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
Undo()
End If
Me.disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
I have a feeling that it has something to do with the dll file being on the server and when we use the using block, Environment.UserName
gets the username from where the process is running on the server. But I can't see why it would work initially when creating a New WindowsIdentity()
within the method that uses the impersonation not when we reference it from our dll since they are both running on the server.
Essentially, HttpContext.Current.User.Identity.Name
has become an issue for us when we are running things locally and trying to debug issues that arise. We would like, 1. an answer to why this is happening for knowledge purposes and 2. a possible resolution if their even is any beyond HttpContext.Current.User.Identity.Name
. (answers in VB or C# are welcomed.)
I resolved my issue by creating a new method in my class library:
Public Shared Function Current() As String
If Not HttpContext.Current.Request.IsLocal Then
Dim id As String = HttpContext.Current.User.Identity.Name
Return id.Substring(4)
Else
Return Environment.UserName
End If
End Function
If the code is run on the server, it uses HttpContext
to pull the domain\username. Then I substringed out the domain (our domain is only three chars).
If it is run locally, I return Environment.UserName
I call this string within my web app by simply referencing the class and method (Employees.Current
)