1810 lines · 73.9 KB · C#
RawDownload
//******************************************************************************************************//  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

Lines 1–500 of 1810.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t