//****************************************************************************************************** // UserInfo.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 01/10/2004 - J. Ritchie Carroll // Original version of source code generated. // 01/03/2006 - Pinal C. Patel // 2.0 version of source code migrated from 1.1 source (GSF.Shared.Identity). // 09/27/2006 - Pinal C. Patel // Added Authenticate() function. // 09/29/2006 - Pinal C. Patel // Added support to impersonate privileged user for retrieving user information. // 11/06/2007 - Pinal C. Patel // Modified the logic of Authenticate method to use the user's DirectoryEntry instance. // Modified UserEntry property to impersonate privileged user if configured for the instance. // 11/08/2007 - J. Ritchie Carroll // Implemented user customizable implementation of privileged account credentials. // 09/15/2008 - J. Ritchie Carroll // Converted to C#. // 10/06/2008 - Pinal C. Patel // Edited code comments. // 10/06/2008 - Pinal C. Patel // Added static properties RemoteUserID and RemoteUserInfo. // 06/18/2009 - Pinal C. Patel // Modified GetUserProperty() to quit if Enabled is false. // 07/14/2009 - Pinal C. Patel // Modified GetUserProperty() to allow information to be retrieved from the logged-on domain only // in order to prevent timeout issues when trying to retrieve information for a non-domain user. // 08/06/2009 - Pinal C. Patel // Enabled upon initialization rather than instantiation. // 08/12/2009 - Pinal C. Patel // Fixed issue introduced by marking the type as enabled upon initialization (Initialize()). // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 03/11/2010 - Pinal C. Patel // Modified AuthenticateUser() to return IPrincipal of the authenticated user instead of boolean. // 04/07/2010 - Pinal C. Patel // Updated the timeout prevention logic used in GetUserProperty() to work correctly when // privileged identity is specified for Active Directory operations. // 05/24/2010 - Pinal C. Patel // Added the ability to derive the domain name for added flexibility if one is not specified. // 05/27/2010 - Pinal C. Patel // Modified DirectoryEntry object creation in Initialize() to take in to consideration the domain. // Modified Initialize() to move the derivation of domain name after DirectoryEntry initialization. // 06/17/2010 - Pinal C. Patel // Modified the process of getting the user's DirectoryEntry object in Initialize() method to use // the default constructor of DirectorySearcher resulting in the search for the user to be // performed in the domain to which the host machine (where this code is executing) is joined. // 07/12/2010 - Pinal C. Patel // Added publicly accessible ImpersonatePrivilegedAccount() method allow for the impersonation of // privileged domain account outside of UserInfo class. // Added a constructor to allow for the AD search root to be specified. // 11/04/2010 - Pinal C. Patel // Modified Initialize() to initialize UserEntry only if the machine is joined to a domain. // 02/14/2011 - J. Ritchie Carroll // Added WinNT DirectoryEntry lookups for local accounts when user is not connected to a domain. // 02/15/2011 - J. Ritchie Carroll // Modified "Groups" property to always return domain prefixed group names for the user. // Modified to allow default lookup of account name from machines not connected to a domain even // if local machine name is not specified as domain name -and- to allow lookup of local account // information even when connected to a domain if machine name is specified as domain name. // 02/23/2011 - Pinal C. Patel // Fixed a issue in the Active Directory lookup of MaximumPasswordAge property. // 04/04/2011 - J. Ritchie Carroll // Added Exists and DomainAvailable properties to accommodate testing when domain server is // not available (such as when laptop is used when not connected to the domain). // 04/05/2011 - J. Ritchie Carroll // Updated class to attempt reintialization after system resume in case user no longer has // access to domain. // 09/21/2011 - J. Ritchie Carroll // Added Mono implementation exception regions. // 11/23/2011 - J. Ritchie Carroll // Modified to support new life cycle interface requirements (i.e., Disposed event). // 12/14/2012 - Starlynn Danyelle Gilliam // Modified Header. // 03/05/2013 - Joe France // Prefer the lastLogonTimestamp property over lastLogon property. // 03/06/2013 - Pinal C. Patel // Added new GetUserPropertyValue() and GetUserPropertyValueAsString() methods to replace old // GetUserProperty() method and marked GetUserPropertyValue() as obsolete. // Updated LastLogon to revert using lastLogon property instead of lastLogonTimestamp since it is // updated less frequently and fixed the parsing logic for it since the return value is a large // integer. // 06/03/2013 - J. Ritchie Carroll // Added static local user/group manipulation functions. // 01/02/2014 - Stephen C. Wills // Added static account-to-SID conversion functions. // //****************************************************************************************************** using System; using System.Configuration; using System.Security; using System.Security.Principal; using System.Text.RegularExpressions; using System.Threading; using GSF.Configuration; namespace GSF.Identity { /// /// Represents information about a local user or a domain user (e.g., from Active Directory). /// /// /// See http://msdn.microsoft.com/en-us/library/ms677980.aspx for more information on active directory properties. /// /// /// This example shows how to retrieve user information from Active Directory: /// /// using System; /// using GSF.Identity; /// /// class Program /// { /// static void Main(string[] args) /// { /// // Retrieve and display user information from Active Directory. /// using (UserInfo user = new UserInfo("XYZCorp\\johndoe")) /// { /// Console.WriteLine(string.Format("First Name: {0}", user.FirstName)); /// Console.WriteLine(string.Format("Last Name: {0}", user.LastName)); /// Console.WriteLine(string.Format("Middle Initial: {0}", user.MiddleInitial)); /// Console.WriteLine(string.Format("Email Address: {0}", user.Email)); /// Console.WriteLine(string.Format("Telephone Number: {0}", user.Telephone)); /// } /// /// Console.ReadLine(); /// } /// } /// /// This example shows the config file section that can be used to specify the domain account to be used for Active Directory queries: /// /// /// /// ///
/// /// /// /// /// /// /// /// /// /// ]]> /// /// public sealed class UserInfo : ISupportLifecycle, IPersistSettings { #region [ Members ] // Constants /// /// Specifies the default value for the property. /// public const bool DefaultPersistSettings = false; /// /// Specifies the default value for the property. /// public const string DefaultSettingsCategory = "ActiveDirectory"; internal const string SecurityExceptionFormat = "{0} user account control information cannot be obtained. {1} may not have needed rights to {2}."; internal const string UnknownErrorFormat = "Unknown error. Invalid value returned when querying user account control for {0}. Value: '{1}'"; internal const int ACCOUNTDISABLED = 2; internal const int LOCKED = 16; internal const int PASSWD_CANT_CHANGE = 64; internal const int DONT_EXPIRE_PASSWORD = 65536; // Events /// /// Occurs when the class has been disposed. /// public event EventHandler Disposed; // Fields private IUserInfo m_userInfo; private string m_domain; private readonly string m_userName; private bool m_persistSettings; private string m_settingsCategory; private IPrincipal m_passthroughPrincipal; private int m_userAccountControl; private bool m_disposed; internal string m_ldapPath; internal string m_privilegedDomain; internal string m_privilegedUserName; internal string m_privilegedPassword; #endregion #region [ Constructors ] /// /// Initializes a new instance of the class. /// /// /// Login ID in 'domain\username' format of the user's account whose information is to be retrieved. Login ID /// can also be specified in 'username' format without the domain name, in which case the domain name will be /// approximated based on the privileged user domain if specified, default logon domain of the host machine /// if available, or the domain of the identity that owns the host process. /// /// is a null or empty string. public UserInfo(string loginID) : this(loginID, string.Empty) { } /// /// Initializes a new instance of the class. /// /// /// Login ID in 'domain\username' format of the user's account whose information is to be retrieved. Login ID /// can also be specified in 'username' format without the domain name, in which case the domain name will be /// approximated based on the privileged user domain if specified, default logon domain of the host machine /// if available, or the domain of the identity that owns the host process. /// /// /// String in 'LDAP://' format that specifies the Active Directory node where search for the user starts. /// /// is a null or empty string. public UserInfo(string loginID, string ldapPath) { if (string.IsNullOrEmpty(loginID)) throw new ArgumentNullException(nameof(loginID)); string[] accountParts = loginID.Split('\\'); if (accountParts.Length != 2) { accountParts = loginID.Split('@'); if (accountParts.Length != 2) { // Login ID is specified in 'username' format. m_userName = loginID; } else { // Login ID is specified in 'username@domain' format. m_userName = accountParts[0]; m_domain = accountParts[1]; } } else { // Login ID is specified in 'domain\username' format. m_domain = accountParts[0]; m_userName = accountParts[1]; } m_ldapPath = ldapPath; // Initialize default settings m_persistSettings = DefaultPersistSettings; m_settingsCategory = DefaultSettingsCategory; m_userAccountControl = -1; if (Common.IsPosixEnvironment) m_userInfo = new UnixUserInfo(this); else m_userInfo = new WindowsUserInfo(this); } /// /// Releases the unmanaged resources before the object is reclaimed by . /// ~UserInfo() { Dispose(false); } #endregion #region [ Properties ] bool ISupportLifecycle.Enabled { get { return m_userInfo.Enabled; } set { m_userInfo.Enabled = value; } } /// /// Gets a flag that indicates whether the object has been disposed. /// bool ISupportLifecycle.IsDisposed { get { return m_disposed; } } /// /// Gets or sets a boolean value that indicates whether the settings of object are /// to be saved to the config file. /// public bool PersistSettings { get { return m_persistSettings; } set { m_persistSettings = value; } } /// /// Gets or sets the category under which the settings of object are to be saved /// to the config file if the property is set to true. /// /// The value being assigned is null or empty string. public string SettingsCategory { get { return m_settingsCategory; } set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); m_settingsCategory = value; } } /// /// Gets or sets the principal used for passthrough authentication. /// /// /// This is necessary to determine whether a domain user exists when the /// computer is disconnected from the domain but still able to authenticate /// using the last seen domain user account. /// public IPrincipal PassthroughPrincipal { get { return m_passthroughPrincipal; } set { m_passthroughPrincipal = value; } } /// /// Gets the domain for the user. /// public string Domain { get { return m_domain; } internal set { m_domain = value; } } /// /// Gets the user name of the user. /// public string UserName { get { return m_userName; } } /// /// Gets LDAP path defined for this user, if any. /// public string LdapPath { get { return m_ldapPath; } } /// /// Gets the Login ID of the user. /// public string LoginID { get { return string.Format("{0}\\{1}", m_domain, m_userName); } } /// /// Gets the ID of the user in LDAP format. /// public string LdapID { get { return string.Format("{0}@{1}", m_userName, m_domain); } } /// /// Gets flag that determines if domain is responding to user existence. /// /// true if domain responds and user exists; otherwise false. /// /// Note that when the domain is unavailable, this function will return false. /// public bool DomainRespondsForUser { get { return m_userInfo.DomainRespondsForUser; } } /// /// Gets flag that determines if user exists. /// /// true if user is found to exist; otherwise false. /// /// /// Unlike other properties in this class, will still work even if the domain server is /// unavailable. Calling this property without access to a domain server will return true if the user /// is authenticated. This only succeeds if the current user is the same as the , there /// is no way to determine if a user other than the currently authenticated user exists without access to /// the domain server. Local accounts are not subject to these constraints since the domain server will /// effectively be your local computer which is always accessible. /// /// /// Since this property can check the to determine if the current thread identity /// is authenticated, it is important that consumers add the following code at application startup: /// AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); /// /// /// If a laptop user that's normally connected to the domain takes their computer on the road without VPN or other /// connectivity to the domain, the user can still login to the laptop using cached credentials therefore they are /// still considered to exist. Without access to the domain, no user information will be available (such as the /// listing) - if this data is needed offline it will need to be cached by the application in /// anticipation of offline access. /// /// public bool Exists { get { return m_userInfo.Exists; } } /// /// Gets the last login time of the user. /// public DateTime LastLogon { get { return m_userInfo.LastLogon; } } /// /// Gets the when the account was created. /// public DateTime AccountCreationDate { get { return m_userInfo.AccountCreationDate; } } /// /// Gets the , in UTC, of next password change for the user. /// public DateTime NextPasswordChangeDate { get { return m_userInfo.NextPasswordChangeDate; } } /// /// Gets the account control information of the user. /// /// User account control information cannot be obtained, may not have needed rights. /// Unknown error. Invalid value returned when querying user account control. public int UserAccountControl { get { if (m_userInfo.Enabled && m_userAccountControl == -1) { if (m_userInfo.IsLocalAccount) { m_userAccountControl = m_userInfo.LocalUserAccountControl; } else { string userPropertyValue = GetUserPropertyValue("userAccountControl"); if (string.IsNullOrEmpty(userPropertyValue)) throw new SecurityException(string.Format(SecurityExceptionFormat, "Active directory", CurrentUserID, m_domain)); if (!int.TryParse(userPropertyValue, out m_userAccountControl)) throw new InvalidOperationException(string.Format(UnknownErrorFormat, LoginID, userPropertyValue)); } } return m_userAccountControl; } internal set { m_userAccountControl = value; } } /// /// Gets flag that determines if account is locked-out for this user. /// /// User account control information cannot be obtained, may not have needed rights. /// Unknown error. Invalid value returned when querying user account control. public bool AccountIsLockedOut { get { return Convert.ToBoolean(UserAccountControl & LOCKED); } } /// /// Gets flag that determines if account is disabled for this user. /// /// User account control information cannot be obtained, may not have needed rights. /// Unknown error. Invalid value returned when querying user account control. public bool AccountIsDisabled { get { return Convert.ToBoolean(UserAccountControl & ACCOUNTDISABLED); } } /// /// Gets flag that determines if account password cannot change for this user. /// /// User account control information cannot be obtained, may not have needed rights. /// Unknown error. Invalid value returned when querying user account control. public bool PasswordCannotChange { get { return Convert.ToBoolean(UserAccountControl & PASSWD_CANT_CHANGE); } } /// /// Gets flag that determines if account password does not expire for this user. /// /// User account control information cannot be obtained, may not have needed rights. /// Unknown error. Invalid value returned when querying user account control. public bool PasswordDoesNotExpire { get { return Convert.ToBoolean(UserAccountControl & DONT_EXPIRE_PASSWORD); } } /// /// Gets this maximum password age for the user. /// public Ticks MaximumPasswordAge { get { return m_userInfo.MaximumPasswordAge; } } /// /// Gets all the groups associated with the user - this includes local groups and Active Directory groups if applicable. /// /// /// Groups names are prefixed with their associated domain, computer name or BUILTIN. /// public string[] Groups { get { return m_userInfo.Groups; } } /// /// Gets the local groups the user is a member of. /// /// /// Groups names are prefixed with BUILTIN or computer name. /// public string[] LocalGroups { get { return m_userInfo.LocalGroups; } } /// /// Gets the First Name of the user. /// /// Returns the value retrieved for the "givenName" active directory property. public string FirstName { get { if (m_userInfo.IsLocalAccount) return GetNameElements(DisplayName)[0]; return GetUserPropertyValue("givenName"); } } /// /// Gets the Last Name of the user. /// /// Returns the value retrieved for the "sn" active directory property. public string LastName { get { if (m_userInfo.IsLocalAccount) return GetNameElements(DisplayName)[1]; return GetUserPropertyValue("sn"); } } /// /// Gets the Display Name the user. /// /// Returns the value retrieved for the "displayName" active directory property. public string DisplayName { get { if (m_userInfo.IsLocalAccount) return m_userInfo.FullLocalUserName; return GetUserPropertyValue("displayName"); } } /// /// Gets the Middle Initial of the user. /// /// Returns the value retrieved for the "initials" active directory property. public string MiddleInitial { get { return GetUserPropertyValue("initials"); } } /// /// Gets the Full Name of the user. /// /// Returns the concatenation of , and properties. public string FullName { get { if (m_userInfo.IsLocalAccount) return m_userInfo.FullLocalUserName; string fName = FirstName; string lName = LastName; string mInitial = MiddleInitial; if (!string.IsNullOrEmpty(fName) && !string.IsNullOrEmpty(lName)) { if (string.IsNullOrEmpty(mInitial)) return fName + " " + lName; return fName + " " + mInitial + ". " + lName; } return LoginID; } } /// /// Gets the E-Mail address of the user. /// /// Returns the value retrieved for the "mail" active directory property. public string Email { get { return GetUserPropertyValue("mail"); } } /// /// Gets the web page address of the user. /// /// Returns the value retrieved for the "wWWHomePage" active directory property. public string Webpage { get { return GetUserPropertyValue("wWWHomePage"); } } /// /// Gets the description specified for the user. /// /// Returns the value retrieved for the "description" active directory property. public string Description { get { return GetUserPropertyValue("description"); } } /// /// Gets the Telephone Number of the user. /// /// Returns the value retrieved for the "telephoneNumber" active directory property. public string Telephone { get { return GetUserPropertyValue("telephoneNumber"); } } /// /// Gets the Title of the user. /// /// Returns the value retrieved for the "title" active directory property. public string Title { get { return GetUserPropertyValue("title"); } } /// /// Gets the Company of the user. /// /// Returns the value retrieved for the "company" active directory property. public string Company { get { return GetUserPropertyValue("company"); } } /// /// Gets the Office location of the user. /// /// Returns the value retrieved for the "physicalDeliveryOfficeName" active directory property. public string Office { get { return GetUserPropertyValue("physicalDeliveryOfficeName"); } } /// /// Gets the Department where the user works. /// /// Returns the value retrieved for the "department" active directory property. public string Department { get { return GetUserPropertyValue("department"); } } /// /// Gets the City where the user works. /// /// Returns the value retrieved for the "l" active directory property. public string City { get { return GetUserPropertyValue("l"); } } /// /// Gets the Mailbox address of where the user works. /// /// Returns the value retrieved for the "streetAddress" active directory property. public string Mailbox { get { return GetUserPropertyValue("streetAddress"); } } /// /// Gets flag that determines if this instance is based on a local account instead of found through LDAP. /// public bool IsLocalAccount { get { return m_userInfo.IsLocalAccount; } } #endregion #region [ Methods ] /// /// Releases all the resources used by the object. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. private void Dispose(bool disposing) { if (!m_disposed) { try { // This will be done regardless of whether the object is finalized or disposed. if (disposing) { if ((object)m_userInfo != null) m_userInfo.Dispose(); m_userInfo = null; // This will be done only when the object is disposed by calling Dispose(). SaveSettings(); } } finally { m_disposed = true; // Prevent duplicate dispose. if (Disposed != null) Disposed(this, EventArgs.Empty); } } } /// /// Initializes the object. /// /// Failed to initialize directory entry for . public void Initialize() { m_userInfo.Initialize(); if (string.IsNullOrEmpty(m_domain)) m_domain = Environment.MachineName; } /// /// Saves settings for the object to the config file if the /// property is set to true. /// /// has a value of null or empty string. public void SaveSettings() { if (m_persistSettings) { // Ensure that settings category is specified. if (string.IsNullOrEmpty(m_settingsCategory)) throw new ConfigurationErrorsException("SettingsCategory property has not been set"); // Save settings under the specified category. ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory]; settings["PrivilegedDomain", true].Update(m_privilegedDomain); settings["PrivilegedUserName", true].Update(m_privilegedUserName); settings["PrivilegedPassword", true].Update(m_privilegedPassword); config.Save(); } } /// /// Loads saved settings for the object from the config file if the /// property is set to true. /// /// has a value of null or empty string. public void LoadSettings() { if (m_persistSettings) { // Ensure that settings category is specified. if (string.IsNullOrEmpty(m_settingsCategory)) throw new ConfigurationErrorsException("SettingsCategory property has not been set"); // Load settings from the specified category. ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory]; settings.Add("PrivilegedDomain", m_privilegedDomain, "Domain of privileged domain user account used for Active Directory information lookup, if needed."); settings.Add("PrivilegedUserName", m_privilegedUserName, "Username of privileged domain user account used for Active Directory information lookup, if needed."); settings.Add("PrivilegedPassword", m_privilegedPassword, "Encrypted password of privileged domain user account used for Active Directory information lookup, if needed.", true); m_privilegedDomain = settings["PrivilegedDomain"].ValueAs(m_privilegedDomain); m_privilegedUserName = settings["PrivilegedUserName"].ValueAs(m_privilegedUserName); m_privilegedPassword = settings["PrivilegedPassword"].ValueAs(m_privilegedPassword); } } /// /// Defines the credentials of a privileged domain account that can be used for impersonation prior to the /// retrieval of user information from the Active Directory. /// /// Domain of privileged domain user account. /// Username of privileged domain user account. /// Password of privileged domain user account. /// /// This example shows how to define the identity of the user to be used for retrieving information from Active Directory: /// /// using System; /// using GSF.Identity; /// /// class Program /// { /// static void Main(string[] args) /// { /// using (UserInfo user = new UserInfo("XYZCorp\\johndoe")) /// { /// // Define the identity to use for retrieving Active Directory information. /// // Persist identity credentials encrypted to the config for easy access. /// user.PersistSettings = true; /// user.DefinePrivilegedAccount("XYZCorp", "admin", "Passw0rd"); /// /// // Retrieve and display user information from Active Directory. /// Console.WriteLine(string.Format("First Name: {0}", user.FirstName)); /// Console.WriteLine(string.Format("Last Name: {0}", user.LastName)); /// Console.WriteLine(string.Format("Middle Initial: {0}", user.MiddleInitial)); /// Console.WriteLine(string.Format("Email Address: {0}", user.Email)); /// Console.WriteLine(string.Format("Telephone Number: {0}", user.Telephone)); /// } /// /// Console.ReadLine(); /// } /// } /// /// public void DefinePrivilegedAccount(string domain, string username, string password) { // Check input parameters. if (string.IsNullOrEmpty(domain)) throw new ArgumentNullException(nameof(domain)); if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username)); if (string.IsNullOrEmpty(password)) throw new ArgumentNullException(nameof(password)); // Set the credentials for privileged domain user account. m_privilegedDomain = domain; m_privilegedUserName = username; m_privilegedPassword = password; } /// /// Impersonates the defined privileged domain account. /// /// An if privileged domain account has been defined, otherwise null. public WindowsImpersonationContext ImpersonatePrivilegedAccount() { if (!string.IsNullOrEmpty(m_privilegedDomain) && !string.IsNullOrEmpty(m_privilegedUserName) && !string.IsNullOrEmpty(m_privilegedPassword)) { // Privileged domain account is specified return ImpersonateUser(m_privilegedDomain, m_privilegedUserName, m_privilegedPassword); } // Privileged domain account is not specified return null; } /// /// Attempts to change the user's password. /// /// Old password. /// New password. public void ChangePassword(string oldPassword, string newPassword) { m_userInfo.ChangePassword(oldPassword, newPassword); } /// /// Returns the value for specified active directory property. /// /// Name of the active directory property whose value is to be retrieved. /// value for the specified active directory property. public string GetUserPropertyValue(string propertyName) { return m_userInfo.GetUserPropertyValue(propertyName); } // Split a string into first name and last name private static string[] GetNameElements(string displayName) { displayName = displayName.Trim(); if (!string.IsNullOrEmpty(displayName)) { int lastSplit = displayName.LastIndexOf(' '); if (lastSplit >= 0) return new[] { displayName.Substring(0, lastSplit), displayName.Substring(lastSplit + 1) }; return new[] { displayName, "" }; } return new[] { "", "" }; } #endregion #region [ Static ] // Static Fields private static string m_lastUserID; private static UserInfo s_currentUserInfo; // Static Properties /// /// Gets the ID name of the current user. /// /// /// The ID name returned is that of the user account under which the code is executing. /// public static string CurrentUserID { get { try { WindowsIdentity identity = WindowsIdentity.GetCurrent(); if ((object)identity != null) return identity.Name; return null; } catch (SecurityException) { return null; } } } /// /// Gets the object for the . /// public static UserInfo CurrentUserInfo { get { string currentUserID = CurrentUserID; if (!string.IsNullOrEmpty(currentUserID)) { if ((object)s_currentUserInfo == null || string.IsNullOrEmpty(m_lastUserID) || !currentUserID.Equals(m_lastUserID, StringComparison.OrdinalIgnoreCase)) { try { s_currentUserInfo = new UserInfo(currentUserID); s_currentUserInfo.Initialize(); } catch (InitializationException) { } } } m_lastUserID = currentUserID; return s_currentUserInfo; } } /// /// Gets the of the remote web user. /// /// /// The returned is that of the remote user accessing the web application. This is /// available only if the virtual directory hosting the web application is configured to use "Integrated /// Windows Authentication". /// public static string RemoteUserID { get { return Thread.CurrentPrincipal.Identity.Name; } } /// /// Gets the object for the . /// public static UserInfo RemoteUserInfo { get { string userID = RemoteUserID; if ((object)userID == null) return null; return new UserInfo(RemoteUserID); } } /// /// Gets a boolean value that indicates whether the current machine is joined to a domain (non-local such as AD or LDAP). /// public static bool MachineIsJoinedToDomain { get { if (Common.IsPosixEnvironment) return UnixUserInfo.MachineIsJoinedToDomain; return WindowsUserInfo.MachineIsJoinedToDomain; } } // Static Methods /// /// Returns a sorted list of the common built-in local groups. On Windows these groups have a domain name of BUILTIN. /// /// Sorted list of the common built-in local groups. /// /// Names in this list will not have a "BUILTIN\" prefix. /// public static string[] GetBuiltInLocalGroups() { if (Common.IsPosixEnvironment) return UnixUserInfo.GetBuiltInLocalGroups(); return WindowsUserInfo.GetBuiltInLocalGroups(); } /// /// Authenticates the specified user credentials. /// /// Domain of user to authenticate. /// Username of user to authenticate. /// Password of user to authenticate. /// true if the user credentials are authenticated successfully; otherwise false. /// /// This example shows how to validate a user's credentials: /// /// using System; /// using GSF.Identity; /// /// class Program /// { /// static void Main(string[] args) /// { /// string domain = "XYZCorp"; /// string username = "johndoe"; /// string password = "password"; /// /// // Authenticate user credentials. /// if ((object)UserInfo.AuthenticateUser(domain, username, password) != null) /// Console.WriteLine("Successfully authenticated user \"{0}\\{1}\".", domain, username); /// else /// Console.WriteLine("Failed to authenticate user \"{0}\\{1}\".", domain, username); /// /// Console.ReadLine(); /// } /// } /// /// public static IPrincipal AuthenticateUser(string domain, string userName, string password) { string errorMessage; return AuthenticateUser(domain, userName, password, out errorMessage); } /// /// Authenticates the specified user credentials. /// /// Domain of user to authenticate. /// Username of user to authenticate. /// Password of user to authenticate. /// Error message returned, if authentication fails. /// true if the user credentials are authenticated successfully; otherwise false. /// /// This example shows how to validate a user's credentials and retrieve an error message if validation fails: /// /// using System; /// using GSF.Identity; /// /// class Program /// { /// static void Main(string[] args) /// { /// string domain = "XYZCorp"; /// string username = "johndoe"; /// string password = "password"; /// string errorMessage; /// /// // Authenticate user credentials. /// if ((object)UserInfo.AuthenticateUser(domain, username, password, out errorMessage) != null) /// Console.WriteLine("Successfully authenticated user \"{0}\\{1}\".", domain, username); /// else /// Console.WriteLine("Failed to authenticate user \"{0}\\{1}\" due to exception: {2}", domain, username, errorMessage); /// /// Console.ReadLine(); /// } /// } /// /// public static IPrincipal AuthenticateUser(string domain, string userName, string password, out string errorMessage) { if (Common.IsPosixEnvironment) return UnixUserInfo.AuthenticateUser(domain, userName, password, out errorMessage); return WindowsUserInfo.AuthenticateUser(domain, userName, password, out errorMessage); } /// /// Impersonates the specified user. /// /// Domain of user to impersonate. /// Username of user to impersonate. /// Password of user to impersonate. /// A object of the impersonated user. /// /// After impersonating a user the code executes under the impersonated user's identity. /// /// /// This example shows how to impersonate a user: /// /// using System; /// using GSF.Identity; /// /// class Program /// { /// static void Main(string[] args) /// { /// Console.WriteLine(string.Format("User before impersonation: {0}", UserInfo.CurrentUserID)); /// UserInfo.ImpersonateUser("XYZCorp", "johndoe", "password"); // Impersonate user. /// Console.WriteLine(string.Format("User after impersonation: {0}", UserInfo.CurrentUserID)); /// /// Console.ReadLine(); /// } /// } /// /// public static WindowsImpersonationContext ImpersonateUser(string domain, string userName, string password) { if (Common.IsPosixEnvironment) return UnixUserInfo.ImpersonateUser(domain, userName, password); return WindowsUserInfo.ImpersonateUser(domain, userName, password); } /// /// Ends the impersonation of the specified user. /// /// of the impersonated user. /// /// This example shows how to terminate an active user impersonation: /// /// using System; /// using System.IO; /// using System.Security.Principal; /// using GSF.Identity; /// /// class Program /// { /// static void Main(string[] args) /// { /// // Impersonate user. /// WindowsImpersonationContext context = UserInfo.ImpersonateUser("XYZCorp", "johndoe", "password"); /// // Perform operation requiring elevated privileges. /// Console.WriteLine(File.ReadAllText(@"\\server\share\file.xml")); /// // End the impersonation. /// UserInfo.EndImpersonation(context); /// /// Console.ReadLine(); /// } /// } /// /// public static void EndImpersonation(WindowsImpersonationContext impersonatedUser) { if ((object)impersonatedUser != null) { impersonatedUser.Undo(); impersonatedUser.Dispose(); } } /// /// Determines if specified is the local domain (i.e., local machine). /// /// Domain name to check. /// trueif specified is the local domain (i.e., local machine); otherwise, false. public static bool IsLocalDomain(string domain) { if (Common.IsPosixEnvironment) return string.IsNullOrEmpty(domain) || domain.Equals(".", StringComparison.Ordinal) || domain.Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase); // TODO: NT AUTHORITY and such groups can be localized to the OS language, these groups won't be recognized as local domains on non EN-US machines in this code return string.IsNullOrEmpty(domain) || domain.Equals(".", StringComparison.Ordinal) || domain.Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase) || domain.Equals("NT SERVICE", StringComparison.OrdinalIgnoreCase) || domain.Equals("NT AUTHORITY", StringComparison.OrdinalIgnoreCase) || domain.Equals("IIS APPPOOL", StringComparison.OrdinalIgnoreCase); } /// /// Determines if local user exists. /// /// User name to test for existence. /// true if exists; otherwise, false. /// was null. /// No was specified. public static bool LocalUserExists(string userName) { if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); // Remove any irrelevant white space userName = userName.Trim(); if (userName.Length == 0) throw new ArgumentException("No user name was specified.", nameof(userName)); if (Common.IsPosixEnvironment) return UnixUserInfo.LocalUserExists(userName); return WindowsUserInfo.LocalUserExists(userName); } /// /// Creates a new local user if it does not exist already. /// /// User name to create if it doesn't exist. /// Password to user for new user. /// Optional user description. /// true if user was created; otherwise, false if user already existed. /// or was null. /// No was specified. /// Could not create local user. public static bool CreateLocalUser(string userName, string password, string userDescription = null) { if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); if ((object)password == null) throw new ArgumentNullException(nameof(password)); // Remove any irrelevant white space userName = userName.Trim(); if (userName.Length == 0) throw new ArgumentException("Cannot create local user: no user name was specified.", nameof(userName)); if (Common.IsPosixEnvironment) return UnixUserInfo.CreateLocalUser(userName, password, userDescription); return WindowsUserInfo.CreateLocalUser(userName, password, userDescription); } /// /// Sets local user's password. /// /// User name to change password for. /// New password fro user. /// or was null. /// No was specified or user does not exist. /// Could not set password for local user. public static void SetLocalUserPassword(string userName, string password) { if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); if ((object)password == null) throw new ArgumentNullException(nameof(password)); // Remove any irrelevant white space userName = userName.Trim(); if (userName.Length == 0) throw new ArgumentException("Cannot set password for local user: no user name was specified.", nameof(userName)); if (Common.IsPosixEnvironment) UnixUserInfo.SetLocalUserPassword(userName, password); WindowsUserInfo.SetLocalUserPassword(userName, password); } /// /// Removes local user if it exists. /// /// User name to remove if it exists. /// true if user was removed; otherwise, false if user did not exist. /// was null. /// No was specified. /// Could not remove local user. public static bool RemoveLocalUser(string userName) { if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); // Remove any irrelevant white space userName = userName.Trim(); if (userName.Length == 0) throw new ArgumentException("Cannot remove local user: no user name was specified.", nameof(userName)); if (Common.IsPosixEnvironment) return UnixUserInfo.RemoveLocalUser(userName); return WindowsUserInfo.RemoveLocalUser(userName); } /// /// Determines if local group exists. /// /// Group name to test for existence. /// true if exists; otherwise, false. /// was null. /// No was specified. public static bool LocalGroupExists(string groupName) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); // Remove any irrelevant white space groupName = groupName.Trim(); if (groupName.Length == 0) throw new ArgumentException("No group name was specified.", nameof(groupName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.LocalGroupExists(groupName); return WindowsUserInfo.LocalGroupExists(groupName); } /// /// Creates a new local group if it does not exist already. /// /// Group name to create if it doesn't exist. /// Optional group description. /// true if group was created; otherwise, false if group already existed. /// was null. /// No was specified. /// Could not create local group. public static bool CreateLocalGroup(string groupName, string groupDescription = null) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); // Remove any irrelevant white space groupName = groupName.Trim(); if (groupName.Length == 0) throw new ArgumentException("Cannot create local group: no group name was specified.", nameof(groupName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.CreateLocalGroup(groupName); return WindowsUserInfo.CreateLocalGroup(groupName, groupDescription); } /// /// Removes local group if it exists. /// /// Group name to remove if it exists. /// true if group was removed; otherwise, false if group did not exist. /// was null. /// No was specified. /// Could not remove local group. public static bool RemoveLocalGroup(string groupName) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); // Remove any irrelevant white space groupName = groupName.Trim(); if (groupName.Length == 0) throw new ArgumentException("Cannot remove local group: no group name was specified.", nameof(groupName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.RemoveLocalGroup(groupName); return WindowsUserInfo.RemoveLocalGroup(groupName); } /// /// Determines if user is in the specified local . /// /// Group name to test. /// User name to test. /// true if user is in group; otherwise, false. /// or was null. /// No or was specified. /// Could not determine if user was in local group. /// /// This function will handle Windows service virtual accounts by specifying the complete virtual account name, /// such as @"NT SERVICE\MyService", as the . This function can also detect /// Active Directory user accounts and groups that may exist in the local group when the /// is prefixed with a domain name and a backslash "\". /// public static bool UserIsInLocalGroup(string groupName, string userName) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); // Remove any irrelevant white space groupName = groupName.Trim(); userName = userName.Trim(); if (groupName.Length == 0) throw new ArgumentException("Cannot determine if user is in local group: no group name was specified.", nameof(groupName)); if (userName.Length == 0) throw new ArgumentException("Cannot determine if user is in local group: no user name was specified.", nameof(userName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.UserIsInLocalGroup(groupName, userName); return WindowsUserInfo.UserIsInLocalGroup(groupName, userName); } /// /// Adds an existing user to the specified local . /// /// Group name to add local user to. /// Existing local user name. /// true if user was added to local group; otherwise, false meaning user was already in group. /// or was null. /// No or was specified. /// Could not add user to local group. /// /// This function will handle Windows service virtual accounts by specifying the complete virtual account name, /// such as @"NT SERVICE\MyService", as the . This function can also add /// Active Directory user accounts and groups to the local group the when the is /// prefixed with a domain name and a backslash "\". /// public static bool AddUserToLocalGroup(string groupName, string userName) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); // Remove any irrelevant white space groupName = groupName.Trim(); userName = userName.Trim(); if (groupName.Length == 0) throw new ArgumentException("Cannot add user to local group: no group name was specified.", nameof(groupName)); if (userName.Length == 0) throw new ArgumentException("Cannot add user to local group: no user name was specified.", nameof(userName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.AddUserToLocalGroup(groupName, userName); return WindowsUserInfo.AddUserToLocalGroup(groupName, userName); } /// /// Removes an existing user from the specified local . /// /// Group name to remove local user from. /// Existing local user name. /// true if user was removed from local group; otherwise, false meaning user did not exist in group. /// or was null. /// No or was specified. /// Could not remove user from local group. /// /// This function will handle Windows service virtual accounts by specifying the complete virtual account name, /// such as @"NT SERVICE\MyService", as the . This function can also remove /// Active Directory user accounts and groups from the local group the when the is /// prefixed with a domain name and a backslash "\". /// public static bool RemoveUserFromLocalGroup(string groupName, string userName) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); if ((object)userName == null) throw new ArgumentNullException(nameof(userName)); // Remove any irrelevant white space groupName = groupName.Trim(); userName = userName.Trim(); if (groupName.Length == 0) throw new ArgumentException("Cannot remove user from local group: no group name was specified.", nameof(groupName)); if (userName.Length == 0) throw new ArgumentException("Cannot remove user from local group: no user name was specified.", nameof(userName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.RemoveUserFromLocalGroup(groupName, userName); return WindowsUserInfo.RemoveUserFromLocalGroup(groupName, userName); } /// /// Gets a list of users that exist in the specified local . /// /// Group name to remove local user from. /// List of users that exist in the in group. /// was null. /// No was specified. /// Could not get members for local group. public static string[] GetLocalGroupUserList(string groupName) { if ((object)groupName == null) throw new ArgumentNullException(nameof(groupName)); // Remove any irrelevant white space groupName = groupName.Trim(); if (groupName.Length == 0) throw new ArgumentException("Cannot get members for local group: no group name was specified.", nameof(groupName)); groupName = ValidateGroupName(groupName); if (Common.IsPosixEnvironment) return UnixUserInfo.GetLocalGroupUserList(groupName); return WindowsUserInfo.GetLocalGroupUserList(groupName); } /// /// Converts the given user name to the SID corresponding to that name. /// /// The user name for which to look up the SID. /// The SID for the given user name, or the user name if no SID can be found. /// /// If the cannot be converted to a SID, /// will be the return value. /// public static string UserNameToSID(string userName) { if (Common.IsPosixEnvironment) return UnixUserInfo.UserNameToSID(userName); return WindowsUserInfo.AccountNameToSID(userName); } /// /// Converts the given group name to the SID corresponding to that name. /// /// The group name for which to look up the SID. /// The SID for the given group name, or the group name if no SID can be found. /// /// If the cannot be converted to a SID, /// will be the return value. /// public static string GroupNameToSID(string groupName) { if (Common.IsPosixEnvironment) return UnixUserInfo.GroupNameToSID(groupName); return WindowsUserInfo.AccountNameToSID(groupName); } /// /// Converts the given SID to the corresponding account name. /// /// The SID for which to look up the account name. /// The account name for the given SID, or the SID if no account name can be found. /// /// If the cannot be converted to an account name, /// will be the return value. /// public static string SIDToAccountName(string sid) { if (Common.IsPosixEnvironment) return UnixUserInfo.SIDToAccountName(sid); return WindowsUserInfo.SIDToAccountName(sid); } /// /// Determines whether the given security identifier identifies a user account. /// /// The security identifier. /// True if the security identifier identifies a user account; false otherwise. public static bool IsUserSID(string sid) { if (Common.IsPosixEnvironment) return UnixUserInfo.IsUserSID(sid); return WindowsUserInfo.IsUserSID(sid); } /// /// Determines whether the given security identifier identifies a group. /// /// The security identifier. /// True if the security identifier identifies a group; false otherwise. public static bool IsGroupSID(string sid) { if (Common.IsPosixEnvironment) return UnixUserInfo.IsGroupSID(sid); return WindowsUserInfo.IsGroupSID(sid); } // DirectoryEntry will only resolve "BUILTIN\" groups with a dot ".\" internal static string ValidateGroupName(string groupName) { if (!Common.IsPosixEnvironment && groupName.StartsWith(@"BUILTIN\", StringComparison.OrdinalIgnoreCase)) return Regex.Replace(groupName, @"^BUILTIN\\", @".\", RegexOptions.IgnoreCase); return groupName; } #endregion } }