using System;
using System.Security.Principal;
namespace Microsoft.Win32.TaskScheduler
{
/// Represents a system account.
internal class User : IEquatable, IDisposable
{
private static readonly WindowsIdentity cur = WindowsIdentity.GetCurrent();
private SecurityIdentifier sid;
/// Initializes a new instance of the class.
///
/// Name of the user. This can be in the format DOMAIN\username or username@domain.com or username or
/// null (for current user).
///
public User(string userName = null)
{
if (string.IsNullOrEmpty(userName)) userName = null;
// 2018-03-02: Hopefully not a breaking change, but by adding in the comparison of an account name without a domain and the
// current user, there is a chance that current implementations will break given the condition that a local account with the same
// name as a domain account exists and the intention was to prefer the local account. In such a case, the developer should
// prepend the user name in TaskDefinition.Principal.UserId with the machine name of the local machine.
if (userName == null || cur.Name.Equals(userName, StringComparison.InvariantCultureIgnoreCase) || GetUser(cur.Name).Equals(userName, StringComparison.InvariantCultureIgnoreCase))
{
Identity = cur;
sid = Identity.User;
}
else if (userName.Contains("\\") && !userName.StartsWith(@"NT AUTHORITY\"))
{
try
{
using (var ds = new NativeMethods.DomainService())
{
Identity = new WindowsIdentity(ds.CrackName(userName));
sid = Identity.User;
}
}
catch { }
}
if (Identity == null)
{
if (userName != null && userName.Contains("@"))
{
Identity = new WindowsIdentity(userName);
sid = Identity.User;
}
if (Identity == null && userName != null)
{
var ntacct = new NTAccount(userName);
try { sid = (SecurityIdentifier)ntacct.Translate(typeof(SecurityIdentifier)); } catch { }
}
}
string GetUser(string domUser)
{
var split = domUser.Split('\\');
return split.Length == 2 ? split[1] : domUser;
}
}
/// Initializes a new instance of the class.
/// The .
internal User(WindowsIdentity wid) { Identity = wid; sid = wid.User; }
/// Gets the current user.
/// The current user.
public static User Current => new User(cur);
/// Gets the identity.
/// The identity.
public WindowsIdentity Identity { get; private set; }
/// Gets a value indicating whether this instance is in an administrator role.
/// true if this instance is an admin; otherwise, false.
public bool IsAdmin => Identity != null ? new WindowsPrincipal(Identity).IsInRole(WindowsBuiltInRole.Administrator) : false;
/// Gets a value indicating whether this instance is the interactive user.
/// true if this instance is the current user; otherwise, false.
public bool IsCurrent => Identity?.User.Equals(cur.User) ?? false;
/// Gets a value indicating whether this instance is a service account.
/// true if this instance is a service account; otherwise, false.
public bool IsServiceAccount
{
get
{
try
{
return (sid != null && (sid.IsWellKnown(WellKnownSidType.LocalSystemSid) || sid.IsWellKnown(WellKnownSidType.NetworkServiceSid) || sid.IsWellKnown(WellKnownSidType.LocalServiceSid)));
}
catch { }
return false;
}
}
/// Gets a value indicating whether this instance is the SYSTEM account.
/// true if this instance is the SYSTEM account; otherwise, false.
public bool IsSystem => sid != null && sid.IsWellKnown(WellKnownSidType.LocalSystemSid);
/// Gets the SID string.
/// The SID string.
public string SidString => sid?.ToString();
/// Gets the NT name (DOMAIN\username).
/// The name of the user.
public string Name => Identity?.Name ?? ((NTAccount)sid?.Translate(typeof(NTAccount)))?.Value;
/// Create a instance from a SID string.
/// The SID string.
/// A instance.
public static User FromSidString(string sid) => new User(((NTAccount)new SecurityIdentifier(sid).Translate(typeof(NTAccount))).Value);
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose() => Identity?.Dispose();
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object obj)
{
if (obj is User user)
return Equals(user);
if (obj is WindowsIdentity wid && sid != null)
return sid.Equals(wid.User);
try
{
if (obj is string un)
return Equals(new User(un));
}
catch { }
return base.Equals(obj);
}
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(User other) => (other != null && sid != null) ? sid.Equals(other.sid) : false;
/// Returns a hash code for this instance.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode() => sid?.GetHashCode() ?? 0;
}
}