Skip to content

Instantly share code, notes, and snippets.

@BenMakesGames
Last active March 30, 2023 20:29
Show Gist options
  • Save BenMakesGames/870e096be50781d97e50859e1557d739 to your computer and use it in GitHub Desktop.
Save BenMakesGames/870e096be50781d97e50859e1557d739 to your computer and use it in GitHub Desktop.
using System.DirectoryServices.AccountManagement;
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
namespace YourNamespaceHere;
// Notes:
// * This class is a service. Hopefully you've got a DI/IoC container you can register it with. (For a desktop application,
// it'd probably make the most sense as a singleton.)
// * This code assumes nullable reference types are enabled, because enabling nullable reference types is the correct way
// to live.
// * Buy Me a Coffee? :) https://ko-fi.com/A0A12KQ16
public sealed class WindowsImpersonationHelper: IDisposable
{
private SafeAccessTokenHandle? ImpersonationToken { get; set; }
private const int InteractiveLogonType = 2;
/// <summary>
/// Once called, any code that calls WindowsImpersonationHelper.RunImpersonated will execute as the impersonated
/// user. Call WindowsImpersonationHelper.StopImpersonation to forget the user.
/// </summary>
/// <returns>True if login succeeded; false, otherwise.</returns>
public bool Impersonate(string? domain, string username, string password)
{
StopImpersonation();
// calls LogonUser, from advapi32.dll (DllImport below)
var success = LogonUser(username, domain, password, InteractiveLogonType, default, out SafeAccessTokenHandle token);
if (!success)
return false;
ImpersonationToken = token;
return true;
}
/// <summary>
/// Stops impersonation.
/// </summary>
public void StopImpersonation()
{
ImpersonationToken?.Dispose();
ImpersonationToken = null;
}
/// <summary>
/// Run `callback` as the impersonated user.
/// </summary>
/// <param name="callback"></param>
public void RunImpersonated(Action callback)
{
// TODO: if your application demands it, you might throw an exception here if ImpersonationToken is null
if (ImpersonationToken == null)
callback.Invoke();
else
WindowsIdentity.RunImpersonated(ImpersonationToken, callback);
}
/// <summary>
/// Get the impersonated user's identity.
/// </summary>
/// <returns></returns>
public UserPrincipal? GetImpersonatedUser()
{
// TODO: this method depends on System.DirectoryServices.AccountManagement. if you don't need this method,
// and/or don't want that dependency, just delete this method
UserPrincipal? identity = null;
RunImpersonated(() =>
{
try
{
identity = UserPrincipal.Current;
}
catch (NoMatchingPrincipalException)
{
// identity is left null
}
});
return identity;
}
public void Dispose()
{
ImpersonationToken?.Dispose();
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(string username, string? domain, string password, int logonType, int logonProvider, out SafeAccessTokenHandle tokenHandle);
}
// example usage:
public sealed class SomeDbFactoryServiceOrWhatever
{
private WindowsImpersonationHelper Impersonator { get; }
public SomeDbFactoryServiceOrWhatever(WindowsImpersonationHelper impersonator)
{
Impersonator = impersonator;
}
public MyDbContext CreateDbContext(DbContextOptions<MyDbContext> options)
{
MyDbContext? db = null;
Impersonator.RunImpersonated(() =>
{
db = new FmsDbContext(options);
db.Database.OpenConnection();
});
return db!;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment