Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set CurrentPrincipal in Winforms for all threads

In a .NET 3.5 Winforms app, after a user provides a username and password, I set a custom principal in the CurrentPrincipal property like this:

My.User.CurrentPrincipal = Service.GetPrincipal(username)

This is done in a method that is called using Invoke, because the originating thread is not the UI thread:

Invoke(New Action(AddressOf doLogin))

But when I click on a button in the Winforms application, the CurrentPrincipal property has been reverted to its default, the current Windows user.

Dim lPrincipal = My.User.CurrentPrincipal ' not my custom principal

Apparently, using Invoke while setting the principal does not solve the problem. Is there another way to set the CurrentPrincipal property for all threads in the application?

Sourcecode to reproduce the problem:

Imports System.Security.Principal
Imports System.Threading

Public Class Form1

  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim lThread As New Thread(AddressOf doLogin)
    lThread.Start()
  End Sub

  Private Sub doLogin()
     Invoke(New Action(AddressOf setPrincipal))
  End Sub

  Private Sub setPrincipal()
     My.User.CurrentPrincipal = New CustomPrincipal
     MsgBox(My.User.CurrentPrincipal.Identity.AuthenticationType) ' Custom
  End Sub

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    MsgBox(My.User.CurrentPrincipal.Identity.AuthenticationType) ' Default
  End Sub
End Class

Public Class CustomPrincipal
  Implements IPrincipal

  Public ReadOnly Property Identity() As IIdentity Implements IPrincipal.Identity
    Get
      Return New CustomIdentity()
    End Get
  End Property

  Public Function IsInRole(ByVal role As String) As Boolean Implements IPrincipal.IsInRole
     Return True
  End Function
End Class

Public Class CustomIdentity
  Implements IIdentity

  Public ReadOnly Property AuthenticationType() As String Implements IIdentity.AuthenticationType
    Get
      Return "Custom"
    End Get
  End Property

  Public ReadOnly Property IsAuthenticated() As Boolean Implements IIdentity.IsAuthenticated
    Get
      Return True
    End Get
  End Property

  Public ReadOnly Property Name() As String Implements IIdentity.Name
    Get
      Return "CustomName"
    End Get
  End Property
End Class
like image 550
Jan Willem B Avatar asked Jan 04 '11 10:01

Jan Willem B


2 Answers

Instead of Thread.CurrentPrincipal (My.User.CurrentPrincipal), use AppDomain.SetThreadPrincipal:

AppDomain.CurrentDomain.SetThreadPrincipal(principal)
like image 83
Kent Boogaart Avatar answered Nov 14 '22 04:11

Kent Boogaart


You are doing battle with something that's called 'call context' in the .NET framework. I need to wave my hands a bit at it because I don't understand it completely. The basic premise is that the current principal is a Big Deal in .NET security. It makes sandboxing work, helping to isolate code so it cannot do anything unsafe. Code running in a browser, a phone, a plug-in, that sort of thing.

The current principal is associated with a thread, Thread.CurrentPrincipal property. That makes Control.Begin/Invoke() tricky, some sort of plugin could hijack the rights of the program's main thread by using it to run code on that thread. The call context is a counter-measure against this. Your code is updating the principal of the call context, not the thread. After the invoked call is complete that falls in the bit bucket, it doesn't flow back to the originating thread in the case of Control.Invoke().

Finding a workaround for this boggles me right now. It really has to be code that originates from your main thread that sets the property, nothing that gets invoked. I can only think of a silly fix, using a one millisecond Timer:

Private Sub setPrincipal()
    Timer1.Enabled = True
End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    Timer1.Enabled = False
    My.User.CurrentPrincipal = New CustomPrincipal
    MsgBox(My.User.CurrentPrincipal.Identity.AuthenticationType) ' Custom
End Sub

It does work. Beware the possible race.

like image 7
Hans Passant Avatar answered Nov 14 '22 06:11

Hans Passant