/// <summary>
        /// Processes a request to update the password for a contact.
        /// </summary>
        /// <returns>true if the password was updated successfully; otherwise, false.</returns>
        /// <param name="username">The contact to update the password for. </param>
        /// <param name="oldPassword">The current password for the specified contact. </param>
        /// <param name="newPassword">The new password for the specified contact. </param>
        /// <exception cref="NotSupportedException">Couldn't change password as the CRM provider is in read-only mode.</exception>
        /// <exception cref="NotSupportedException">The CRM provider is not configured to store password.</exception>
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            if (this.initialized)
            {
                if (this.ReadOnly)
                {
                    throw new NotSupportedException("Couldn't change password as the CRM provider is in read-only mode.");
                }

                if (String.IsNullOrEmpty(this.PasswordFieldName))
                {
                    throw new NotSupportedException("The CRM provider is not configured to store password.");
                }

                var password = this.profileRepository.GetUserProperty(username, this.PasswordFieldName) as string;
                if (String.IsNullOrEmpty(password))
                {
                    ConditionalLog.Warn(String.Format("CrmSecurityProvider: user \"{0}\" doesn't have a password. You should reset it before changing.", username), this);
                    return(false);
                }

                SHA1Managed sha1                   = new SHA1Managed();
                byte[]      oldPasswordBytes       = Encoding.UTF8.GetBytes(oldPassword);
                byte[]      hashedOldPasswordBytes = sha1.ComputeHash(oldPasswordBytes);
                string      hashedOldPassword      = System.Convert.ToBase64String(hashedOldPasswordBytes);

                if (password != hashedOldPassword)
                {
                    return(false);
                }

                if (!IsPasswordValid(newPassword))
                {
                    return(false);
                }

                byte[] newPasswordBytes       = Encoding.UTF8.GetBytes(newPassword);
                byte[] hashedNewPasswordBytes = sha1.ComputeHash(newPasswordBytes);
                string hashedNewPassword      = System.Convert.ToBase64String(hashedNewPasswordBytes);

                var properties = new Dictionary <string, object>();
                properties.Add(PasswordFieldName, hashedNewPassword);

                return(this.profileRepository.UpdateUserProperties(username, properties));
            }

            return(false);
        }
        /// <summary>
        /// Processes a request to update the password question and answer for a contact.
        /// </summary>
        /// <returns>true if the password question and answer are updated successfully; otherwise, false.</returns>
        /// <param name="username">The contact to change the password question and answer for. </param>
        /// <param name="password">The password for the specified contact. </param>
        /// <param name="newPasswordQuestion">The new password question for the specified contact. </param>
        /// <param name="newPasswordAnswer">The new password answer for the specified contact. </param>
        /// <exception cref="NotSupportedException">Couldn't change password question or(and) password answer as the CRM provider is in read-only mode.</exception>
        /// <exception cref="NotSupportedException">The CRM provider is not configured to store password.</exception>
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            if (this.initialized)
            {
                if (this.ReadOnly)
                {
                    throw new NotSupportedException("Couldn't change password question or(and) password answer as the CRM provider is in read-only mode.");
                }

                if (String.IsNullOrEmpty(this.PasswordFieldName))
                {
                    throw new NotSupportedException("The CRM provider is not configured to store password.");
                }

                var validPassword = this.profileRepository.GetUserProperty(username, this.PasswordFieldName) as string;
                if (String.IsNullOrEmpty(validPassword))
                {
                    ConditionalLog.Warn(String.Format("CrmSecurityProvider: user \"{0}\" doesn't have a password.", username), this);
                    return(false);
                }

                SHA1Managed sha1                = new SHA1Managed();
                byte[]      passwordBytes       = Encoding.UTF8.GetBytes(password);
                byte[]      hashedPasswordBytes = sha1.ComputeHash(passwordBytes);
                string      hashedPassword      = System.Convert.ToBase64String(hashedPasswordBytes);

                if (validPassword != hashedPassword)
                {
                    return(false);
                }

                var properties = new Dictionary <string, object>();
                properties.Add(this.PasswordQuestionFieldName, newPasswordQuestion);
                properties.Add(this.PasswordAnswerFieldName, newPasswordAnswer);

                return(this.profileRepository.UpdateUserProperties(username, properties));
            }

            return(false);
        }
        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">The friendly name of the provider.</param>
        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
        /// <exception cref="T:System.ArgumentNullException">The name of the provider is null.</exception>
        /// <exception cref="T:System.ArgumentException">The name of the provider has a length of zero.</exception>
        /// <exception cref="T:System.InvalidOperationException">An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"/> on a provider after the provider has already been initialized.</exception>
        public override void Initialize(string name, NameValueCollection config)
        {
            base.Initialize(name, config);

            try
            {
                Error.AssertLicense("Sitecore.MSCRM", "Microsoft Dynamics CRM security provider.");

                this.ApplicationName                      = config["applicationName"];
                this.ReadOnly                             = (String.IsNullOrEmpty(config["readOnly"]) || config["readOnly"] == "true");
                this.AutoCreatePasswordField              = MainUtil.GetBool(config["autoCreatePasswordField"], false);
                this.PasswordFieldName                    = StringUtil.GetString((object)config["passwordFieldName"]);
                this.minRequiredPasswordLength            = MainUtil.GetInt(config["minRequiredPasswordLength"], 7);
                this.minRequiredNonalphanumericCharacters = MainUtil.GetInt(config["minRequiredNonalphanumericCharacters"], 1);
                this.maxInvalidPasswordAttempts           = MainUtil.GetInt(config["maxInvalidPasswordAttempts"], 5);
                this.passwordAttemptWindow                = MainUtil.GetInt(config["passwordAttemptWindow"], 0);
                this.passwordStrengthRegularExpression    = StringUtil.GetString((object)config["passwordStrengthRegularExpression"]).Trim();
                this.requiresUniqueEmail                  = MainUtil.GetBool(config["requiresUniqueEmail"], false);
                this.enablePasswordReset                  = MainUtil.GetBool(config["enablePasswordReset"], true);
                this.requiresQuestionAndAnswer            = MainUtil.GetBool(config["requiresQuestionAndAnswer"], false);
                if (this.RequiresQuestionAndAnswer)
                {
                    this.PasswordQuestionFieldName = config["passwordQuestionFieldName"];
                    this.PasswordAnswerFieldName   = config["passwordAnswerFieldName"];
                }

                if (!String.IsNullOrEmpty(this.passwordStrengthRegularExpression))
                {
                    new Regex(this.passwordStrengthRegularExpression);
                }

                var connectionStringName = config["connectionStringName"];
                var connectionString     = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;

                var settings = Configuration.ConfigurationSettings.Parse(connectionString);

                this.userRepository    = this.userRepositoryFactory.GetRepository(settings);
                this.profileRepository = this.profileRepositoryFactory.GetRepository(settings);
                this.initialized       = true;
            }
            catch (Exception e)
            {
                this.initialized = false;
                ConditionalLog.Error("The CRM provider couldn't be initialized.", e, this);

                return;
            }

            try
            {
                if (!String.IsNullOrEmpty(this.PasswordFieldName))
                {
                    var passwordFieldType = this.profileRepository.GetPropertyType(this.PasswordFieldName);
                    if (passwordFieldType != SupportedTypes.String)
                    {
                        this.PasswordFieldName = String.Empty;
                        ConditionalLog.Warn("The attribute for the password storage isn't of String type. Password storage feature disabled.", this);
                    }
                }
            }
            catch (ProviderException)
            {
                if ((this.userRepository is UserRepositoryV4 || this.userRepository is UserRepositoryV5) && this.AutoCreatePasswordField)
                {
                    if (!this.profileRepository.CreateContactAttribute(this.PasswordFieldName, SupportedTypes.String, false))
                    {
                        this.PasswordFieldName = String.Empty;
                        ConditionalLog.Warn("The attribute for password storage couldn't be created. Password storage feature disabled.", this);
                    }
                }
                else
                {
                    this.PasswordFieldName = String.Empty;
                    ConditionalLog.Warn("The attribute for the password storage doesn't exist. Password storage feature disabled.", this);
                }
            }
        }