11

I'm using ASP.Net Web API 2 / .Net 4.5.2.

I'm trying to retain the calling principal when queueing a background work item. To that end, I'm trying to:

Thread.CurrentPrincipal = callingPrincipal;

But when I do so, I get an ObjectDisposedException:

System.ObjectDisposedException: Safe handle has been closed

How do I keep the current principal inside the background work item?
Can I make a copy of the principal somehow?

public void Run<T>(Action<T> action)
{
    _logger.Debug("Queueing background work item");
    var callingPrincipal = Thread.CurrentPrincipal;
    HostingEnvironment.QueueBackgroundWorkItem(token =>
    {
        try
        {
            // UNCOMMENT - THROWS EXCEPTION
            // Thread.CurrentPrincipal = callingPrincipal;
            _logger.Debug("Executing queued background work item");
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        catch (Exception ex)
        {
            _logger.Fatal(ex);
        }
        finally
        {
            _logger.Debug("Completed queued background work item");
        }
    });
}
VMAtm
  • 26,645
  • 17
  • 75
  • 107
  • Do you actually need the `Thread.CurrentPrincipal`, or do you really need `HttpContext.User`? Perhaps elaborate on why you need to flow the current principal like that. – Yuval Itzchakov Feb 17 '16 at 15:15
  • What is the reason behind keeping Current Principal in Background Thread? – Win Feb 17 '16 at 15:16
  • For getting some personal data which is very time-consuming, for example. – VMAtm Feb 17 '16 at 15:21
  • 3
    @VMAtm Can you elaborate? What does *getting some personal data* mean? – Yuval Itzchakov Feb 17 '16 at 15:22
  • @YuvalItzchakov I can't say for sure, as I'm not an OP, but, for example, user needs some data got from remote service, which must be accessed by his identity. This should be done as very long operation, but still needs to be done with user context . – VMAtm Feb 17 '16 at 15:24
  • Post the stack trace corresponding to your ObjectDisposedException – Joe Feb 17 '16 at 15:47
  • @win, Why I need to retain the principal shouldn't matter, but to answer your question: the background process may call on other services and I need to have the principal around for those service calls. I also audit changes to entities with the user information. –  Feb 17 '16 at 16:43

2 Answers2

8

Turns out ClaimsPrincipal now has a copy constructor.

var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);

This appears to resolve the issue while retaining all of the identity and claims information. The complete function follows:

public void Run<T>(Action<T> action)
{
    _logger.Debug("Queueing background work item");
    var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);

    HostingEnvironment.QueueBackgroundWorkItem(token =>
    {
        try
        {
            Thread.CurrentPrincipal = principal;
            _logger.Debug("Executing queued background work item");
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        catch (Exception ex)
        {
            _logger.Fatal(ex);
        }
        finally
        {
            _logger.Debug("Completed queued background work item");
        }
    });
}
0

The problem of your situation is that the background task being executed after the Thread.CurrentPrincipal being disposed. This happens because of ASP.NET model - the request is being handled in user context and after that all the values corresponding to the user are freed. So this is exactly happens to your identity. Try to save the information about the user and his identity to impersonate it later.

You can review a support article from Microsoft for impersonating the operations in ASP.NET sites, but I don't think that this will be helpful for you:

System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = 
    ((System.Security.Principal.WindowsIdentity)callingPrincipal.Identity).Impersonate();

//Insert your code that runs under the security context of the authenticating user here.

impersonationContext.Undo();

or, may be, you can use the User.Token, something like this:

HostingEnvironment.QueueBackgroundWorkItem(token =>
{
    try
    {
        _logger.Debug("Executing queued background work item");
        using (HostingEnvironment.Impersonate(callingPrincipal.Identity))
        {
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        // UNCOMMENT - THROWS EXCEPTION
        // Thread.CurrentPrincipal = callingPrincipal;
    }
    catch (Exception ex)
    {
        _logger.Fatal(ex);
    }
    finally
    {
        _logger.Debug("Completed queued background work item");
    }
});

I suggest you to review your architecture design so you can find a way to move out the background operation to other context, in which the user identity would stay longer. Other way, for example, is to use the passing the current OperationContext to the Task:

// store local operation context
var operationContext = OperationContext.Current;
TaskFactory.StartNew(() =>
{
    // initialize the current operation context
    OperationContext.Current = operationContext;
    action();
})
VMAtm
  • 26,645
  • 17
  • 75
  • 107
  • OP is quite different from impersonating. The problem OP having is the main thread is disposed while Background Thread is processing. **It is not a good practice in ASP.Net** which is why Yuval Itzchakov and I asking. Normally, we store the data in persistent storage like database before processing in Background thread. – Win Feb 17 '16 at 15:35
  • I understand the problem and tried to explain how to not to bind to `CurrentThread` for the long operation. – VMAtm Feb 17 '16 at 15:38