File: System\DirectoryServices\AccountManagement\User.cs
Web Access
Project: src\src\runtime\src\libraries\System.DirectoryServices.AccountManagement\src\System.DirectoryServices.AccountManagement.csproj (System.DirectoryServices.AccountManagement)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Principal;

namespace System.DirectoryServices.AccountManagement
{
    [DirectoryRdnPrefix("CN")]
    public class UserPrincipal : AuthenticablePrincipal
    {
        //
        // Public constructors
        //
        public UserPrincipal(PrincipalContext context) : base(context)
        {
            if (context == null)
                throw new ArgumentException(SR.NullArguments);

            this.ContextRaw = context;
            this.unpersisted = true;
        }

        public UserPrincipal(PrincipalContext context, string samAccountName, string password, bool enabled) : this(context)
        {
            if (samAccountName == null || password == null)
                throw new ArgumentException(SR.NullArguments);

            if (Context.ContextType != ContextType.ApplicationDirectory)
                this.SamAccountName = samAccountName;

            this.Name = samAccountName;
            this.SetPassword(password);
            this.Enabled = enabled;
        }

        //
        // Public properties
        //

        // GivenName
        private string _givenName;        // the actual property value
        private LoadState _givenNameChanged = LoadState.NotSet;   // change-tracking

        public string GivenName
        {
            get
            {
                return HandleGet<string>(ref _givenName, PropertyNames.UserGivenName, ref _givenNameChanged);
            }

            set
            {
                if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.UserGivenName))
                    throw new InvalidOperationException(SR.InvalidPropertyForStore);

                HandleSet<string>(ref _givenName, value, ref _givenNameChanged,
                                  PropertyNames.UserGivenName);
            }
        }

        // MiddleName
        private string _middleName;        // the actual property value
        private LoadState _middleNameChanged = LoadState.NotSet;   // change-tracking

        public string MiddleName
        {
            get
            {
                return HandleGet<string>(ref _middleName, PropertyNames.UserMiddleName, ref _middleNameChanged);
            }

            set
            {
                if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.UserMiddleName))
                    throw new InvalidOperationException(SR.InvalidPropertyForStore);

                HandleSet<string>(ref _middleName, value, ref _middleNameChanged,
                                  PropertyNames.UserMiddleName);
            }
        }

        // Surname
        private string _surname;        // the actual property value
        private LoadState _surnameChanged = LoadState.NotSet;   // change-tracking

        public string Surname
        {
            get
            {
                return HandleGet<string>(ref _surname, PropertyNames.UserSurname, ref _surnameChanged);
            }

            set
            {
                if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.UserSurname))
                    throw new InvalidOperationException(SR.InvalidPropertyForStore);

                HandleSet<string>(ref _surname, value, ref _surnameChanged,
                                  PropertyNames.UserSurname);
            }
        }

        // EmailAddress
        private string _emailAddress;        // the actual property value
        private LoadState _emailAddressChanged = LoadState.NotSet;   // change-tracking

        public string EmailAddress
        {
            get
            {
                return HandleGet<string>(ref _emailAddress, PropertyNames.UserEmailAddress, ref _emailAddressChanged);
            }

            set
            {
                if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.UserEmailAddress))
                    throw new InvalidOperationException(SR.InvalidPropertyForStore);

                HandleSet<string>(ref _emailAddress, value, ref _emailAddressChanged,
                                  PropertyNames.UserEmailAddress);
            }
        }

        // VoiceTelephoneNumber
        private string _voiceTelephoneNumber;        // the actual property value
        private LoadState _voiceTelephoneNumberChanged = LoadState.NotSet;   // change-tracking

        public string VoiceTelephoneNumber
        {
            get
            {
                return HandleGet<string>(ref _voiceTelephoneNumber, PropertyNames.UserVoiceTelephoneNumber, ref _voiceTelephoneNumberChanged);
            }

            set
            {
                if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.UserVoiceTelephoneNumber))
                    throw new InvalidOperationException(SR.InvalidPropertyForStore);

                HandleSet<string>(ref _voiceTelephoneNumber, value, ref _voiceTelephoneNumberChanged,
                                  PropertyNames.UserVoiceTelephoneNumber);
            }
        }

        // EmployeeId
        private string _employeeID;        // the actual property value
        private LoadState _employeeIDChanged = LoadState.NotSet;   // change-tracking

        public string EmployeeId
        {
            get
            {
                return HandleGet<string>(ref _employeeID, PropertyNames.UserEmployeeID, ref _employeeIDChanged);
            }

            set
            {
                if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.UserEmployeeID))
                    throw new InvalidOperationException(SR.InvalidPropertyForStore);

                HandleSet<string>(ref _employeeID, value, ref _employeeIDChanged,
                                  PropertyNames.UserEmployeeID);
            }
        }

        public override AdvancedFilters AdvancedSearchFilter { get { return rosf; } }

        public static UserPrincipal Current
        {
            get
            {
                PrincipalContext context;

                // Get the correct PrincipalContext to query against, depending on whether we're running
                // as a local or domain user
                if (Utils.IsSamUser())
                {
                    GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "Current: is local user");

                    context = new PrincipalContext(ContextType.Machine);
                }
                else
                {
                    GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "Current: is not local user");

                    context = new PrincipalContext(ContextType.Domain);
                }

                // Construct a query for the current user, using a SID IdentityClaim

                IntPtr pSid = IntPtr.Zero;
                UserPrincipal user = null;

                try
                {
                    pSid = Utils.GetCurrentUserSid();
                    byte[] sid = Utils.ConvertNativeSidToByteArray(pSid);
                    SecurityIdentifier sidObj = new SecurityIdentifier(sid, 0);

                    GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "Current: using SID " + sidObj.ToString());

                    user = UserPrincipal.FindByIdentity(context, IdentityType.Sid, sidObj.ToString());
                }
                finally
                {
                    if (pSid != IntPtr.Zero)
                        System.Runtime.InteropServices.Marshal.FreeHGlobal(pSid);
                }

                // We're running as the user, we know they must exist, but perhaps we don't have access
                // to their user object
                if (user == null)
                {
                    GlobalDebug.WriteLineIf(GlobalDebug.Warn, "User", "Current: found no user");
                    throw new NoMatchingPrincipalException(SR.UserCouldNotFindCurrent);
                }

                return user;
            }
        }

        //
        // Public methods
        //

        public static new PrincipalSearchResult<UserPrincipal> FindByLockoutTime(PrincipalContext context, DateTime time, MatchType type)
        {
            return FindByLockoutTime<UserPrincipal>(context, time, type);
        }

        public static new PrincipalSearchResult<UserPrincipal> FindByLogonTime(PrincipalContext context, DateTime time, MatchType type)
        {
            return FindByLogonTime<UserPrincipal>(context, time, type);
        }

        public static new PrincipalSearchResult<UserPrincipal> FindByExpirationTime(PrincipalContext context, DateTime time, MatchType type)
        {
            return FindByExpirationTime<UserPrincipal>(context, time, type);
        }

        public static new PrincipalSearchResult<UserPrincipal> FindByBadPasswordAttempt(PrincipalContext context, DateTime time, MatchType type)
        {
            return FindByBadPasswordAttempt<UserPrincipal>(context, time, type);
        }

        public static new PrincipalSearchResult<UserPrincipal> FindByPasswordSetTime(PrincipalContext context, DateTime time, MatchType type)
        {
            return FindByPasswordSetTime<UserPrincipal>(context, time, type);
        }

        public static new UserPrincipal FindByIdentity(PrincipalContext context, string identityValue)
        {
            return (UserPrincipal)FindByIdentityWithType(context, typeof(UserPrincipal), identityValue);
        }

        public static new UserPrincipal FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue)
        {
            return (UserPrincipal)FindByIdentityWithType(context, typeof(UserPrincipal), identityType, identityValue);
        }

        public PrincipalSearchResult<Principal> GetAuthorizationGroups()
        {
            return new PrincipalSearchResult<Principal>(GetAuthorizationGroupsHelper());
        }

        //
        // Internal "constructor": Used for constructing Users returned by a query
        //
        internal static UserPrincipal MakeUser(PrincipalContext ctx)
        {
            UserPrincipal u = new UserPrincipal(ctx);
            u.unpersisted = false;

            return u;
        }

        //
        // Private implementation
        //

        private ResultSet GetAuthorizationGroupsHelper()
        {
            // Make sure we're not disposed or deleted.
            CheckDisposedOrDeleted();

            // Unpersisted principals are not members of any group
            if (this.unpersisted)
            {
                GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "GetAuthorizationGroupsHelper: unpersisted, using EmptySet");
                return new EmptySet();
            }

            StoreCtx storeCtx = GetStoreCtxToUse();
            Debug.Assert(storeCtx != null);

            GlobalDebug.WriteLineIf(
                    GlobalDebug.Info,
                    "User",
                    "GetAuthorizationGroupsHelper: retrieving AZ groups from StoreCtx of type=" + storeCtx.GetType().ToString() +
                        ", base path=" + storeCtx.BasePath);

            ResultSet resultSet = storeCtx.GetGroupsMemberOfAZ(this);

            return resultSet;
        }

        //
        // Load/Store implementation
        //

        //
        // Loading with query results
        //
        internal override void LoadValueIntoProperty(string propertyName, object value)
        {
            if (value == null)
            {
                GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "LoadValueIntoProperty: name=" + propertyName + " value= null");
            }
            else
            {
                GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "LoadValueIntoProperty: name=" + propertyName + " value=" + value.ToString());
            }

            switch (propertyName)
            {
                case (PropertyNames.UserGivenName):
                    _givenName = (string)value;
                    _givenNameChanged = LoadState.Loaded;
                    break;

                case (PropertyNames.UserMiddleName):
                    _middleName = (string)value;
                    _middleNameChanged = LoadState.Loaded;
                    break;

                case (PropertyNames.UserSurname):
                    _surname = (string)value;
                    _surnameChanged = LoadState.Loaded;
                    break;

                case (PropertyNames.UserEmailAddress):
                    _emailAddress = (string)value;
                    _emailAddressChanged = LoadState.Loaded;
                    break;

                case (PropertyNames.UserVoiceTelephoneNumber):
                    _voiceTelephoneNumber = (string)value;
                    _voiceTelephoneNumberChanged = LoadState.Loaded;
                    break;

                case (PropertyNames.UserEmployeeID):
                    _employeeID = (string)value;
                    _employeeIDChanged = LoadState.Loaded;
                    break;

                default:
                    base.LoadValueIntoProperty(propertyName, value);
                    break;
            }
        }

        //
        // Getting changes to persist (or to build a query from a QBE filter)
        //

        // Given a property name, returns true if that property has changed since it was loaded, false otherwise.
        internal override bool GetChangeStatusForProperty(string propertyName)
        {
            GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "GetChangeStatusForProperty: name=" + propertyName);

            return propertyName switch
            {
                PropertyNames.UserGivenName => _givenNameChanged == LoadState.Changed,
                PropertyNames.UserMiddleName => _middleNameChanged == LoadState.Changed,
                PropertyNames.UserSurname => _surnameChanged == LoadState.Changed,
                PropertyNames.UserEmailAddress => _emailAddressChanged == LoadState.Changed,
                PropertyNames.UserVoiceTelephoneNumber => _voiceTelephoneNumberChanged == LoadState.Changed,
                PropertyNames.UserEmployeeID => _employeeIDChanged == LoadState.Changed,
                _ => base.GetChangeStatusForProperty(propertyName),
            };
        }

        // Given a property name, returns the current value for the property.
        internal override object GetValueForProperty(string propertyName)
        {
            GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "GetValueForProperty: name=" + propertyName);

            return propertyName switch
            {
                PropertyNames.UserGivenName => _givenName,
                PropertyNames.UserMiddleName => _middleName,
                PropertyNames.UserSurname => _surname,
                PropertyNames.UserEmailAddress => _emailAddress,
                PropertyNames.UserVoiceTelephoneNumber => _voiceTelephoneNumber,
                PropertyNames.UserEmployeeID => _employeeID,
                _ => base.GetValueForProperty(propertyName),
            };
        }

        // Reset all change-tracking status for all properties on the object to "unchanged".
        internal override void ResetAllChangeStatus()
        {
            GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "ResetAllChangeStatus");

            _givenNameChanged = (_givenNameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet;
            _middleNameChanged = (_middleNameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet;
            _surnameChanged = (_surnameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet;
            _emailAddressChanged = (_emailAddressChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet;
            _voiceTelephoneNumberChanged = (_voiceTelephoneNumberChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet;
            _employeeIDChanged = (_employeeIDChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet;

            base.ResetAllChangeStatus();
        }
    }
}