FlashPatcher/FlashPatcher/TaskService/User.cs

146 lines
6.1 KiB
C#

using System;
using System.Security.Principal;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>Represents a system account.</summary>
internal class User : IEquatable<User>, IDisposable
{
private static readonly WindowsIdentity cur = WindowsIdentity.GetCurrent();
private SecurityIdentifier sid;
/// <summary>Initializes a new instance of the <see cref="User"/> class.</summary>
/// <param name="userName">
/// Name of the user. This can be in the format <c>DOMAIN\username</c> or <c>username@domain.com</c> or <c>username</c> or
/// <c>null</c> (for current user).
/// </param>
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;
}
}
/// <summary>Initializes a new instance of the <see cref="User"/> class.</summary>
/// <param name="wid">The <see cref="WindowsIdentity"/>.</param>
internal User(WindowsIdentity wid) { Identity = wid; sid = wid.User; }
/// <summary>Gets the current user.</summary>
/// <value>The current user.</value>
public static User Current => new User(cur);
/// <summary>Gets the identity.</summary>
/// <value>The identity.</value>
public WindowsIdentity Identity { get; private set; }
/// <summary>Gets a value indicating whether this instance is in an administrator role.</summary>
/// <value><c>true</c> if this instance is an admin; otherwise, <c>false</c>.</value>
public bool IsAdmin => Identity != null ? new WindowsPrincipal(Identity).IsInRole(WindowsBuiltInRole.Administrator) : false;
/// <summary>Gets a value indicating whether this instance is the interactive user.</summary>
/// <value><c>true</c> if this instance is the current user; otherwise, <c>false</c>.</value>
public bool IsCurrent => Identity?.User.Equals(cur.User) ?? false;
/// <summary>Gets a value indicating whether this instance is a service account.</summary>
/// <value><c>true</c> if this instance is a service account; otherwise, <c>false</c>.</value>
public bool IsServiceAccount
{
get
{
try
{
return (sid != null && (sid.IsWellKnown(WellKnownSidType.LocalSystemSid) || sid.IsWellKnown(WellKnownSidType.NetworkServiceSid) || sid.IsWellKnown(WellKnownSidType.LocalServiceSid)));
}
catch { }
return false;
}
}
/// <summary>Gets a value indicating whether this instance is the SYSTEM account.</summary>
/// <value><c>true</c> if this instance is the SYSTEM account; otherwise, <c>false</c>.</value>
public bool IsSystem => sid != null && sid.IsWellKnown(WellKnownSidType.LocalSystemSid);
/// <summary>Gets the SID string.</summary>
/// <value>The SID string.</value>
public string SidString => sid?.ToString();
/// <summary>Gets the NT name (DOMAIN\username).</summary>
/// <value>The name of the user.</value>
public string Name => Identity?.Name ?? ((NTAccount)sid?.Translate(typeof(NTAccount)))?.Value;
/// <summary>Create a <see cref="User"/> instance from a SID string.</summary>
/// <param name="sid">The SID string.</param>
/// <returns>A <see cref="User"/> instance.</returns>
public static User FromSidString(string sid) => new User(((NTAccount)new SecurityIdentifier(sid).Translate(typeof(NTAccount))).Value);
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose() => Identity?.Dispose();
/// <summary>Determines whether the specified <see cref="System.Object"/>, is equal to this instance.</summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.</returns>
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);
}
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
public bool Equals(User other) => (other != null && sid != null) ? sid.Equals(other.sid) : false;
/// <summary>Returns a hash code for this instance.</summary>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode() => sid?.GetHashCode() ?? 0;
}
}