Skip to content

Instantly share code, notes, and snippets.

@lakeman
Created November 24, 2017 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lakeman/26af3a6d047d3de84c710711781ea709 to your computer and use it in GitHub Desktop.
Save lakeman/26af3a6d047d3de84c710711781ea709 to your computer and use it in GitHub Desktop.
Win32 Impersonate Middleware
public class Impersonate
{
private readonly RequestDelegate next;
private ILogger<Impersonate> logger;
private readonly IServiceProvider _provider;
public Impersonate(ILogger<Impersonate> logger, RequestDelegate next, IServiceProvider provider) {
this.next = next;
this.logger = logger;
this._provider = provider;
}
private static AsyncLocal<WindowsIdentity> currentToken = new AsyncLocal<WindowsIdentity>(TokenChanged);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool ImpersonateLoggedOnUser(SafeAccessTokenHandle userToken);
[DllImport("advapi32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern bool RevertToSelf();
private static void TokenChanged(AsyncLocalValueChangedArgs<WindowsIdentity> args)
{
if (args.CurrentValue == args.PreviousValue)
return;
if (args.PreviousValue != null)
{
if (!RevertToSelf())
throw new Win32Exception();
}
if (args.CurrentValue != null)
{
// A call occurs *after* setting the current token to null, with a closed but not invalid token.
if (!args.CurrentValue.AccessToken.IsClosed && !args.CurrentValue.AccessToken.IsInvalid)
{
if (!ImpersonateLoggedOnUser(args.CurrentValue.AccessToken))
throw new Win32Exception();
}
}
}
public async Task Invoke(HttpContext context) {
var winIdent = context.User.Identity as WindowsIdentity;
if (winIdent == null)
{
logger.LogDebug($"Ignoring Impersonation, no windows identity");
await next.Invoke(context);
}else {
logger.LogDebug($"Impersonating {winIdent.Name}");
currentToken.Value = winIdent;
try
{
using (var scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
await next.Invoke(context);
}
}
catch
{
currentToken.Value = null;
logger.LogDebug($"Impersonation complete");
throw;
}
currentToken.Value = null;
logger.LogDebug($"Impersonation complete");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment