These forums are read-only and considered to be an archive. Please use the new Community for future interaction and posts.

Upload Via Programmatic Impersonation

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