Hi! I'm still having a problem getting the Upload feature to work via programmatic impersonation.
I'm impersonating in the global.asx event 'Application_PreRequestHandler' (Application_BeginRequest doesn't allow access to Session variables),
and I'm checking for Request.Paths of 'FileVista', 'Download' and 'Upload'.
The download now works properly, but the upload still gives me an 'Access Denied' error... My guess is the upload component is still running as NETWORK_SERVICE, and I'm missing something when it comes to impersonation.
If I impersonate directly via the Web.Config, the upload works great...
My Web.Config specifies 'Windows' authentication.
We are using SSL, and an upload method of 'Browser' (I know that Flash has some authentication issues when it comes to Windows authentication).
I am currently using a UNC path to the destination folder.
Thanks for your help - it's a terrific product otherwise (just a few little things to figure out).
Tim
Tim Pollard
2/20/2009 9:19 AM
Hi Tim,
HttpModules are invoked before
global.asax so the upload module does its work before reaching your impersonation code.
So you need to write a simple HttpModule with OnPreRequestHandlerExecute event and place your impersonation code here.
Then in your web.config, you should put your module in <modules> section before GleamTech.Web.FileTransfer.UploadHttpModule so that it's executed before the upload module.
Cem Alacayir
3/10/2009 11:10 PM
I am having a similar problem. I have a HttpModule that executes an impersonation routine on the PreRequestHandlerExecute event. However, the only way I get this to work is to hard code the username and password into the impersonation routine. If I pass them as variables it doesn't work. Is there another way of doing impersonation based on the logged on user? If you would like to see my code, let me know and I will post a sample of it.
We would like to use this product to give users access to their own network drive space when they are away from our network.
Ben
4/24/2009 8:27 AM
Yes, if you post your code sample, it will be easier to find the problem.
Cem Alacayir
4/28/2009 3:45 PM
Here is my httpmodule that I have running before GleamTech.Web.FileTransfer.UploadHttpModule.
Imports System
Imports System.Web
Imports System.Web.Security
Imports System.Security.Principal
Imports System.Web.SessionState
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic
Imports System.IO
Imports System.Configuration.ConfigurationManager
Public Class FixPermissions
Implements IHttpModule
Dim LOGON32_LOGON_INTERACTIVE As Integer = 2
Dim LOGON32_PROVIDER_DEFAULT As Integer = 0
Dim impersonationContext As WindowsImpersonationContext
Declare Function LogonUserA 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 DuplicateToken Lib "advapi32.dll" ( _
ByVal ExistingTokenHandle As IntPtr, _
ByVal ImpersonationLevel As Integer, _
ByRef DuplicateTokenHandle As IntPtr) As Integer
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long
' In the Init function, register for HttpApplication
' events by adding your handlers.
Public Sub Init(ByVal application As HttpApplication) Implements IHttpModule.Init
AddHandler application.PostAcquireRequestState, AddressOf Me.Application_PostAcquireRequestState
AddHandler application.PostMapRequestHandler, AddressOf Me.Application_PostMapRequestHandler
AddHandler application.PreRequestHandlerExecute, AddressOf Me.LoadUser
End Sub
Public Sub Application_PostMapRequestHandler(ByVal source As Object, ByVal e As EventArgs)
Dim app As HttpApplication = CType(source, HttpApplication)
If TypeOf app.Context.Handler Is IReadOnlySessionState Or TypeOf app.Context.Handler Is IRequiresSessionState Then
'no need to replace current handler
Else
'swap current handler
app.Context.Handler = New MyHttpHandler(app.Context.Handler)
End If
'app.Response.Cookies.Add(New HttpCookie("testLogin", "works here 1"))
LoadUser(source, e)
End Sub
Public Sub Application_PostAcquireRequestState(ByVal source As Object, ByVal e As EventArgs)
Dim app As HttpApplication = CType(source, HttpApplication)
Dim resourceHttpHandler As New MyHttpHandler(HttpContext.Current.Handler) ' = CType(HttpContext.Current.Handler, MyHttpHandler)
Dim context As HttpContext = app.Context
If Not (resourceHttpHandler Is Nothing) Then
'set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler
End If
'session state should be availalbe now
'Debug.Assert(Not (app.Session Is Nothing), "it did not work")
'context.Response.Write("<h1>This is a test </h1>")
End Sub
Public Sub LoadUser(ByVal source As Object, ByVal e As EventArgs)
Dim app As HttpApplication = CType(source, HttpApplication)
Dim context As HttpContext = app.Context
'Dim session As HttpSessionState = context.Session
Dim cookie As HttpCookie
Dim sr As StreamReader
Dim sID As String
Dim TDES As cTripleDES
Dim mKEY(23) As Byte
Dim mIV(7) As Byte
Dim sKeys() As String
Dim sVectors() As String
Dim sAuth() As String
Dim temp As String
'cookie = New HttpCookie("testLogin", "Works here 2 ")
'cookie.Expires = DateTime.Now.AddMinutes("5")
'app.Response.Cookies.Add(cookie)
' If File.Exists(app.Server.MapPath(Path.Combine("junk", sID & ".txt"))) Then
'read and decrypt file
'sKeys = Split(AppSettings("encKey").ToString, ",")
'For i As Integer = 0 To 23
' mKEY(i) = sKeys(i)
'Next
'sKeys = Nothing
'sVectors = Split(AppSettings("vector").ToString, ",")
'For i As Integer = 0 To 7
' mIV(i) = sVectors(i)
'Next
'sVectors = Nothing
'TDES = New cTripleDES(mKEY, mIV)
'sr = New StreamReader(app.Server.MapPath(Path.Combine("junk", sID & ".txt")))
'temp = sr.ReadToEnd
'If Not (app.Response.Cookies("username").Value Is Nothing) And Not (app.Response.Cookies("password").Value Is Nothing) Then
If Not String.IsNullOrEmpty(app.Context.Request.QueryString("id")) Then
sID = app.Context.Request.QueryString("id")
sAuth = Split(sID, "||")
'sr.Close()
'file decrypted, delete file
'File.Delete(app.Server.MapPath(Path.Combine("junk", sID & ".txt")))
If impersonateValidUser(sAuth(0), "RWC_NET", sAuth(1)) Then
'Insert your code that runs under the security context of a specific user here.
'undoImpersonation()
'context.Response.Cookies.Remove("username")
'context.Response.Cookies.Remove("password")
cookie = New HttpCookie("testLogin", "Works here 2 || " & app.Context.Request.QueryString("id"))
cookie.Expires = DateTime.Now.AddMinutes("5")
app.Response.Cookies.Add(cookie)
'context.Response.Cookies.Add(cookie)
Else
cookie = New HttpCookie("testLogin", "works here 5 ~" & sAuth(0) & "||" & sAuth(1))
cookie.Expires = DateTime.Now.AddMinutes("5")
app.Response.Cookies.Add(cookie)
'AccessDenied(app)
'Your impersonation failed. Therefore, include a fail-safe mechanism here.
'cookie = New HttpCookie("testLogin", "failed")
'cookie.Expires = DateTime.Now.AddMinutes("5")
'app.Response.Cookies.Add(cookie)
'context.Response.Cookies.Add(cookie)
End If
' Else
''guid file doesn't exist, redirect back to login or access denied
'AccessDenied(app)
' End If
End If
End Sub
Public Sub Dispose() Implements IHttpModule.Dispose
End Sub
Public Function impersonateValidUser(ByVal userName As String, ByVal domain As String, ByVal password As String) As Boolean
Dim tempWindowsIdentity As WindowsIdentity
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
impersonateValidUser = False
If RevertToSelf() Then
If LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
impersonationContext = tempWindowsIdentity.Impersonate()
If Not impersonationContext Is Nothing Then
impersonateValidUser = True
End If
End If
End If
End If
If Not tokenDuplicate.Equals(IntPtr.Zero) Then
CloseHandle(tokenDuplicate)
End If
If Not token.Equals(IntPtr.Zero) Then
CloseHandle(token)
End If
End Function
Private Sub undoImpersonation()
impersonationContext.Undo()
End Sub
Private Sub AccessDenied(ByVal app As HttpApplication)
'http://blogs.x2line.com/al/articles/146.aspx
app.Response.StatusCode = 401
app.Response.StatusDescription = "Access Denied"
app.Response.Write("<html><head></head><body>401 Access Denied <br /> <br /> <a href=""default.aspx"">click here to login</a></body></html>")
app.CompleteRequest()
End Sub
Public Class MyHttpHandler
'Implements IRequiresSessionState
Implements IHttpHandler
Friend ReadOnly OriginalHandler As IHttpHandler
Public Sub New(ByVal origHandler As IHttpHandler)
Me.OriginalHandler = origHandler
End Sub
Public Sub ProcessRequest(ByVal context As HttpContext) Implements System.Web.IHttpHandler.ProcessRequest
Throw New InvalidOperationException("MyHttpHandler cannot process requests.")
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
End Class
The only way that I have gotten this work is if in the sub "LoadUser" I hard code the username and password. For example: sID = "username||password". When I do that it works. If I try reading from a file, reading a cookie, or getting the posted value it doesn't work. However, I find it funny that in the case of reading from a file or posted value I can create a cookie called "testLogin", set its value to the passed in username and password, write it out on the receiving asp.net page and see that it has the correct values.
Ben
4/29/2009 5:54 AM
Hi Cem,
We have figured out our problem of impersonating. The solution was to grab the logged on users identity and use the impersonate function on it.
Public Sub LoadUser(ByVal source As Object, ByVal e As EventArgs)
Dim ctx As HttpContext = HttpContext.Current
Dim tempWindowsIdentity As WindowsIdentity
tempWindowsIdentity = CType(ctx.User.Identity, WindowsIdentity)
impersonationContext = tempWindowsIdentity.Impersonate()
End Sub
Ben
5/20/2009 1:10 PM
Say Ben,
We are trying to do the same thing with FileVistaControl... that is, use it to provide access to user's home directories. Unfortunately, we're not programmers... would you mind sharing your code with us?
You can contact me directly at nathan.epp@usask.ca
Thanks.
Nathan
4/15/2010 10:52 AM
Hi Nathan,
We found that there were still some problems doing it that way and ended up giving Network Services Read, Write, and Modify permissions to the level above where the user normally would go (ie. permissions given to "userfolders" \\servername\userfolders\username). Next we use active directory to authenticate the user. Once they have authenticated I grab the user's logged in name (username) and use that to create the root directory. Doing it this way even though we have technically given them access above their root folder they don't actually have a way of getting there. Here is a sample of the code I use:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim fvc As FileVistaControl = CType(LoadControl("~/FileVistaControl/filevista.ascx"), FileVistaControl)
Dim rootFolder As FileVistaRootFolder
Dim myLoginMgr As New its.LoginManagement 'custom dll that contains some login functions
Dim myUsername As String = myLoginMgr.FormattedUserName(Request.ServerVariables("LOGON_USER"))
Dim folderPath As String
If Not IsPostBack Then
folderPath = "\\servername\userfolders\" & myUsername
If System.IO.Directory.Exists(folderPath) Then
fvc.Style = "width: 600px; height: 600px;"
fvc.Language = "en"
fvc.LicenseKey = "Your Key Here"
fvc.UploadMethod = UploadMethod.Browser
rootFolder = New FileVistaRootFolder("H Drive - " & myUsername, folderPath)
rootFolder.Permissions = FileVistaPermissions.Full
fvc.RootFolders.Add(rootFolder)
'this is a PlaceHolder control on the .aspx side
phFileVista.Controls.Add(fvc)
Else
'label on the .aspx side to display messages to the user
lblMessage.Text = "There was a problem loading your H Drive. Please contact ITS at 594-6898."
End If
End If
End Sub
Ben
4/16/2010 5:37 AM
I thought I should update this to say that I was able to get this working without any significant programming. In order to get the FileVistaControl to work with a UNC path such that each user that logs on to the FileVistaControl front end can only access the folders to which he/she has permission, here is what we did:
- in Active Directory, configure the web front end computer object to have delegate access for the cifs service to the file servers you wish them to access
- configure the web front end to run FileVistaControl using Windows 2008 Server with IIS 7
- configure the application to use basic authentication
- configure the application to use ASP.NET Impersonation for the Authenticated user
- configure applications with these same settings for the 'App_data' and 'FileVista' subfolders
Nathan.
Nathan
5/6/2010 9:10 AM
Thanks Nathan! That is exactly what we were looking for. Much simpler!
Ben
4/28/2011 7:39 AM