Added WindowsLoggedInIdentity to capture LogonUser typical work. Changed WindowsImpersonatedIdentity to derive from this new class.

pull/60/head
David Hall 2019-03-09 21:00:58 -07:00
parent 3877cc58bc
commit 3e23723019
2 changed files with 111 additions and 58 deletions

View File

@ -1,99 +1,65 @@
#if (NET20 || NET35 || NET40 || NET45) #if NET20 || NET35 || NET40 || NET45
using System;
using System.ComponentModel;
using System.Security.Principal; using System.Security.Principal;
using static Vanara.PInvoke.AdvApi32; using static Vanara.PInvoke.AdvApi32;
namespace Vanara.Security.Principal namespace Vanara.Security.Principal
{ {
/// <summary> /// <summary>
/// Impersonation of a user. Allows to execute code under another user context. Please note that the account that instantiates this class needs to have the 'Act as part of operating system' privilege set. /// Impersonation of a user. Allows to execute code under another user context. Please note that the account that instantiates this class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <code> /// <code>
/// // The following code impersonates an account to perform work /// // The following code impersonates an account to perform work
/// using (new WindowsImpersonatedIdentity("bob", "WORKDOMAIN", "bobs_secret_passw0rd") /// using (new WindowsImpersonatedIdentity("bob", "WORKDOMAIN", "bobs_secret_passw0rd")
/// { /// {
/// // Perform impersonated work in the body. Once the 'using' statement closes, /// // Perform impersonated work in the body. Once the 'using' statement closes,
/// // the impersonation ends. /// // the impersonation ends.
/// } /// }
/// </code> /// </code>
/// </remarks> /// </remarks>
public class WindowsImpersonatedIdentity : IDisposable, IIdentity public class WindowsImpersonatedIdentity : WindowsLoggedInIdentity
{ {
private readonly WindowsImpersonationContext impersonationContext; private readonly WindowsImpersonationContext impersonationContext;
private WindowsIdentity identity;
private readonly bool ownId = true;
/// <summary> /// <summary>
/// Starts the impersonation with the given credentials. Please note that the account that instantiates this class needs to have the 'Act as part of /// Starts the impersonation with the given credentials. Please note that the account that instantiates this class needs to have the
/// operating system' privilege set. /// 'Act as part of operating system' privilege set.
/// </summary> /// </summary>
/// <param name="userName"> /// <param name="userName">
/// A string that specifies the name of the user. This is the name of the user account to log on to. If you use the user principal name (UPN) format, /// A string that specifies the name of the user. This is the name of the user account to log on to. If you use the user principal
/// User@DNSDomainName, the <paramref name="domainName"/> parameter must be NULL. /// name (UPN) format, User@DNSDomainName, the <paramref name="domainName"/> parameter must be NULL.
/// </param> /// </param>
/// <param name="domainName"> /// <param name="domainName">
/// A string that specifies the name of the domain or server whose account database contains the <paramref name="userName"/> account. If this parameter /// A string that specifies the name of the domain or server whose account database contains the <paramref name="userName"/> account.
/// is NULL, the user name must be specified in UPN format. If this parameter is ".", the account is validated by using only the local account database. /// If this parameter is NULL, the user name must be specified in UPN format. If this parameter is ".", the account is validated by
/// using only the local account database.
/// </param> /// </param>
/// <param name="password">A string that specifies the plaintext password for the user account specified by <paramref name="userName"/>.</param> /// <param name="password">A string that specifies the plain-text password for the user account specified by <paramref name="userName"/>.</param>
/// <param name="logonType"> /// <param name="logonType">
/// Type of the logon. This parameter can usually be left as the default. For more information, lookup more detail for the dwLogonType parameter of the /// Type of the logon. This parameter can usually be left as the default. For more information, lookup more detail for the
/// Windows LogonUser function. /// dwLogonType parameter of the Windows LogonUser function.
/// </param> /// </param>
/// <param name="provider"> /// <param name="provider">
/// The logon provider. This parameter can usually be left as the default. For more information, lookup more detail for the dwLogonProvider parameter of /// The logon provider. This parameter can usually be left as the default. For more information, lookup more detail for the
/// the Windows LogonUser function. /// dwLogonProvider parameter of the Windows LogonUser function.
/// </param> /// </param>
public WindowsImpersonatedIdentity(string userName, string domainName, string password, LogonUserType logonType = LogonUserType.LOGON32_LOGON_INTERACTIVE, public WindowsImpersonatedIdentity(string userName, string domainName, string password, LogonUserType logonType = LogonUserType.LOGON32_LOGON_INTERACTIVE,
LogonUserProvider provider = LogonUserProvider.LOGON32_PROVIDER_DEFAULT) LogonUserProvider provider = LogonUserProvider.LOGON32_PROVIDER_DEFAULT) : base(userName, domainName, password, logonType, provider) => impersonationContext = AuthenticatedIdentity.Impersonate();
{
if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException(nameof(userName));
if (string.IsNullOrEmpty(password)) throw new ArgumentNullException(nameof(password));
if (string.IsNullOrEmpty(domainName) && !userName.Contains("@")) throw new ArgumentNullException(nameof(domainName));
if (LogonUser(userName, domainName, password, logonType, provider, out var hToken))
{
using (hToken)
{
identity = new WindowsIdentity(hToken.DangerousGetHandle());
impersonationContext = identity.Impersonate();
}
}
else
throw new Win32Exception();
}
/// <summary> /// <summary>
/// Starts the impersonation with the given <see cref="WindowsIdentity" />. Please note that the account that instantiates this class needs to have the 'Act as part of operating system' privilege set. /// Starts the impersonation with the given <see cref="WindowsIdentity"/>. Please note that the account that instantiates this class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary> /// </summary>
/// <param name="identityToImpersonate">The identity to impersonate.</param> /// <param name="identityToImpersonate">The identity to impersonate.</param>
public WindowsImpersonatedIdentity(WindowsIdentity identityToImpersonate) public WindowsImpersonatedIdentity(WindowsIdentity identityToImpersonate) : base(identityToImpersonate) => impersonationContext = AuthenticatedIdentity.Impersonate();
{
identity = identityToImpersonate;
impersonationContext = identity.Impersonate();
ownId = false;
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary> /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose() public override void Dispose()
{ {
impersonationContext?.Undo(); impersonationContext?.Undo();
if (ownId) identity?.Dispose(); base.Dispose();
identity = null;
} }
/// <summary>Gets the authenticated identity.</summary>
public WindowsIdentity AuthenticatedIdentity => identity;
/// <summary>Gets the type of authentication used.</summary>
string IIdentity.AuthenticationType => identity?.AuthenticationType;
/// <summary>Gets a value that indicates whether the user has been authenticated.</summary>
bool IIdentity.IsAuthenticated => identity?.IsAuthenticated ?? false;
/// <summary>Gets the name of the current user.</summary>
string IIdentity.Name => identity?.Name;
} }
} }
#endif #endif

View File

@ -0,0 +1,87 @@
using System;
using System.ComponentModel;
using System.Security.Principal;
using static Vanara.PInvoke.AdvApi32;
namespace Vanara.Security.Principal
{
/// <summary>
/// Impersonation of a user. Allows to execute code under another user context. Please note that the account that instantiates this class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
public class WindowsLoggedInIdentity : IDisposable, IIdentity
{
private readonly SafeHTOKEN hToken;
private readonly bool ownId = true;
/// <summary>
/// Provides an identity to a logged into user given the supplied credentials. Please note that the account that instantiates this
/// class needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">
/// A string that specifies the name of the user. This is the name of the user account to log on to. If you use the user principal
/// name (UPN) format, User@DNSDomainName, the <paramref name="domainName"/> parameter must be NULL.
/// </param>
/// <param name="domainName">
/// A string that specifies the name of the domain or server whose account database contains the <paramref name="userName"/> account.
/// If this parameter is NULL, the user name must be specified in UPN format. If this parameter is ".", the account is validated by
/// using only the local account database.
/// </param>
/// <param name="password">A string that specifies the plain-text password for the user account specified by <paramref name="userName"/>.</param>
/// <param name="logonType">
/// Type of the logon. This parameter can usually be left as the default. For more information, lookup more detail for the
/// dwLogonType parameter of the Windows LogonUser function.
/// </param>
/// <param name="provider">
/// The logon provider. This parameter can usually be left as the default. For more information, lookup more detail for the
/// dwLogonProvider parameter of the Windows LogonUser function.
/// </param>
public WindowsLoggedInIdentity(string userName, string domainName, string password, LogonUserType logonType = LogonUserType.LOGON32_LOGON_INTERACTIVE,
LogonUserProvider provider = LogonUserProvider.LOGON32_PROVIDER_DEFAULT)
{
if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException(nameof(userName));
if (string.IsNullOrEmpty(password)) throw new ArgumentNullException(nameof(password));
if (string.IsNullOrEmpty(domainName) && !userName.Contains("@")) throw new ArgumentNullException(nameof(domainName));
if (LogonUser(userName, domainName, password, logonType, provider, out hToken))
{
using (hToken)
{
AuthenticatedIdentity = new WindowsIdentity(hToken.DangerousGetHandle());
}
}
else
throw new Win32Exception();
}
/// <summary>
/// Starts the impersonation with the given <see cref="WindowsIdentity"/>. Please note that the account that instantiates this class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="identityToImpersonate">The identity to impersonate.</param>
public WindowsLoggedInIdentity(WindowsIdentity identityToImpersonate)
{
AuthenticatedIdentity = identityToImpersonate;
ownId = false;
}
/// <summary>Gets the authenticated identity.</summary>
public WindowsIdentity AuthenticatedIdentity { get; private set; }
/// <summary>Gets the type of authentication used.</summary>
string IIdentity.AuthenticationType => AuthenticatedIdentity?.AuthenticationType;
/// <summary>Gets a value that indicates whether the user has been authenticated.</summary>
bool IIdentity.IsAuthenticated => AuthenticatedIdentity?.IsAuthenticated ?? false;
/// <summary>Gets the name of the current user.</summary>
string IIdentity.Name => AuthenticatedIdentity?.Name;
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
{
if (ownId) AuthenticatedIdentity?.Dispose();
hToken?.Dispose();
AuthenticatedIdentity = null;
}
}
}