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; } }