//******************************************************************************************************// 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{ /// <summary> /// Represents information about a local user or a domain user (e.g., from Active Directory). /// </summary> /// <remarks> /// See <a href="http://msdn.microsoft.com/en-us/library/ms677980.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms677980.aspx</a> for more information on active directory properties. /// </remarks> /// <example> /// This example shows how to retrieve user information from Active Directory: /// <code> /// 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(); /// } /// } /// </code> /// This example shows the config file section that can be used to specify the domain account to be used for Active Directory queries: /// <code> /// <![CDATA[ /// <?xml version="1.0"?> /// <configuration> /// <configSections> /// <section name="categorizedSettings" type="GSF.Configuration.CategorizedSettingsSection, GSF.Core" /> /// </configSections> /// <categorizedSettings> /// <activeDirectory> /// <add name="PrivilegedDomain" value="" description="Domain of privileged domain user account." /// encrypted="false" /> /// <add name="PrivilegedUserName" value="" description="Username of privileged domain user account." /// encrypted="false" /> /// <add name="PrivilegedPassword" value="" description="Password of privileged domain user account." /// encrypted="true" /> /// </activeDirectory> /// </categorizedSettings> /// </configuration> /// ]]> /// </code> /// </example> public sealed class UserInfo : ISupportLifecycle, IPersistSettings { #region [ Members ] // Constants /// <summary> /// Specifies the default value for the <see cref="PersistSettings"/> property. /// </summary> public const bool DefaultPersistSettings = false; /// <summary> /// Specifies the default value for the <see cref="SettingsCategory"/> property. /// </summary> 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 /// <summary> /// Occurs when the class has been disposed. /// </summary> 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 ] /// <summary> /// Initializes a new instance of the <see cref="UserInfo"/> class. /// </summary> /// <param name="loginID"> /// 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. /// </param> /// <exception cref="ArgumentNullException"><paramref name="loginID"/> is a null or empty string.</exception> public UserInfo(string loginID) : this(loginID, string.Empty) { } /// <summary> /// Initializes a new instance of the <see cref="UserInfo"/> class. /// </summary> /// <param name="loginID"> /// 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. /// </param> /// <param name="ldapPath"> /// String in 'LDAP://' format that specifies the Active Directory node where search for the user starts. /// </param> /// <exception cref="ArgumentNullException"><paramref name="loginID"/> is a null or empty string.</exception> 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); } /// <summary> /// Releases the unmanaged resources before the <see cref="UserInfo"/> object is reclaimed by <see cref="GC"/>. /// </summary> ~UserInfo() { Dispose(false); } #endregion #region [ Properties ] bool ISupportLifecycle.Enabled { get { return m_userInfo.Enabled; } set { m_userInfo.Enabled = value; } } /// <summary> /// Gets a flag that indicates whether the object has been disposed. /// </summary> bool ISupportLifecycle.IsDisposed { get { return m_disposed; } } /// <summary> /// Gets or sets a boolean value that indicates whether the settings of <see cref="UserInfo"/> object are /// to be saved to the config file. /// </summary> public bool PersistSettings { get { return m_persistSettings; } set { m_persistSettings = value; } } /// <summary> /// Gets or sets the category under which the settings of <see cref="UserInfo"/> object are to be saved /// to the config file if the <see cref="PersistSettings"/> property is set to true. /// </summary> /// <exception cref="ArgumentNullException">The value being assigned is null or empty string.</exception> public string SettingsCategory { get { return m_settingsCategory; } set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); m_settingsCategory = value; } } /// <summary> /// Gets or sets the principal used for passthrough authentication. /// </summary> /// <remarks> /// 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. /// </remarks> public IPrincipal PassthroughPrincipal { get { return m_passthroughPrincipal; } set { m_passthroughPrincipal = value; } } /// <summary> /// Gets the domain for the user. /// </summary> public string Domain { get { return m_domain; } internal set { m_domain = value; } } /// <summary> /// Gets the user name of the user. /// </summary> public string UserName { get { return m_userName; } } /// <summary> /// Gets LDAP path defined for this user, if any. /// </summary> public string LdapPath { get { return m_ldapPath; } } /// <summary> /// Gets the Login ID of the user. /// </summary> public string LoginID { get { return string.Format("{0}\\{1}", m_domain, m_userName); } } /// <summary> /// Gets the ID of the user in LDAP format. /// </summary> public string LdapID { get { return string.Format("{0}@{1}", m_userName, m_domain); } } /// <summary> /// Gets flag that determines if domain is responding to user existence. /// </summary> /// <returns><c>true</c> if domain responds and user exists; otherwise <c>false</c>.</returns> /// <remarks> /// Note that when the domain is unavailable, this function will return <c>false</c>. /// </remarks> public bool DomainRespondsForUser { get { return m_userInfo.DomainRespondsForUser; } } /// <summary> /// Gets flag that determines if user exists. /// </summary> /// <returns><c>true</c> if user is found to exist; otherwise <c>false</c>.</returns> /// <remarks> /// <para> /// Unlike other properties in this class, <see cref="Exists"/> will still work even if the domain server is /// unavailable. Calling this property without access to a domain server will return <c>true</c> if the user /// is authenticated. This only succeeds if the current user is the same as the <see cref="LoginID"/>, 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. /// </para> /// <para> /// Since this property can check the <see cref="WindowsPrincipal"/> to determine if the current thread identity /// is authenticated, it is important that consumers add the following code at application startup: /// <code>AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);</code> /// </para> /// <para> /// 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 /// <see cref="Groups"/> listing) - if this data is needed offline it will need to be cached by the application in /// anticipation of offline access. /// </para> /// </remarks> public bool Exists { get { return m_userInfo.Exists; } } /// <summary> /// Gets the last login time of the user. /// </summary> public DateTime LastLogon { get { return m_userInfo.LastLogon; } } /// <summary> /// Gets the <see cref="DateTime"/> when the account was created. /// </summary> public DateTime AccountCreationDateLines 1–500 of 1810. Load more
//****************************************************************************************************** // 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 { /// <summary> /// Represents information about a local user or a domain user (e.g., from Active Directory). /// </summary> /// <remarks> /// See <a href="http://msdn.microsoft.com/en-us/library/ms677980.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms677980.aspx</a> for more information on active directory properties. /// </remarks> /// <example> /// This example shows how to retrieve user information from Active Directory: /// <code> /// 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(); /// } /// } /// </code> /// This example shows the config file section that can be used to specify the domain account to be used for Active Directory queries: /// <code> /// <![CDATA[ /// <?xml version="1.0"?> /// <configuration> /// <configSections> /// <section name="categorizedSettings" type="GSF.Configuration.CategorizedSettingsSection, GSF.Core" /> /// </configSections> /// <categorizedSettings> /// <activeDirectory> /// <add name="PrivilegedDomain" value="" description="Domain of privileged domain user account." /// encrypted="false" /> /// <add name="PrivilegedUserName" value="" description="Username of privileged domain user account." /// encrypted="false" /> /// <add name="PrivilegedPassword" value="" description="Password of privileged domain user account." /// encrypted="true" /> /// </activeDirectory> /// </categorizedSettings> /// </configuration> /// ]]> /// </code> /// </example> public sealed class UserInfo : ISupportLifecycle, IPersistSettings { #region [ Members ] // Constants /// <summary> /// Specifies the default value for the <see cref="PersistSettings"/> property. /// </summary> public const bool DefaultPersistSettings = false; /// <summary> /// Specifies the default value for the <see cref="SettingsCategory"/> property. /// </summary> 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 /// <summary> /// Occurs when the class has been disposed. /// </summary> 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 ] /// <summary> /// Initializes a new instance of the <see cref="UserInfo"/> class. /// </summary> /// <param name="loginID"> /// 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. /// </param> /// <exception cref="ArgumentNullException"><paramref name="loginID"/> is a null or empty string.</exception> public UserInfo(string loginID) : this(loginID, string.Empty) { } /// <summary> /// Initializes a new instance of the <see cref="UserInfo"/> class. /// </summary> /// <param name="loginID"> /// 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. /// </param> /// <param name="ldapPath"> /// String in 'LDAP://' format that specifies the Active Directory node where search for the user starts. /// </param> /// <exception cref="ArgumentNullException"><paramref name="loginID"/> is a null or empty string.</exception> 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); } /// <summary> /// Releases the unmanaged resources before the <see cref="UserInfo"/> object is reclaimed by <see cref="GC"/>. /// </summary> ~UserInfo() { Dispose(false); } #endregion #region [ Properties ] bool ISupportLifecycle.Enabled { get { return m_userInfo.Enabled; } set { m_userInfo.Enabled = value; } } /// <summary> /// Gets a flag that indicates whether the object has been disposed. /// </summary> bool ISupportLifecycle.IsDisposed { get { return m_disposed; } } /// <summary> /// Gets or sets a boolean value that indicates whether the settings of <see cref="UserInfo"/> object are /// to be saved to the config file. /// </summary> public bool PersistSettings { get { return m_persistSettings; } set { m_persistSettings = value; } } /// <summary> /// Gets or sets the category under which the settings of <see cref="UserInfo"/> object are to be saved /// to the config file if the <see cref="PersistSettings"/> property is set to true. /// </summary> /// <exception cref="ArgumentNullException">The value being assigned is null or empty string.</exception> public string SettingsCategory { get { return m_settingsCategory; } set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); m_settingsCategory = value; } } /// <summary> /// Gets or sets the principal used for passthrough authentication. /// </summary> /// <remarks> /// 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. /// </remarks> public IPrincipal PassthroughPrincipal { get { return m_passthroughPrincipal; } set { m_passthroughPrincipal = value; } } /// <summary> /// Gets the domain for the user. /// </summary> public string Domain { get { return m_domain; } internal set { m_domain = value; } } /// <summary> /// Gets the user name of the user. /// </summary> public string UserName { get { return m_userName; } } /// <summary> /// Gets LDAP path defined for this user, if any. /// </summary> public string LdapPath { get { return m_ldapPath; } } /// <summary> /// Gets the Login ID of the user. /// </summary> public string LoginID { get { return string.Format("{0}\\{1}", m_domain, m_userName); } } /// <summary> /// Gets the ID of the user in LDAP format. /// </summary> public string LdapID { get { return string.Format("{0}@{1}", m_userName, m_domain); } } /// <summary> /// Gets flag that determines if domain is responding to user existence. /// </summary> /// <returns><c>true</c> if domain responds and user exists; otherwise <c>false</c>.</returns> /// <remarks> /// Note that when the domain is unavailable, this function will return <c>false</c>. /// </remarks> public bool DomainRespondsForUser { get { return m_userInfo.DomainRespondsForUser; } } /// <summary> /// Gets flag that determines if user exists. /// </summary> /// <returns><c>true</c> if user is found to exist; otherwise <c>false</c>.</returns> /// <remarks> /// <para> /// Unlike other properties in this class, <see cref="Exists"/> will still work even if the domain server is /// unavailable. Calling this property without access to a domain server will return <c>true</c> if the user /// is authenticated. This only succeeds if the current user is the same as the <see cref="LoginID"/>, 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. /// </para> /// <para> /// Since this property can check the <see cref="WindowsPrincipal"/> to determine if the current thread identity /// is authenticated, it is important that consumers add the following code at application startup: /// <code>AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);</code> /// </para> /// <para> /// 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 /// <see cref="Groups"/> listing) - if this data is needed offline it will need to be cached by the application in /// anticipation of offline access. /// </para> /// </remarks> public bool Exists { get { return m_userInfo.Exists; } } /// <summary> /// Gets the last login time of the user. /// </summary> public DateTime LastLogon { get { return m_userInfo.LastLogon; } } /// <summary> /// Gets the <see cref="DateTime"/> when the account was created. /// </summary> public DateTime AccountCreationDate