Esempio n. 1
0
 private static void UpdateName(MemberSave memberSave)
 {
     //Don't update the name if it is empty
     if (memberSave.Name.IsNullOrWhiteSpace() == false)
     {
         memberSave.PersistedContent.Name = memberSave.Name;
     }
 }
        /// <summary>
        /// Maps the property values to the persisted entity
        /// </summary>
        /// <param name="contentItem"></param>
        private void MapPropertyValues(MemberSave contentItem)
        {
            UpdateName(contentItem);

            //map the custom properties - this will already be set for new entities in our member binder
            contentItem.PersistedContent.Email    = contentItem.Email;
            contentItem.PersistedContent.Username = contentItem.Username;

            //use the base method to map the rest of the properties
            base.MapPropertyValues(contentItem);
        }
        internal bool ValidateUniqueEmail(MemberSave model, MembershipProvider membershipProvider)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }
            if (membershipProvider == null)
            {
                throw new ArgumentNullException(nameof(membershipProvider));
            }

            if (membershipProvider.RequiresUniqueEmail == false)
            {
                return(true);
            }

            int totalRecs;
            var existingByEmail = membershipProvider.FindUsersByEmail(model.Email.Trim(), 0, int.MaxValue, out totalRecs);

            switch (model.Action)
            {
            case ContentSaveAction.Save:
                //ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ?
                if (model.PersistedContent.Email.InvariantEquals(model.Email.Trim()) == false)
                {
                    //they are changing their email
                    if (existingByEmail.Cast <MembershipUser>().Select(x => x.Email)
                        .Any(x => x.InvariantEquals(model.Email.Trim())))
                    {
                        //the user cannot use this email
                        return(false);
                    }
                }
                break;

            case ContentSaveAction.SaveNew:
                //check if the user's email already exists
                if (existingByEmail.Cast <MembershipUser>().Select(x => x.Email)
                    .Any(x => x.InvariantEquals(model.Email.Trim())))
                {
                    //the user cannot use this email
                    return(false);
                }
                break;

            default:
                //we don't support this for members
                throw new ArgumentOutOfRangeException();
            }

            return(true);
        }
Esempio n. 4
0
        /// <summary>
        /// Gets an instance of IMember used when creating a member
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <remarks>
        /// Depending on whether a custom membership provider is configured this will return different results.
        /// </remarks>
        private IMember CreateNew(MemberSave model)
        {
            var contentType = _memberTypeService.Get(model.ContentTypeAlias);

            if (contentType == null)
            {
                throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
            }

            //remove all membership properties, these values are set with the membership provider.
            FilterMembershipProviderProperties(contentType);

            //return the new member with the details filled in
            return(new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType));
        }
        /// <summary>
        /// Re-fetches the database data to map to the PersistedContent object and re-assigns the already mapped the posted properties so that the display object is up-to-date
        /// </summary>
        /// <param name="contentItem"></param>
        /// <param name="lookup"></param>
        /// <remarks>
        /// This is done during an update if the membership provider has changed some underlying data - we need to ensure that our model is consistent with that data
        /// </remarks>
        private void RefetchMemberData(MemberSave contentItem, LookupType lookup)
        {
            var currProps = contentItem.PersistedContent.Properties.ToArray();

            switch (MembershipScenario)
            {
            case MembershipScenario.NativeUmbraco:
                switch (lookup)
                {
                case LookupType.ByKey:
                    //Go and re-fetch the persisted item
                    contentItem.PersistedContent = Services.MemberService.GetByKey(contentItem.Key);
                    break;

                case LookupType.ByUserName:
                    contentItem.PersistedContent = Services.MemberService.GetByUsername(contentItem.Username.Trim());
                    break;
                }
                break;

            case MembershipScenario.CustomProviderWithUmbracoLink:
            case MembershipScenario.StandaloneCustomProvider:
            default:
                var membershipUser = _provider.GetUser(contentItem.Key, false);
                //Go and re-fetch the persisted item
                contentItem.PersistedContent = Mapper.Map <MembershipUser, IMember>(membershipUser);
                break;
            }

            UpdateName(contentItem);

            //re-assign the mapped values that are not part of the membership provider properties.
            var builtInAliases = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray();

            foreach (var p in contentItem.PersistedContent.Properties)
            {
                var valueMapped = currProps.FirstOrDefault(x => x.Alias == p.Alias);
                if (builtInAliases.Contains(p.Alias) == false && valueMapped != null)
                {
                    p.SetValue(valueMapped.GetValue());

                    // fixme - ok, I give up, at that point tags are dead here, until we figure it out
                    //p.TagChanges.Behavior = valueMapped.TagChanges.Behavior;
                    //p.TagChanges.Enable = valueMapped.TagChanges.Enable;
                    //p.TagChanges.Tags = valueMapped.TagChanges.Tags;
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Returns an IMember instance used to bind values to and save (depending on the membership scenario)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private IMember GetExisting(MemberSave model)
        {
            var scenario = _services.MemberService.GetMembershipScenario();
            var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();

            switch (scenario)
            {
            case MembershipScenario.NativeUmbraco:
                return(GetExisting(model.Key));

            case MembershipScenario.CustomProviderWithUmbracoLink:
            case MembershipScenario.StandaloneCustomProvider:
            default:
                var membershipUser = provider.GetUser(model.Key, false);
                if (membershipUser == null)
                {
                    throw new InvalidOperationException("Could not find member with key " + model.Key);
                }

                // TODO: Support this scenario!
                //if (scenario == MembershipScenario.CustomProviderWithUmbracoLink)
                //{
                //    //if there's a 'Member' type then we should be able to just go get it from the db since it was created with a link
                //    // to our data.
                //    var memberType = ApplicationContext.Services.MemberTypeService.GetMemberType(Constants.Conventions.MemberTypes.Member);
                //    if (memberType != null)
                //    {
                //        var existing = GetExisting(model.Key);
                //        FilterContentTypeProperties(existing.ContentType, existing.ContentType.PropertyTypes.Select(x => x.Alias).ToArray());
                //    }
                //}

                //generate a member for a generic membership provider
                //NOTE: We don't care about the password here, so just generate something
                //var member = MemberService.CreateGenericMembershipProviderMember(model.Name, model.Email, model.Username, Guid.NewGuid().ToString("N"));

                //var convertResult = membershipUser.ProviderUserKey.TryConvertTo<Guid>();
                //if (convertResult.Success == false)
                //{
                //    throw new InvalidOperationException("Only membership providers that store a GUID as their ProviderUserKey are supported" + model.Key);
                //}
                //member.Key = convertResult.Result;

                var member = Current.Mapper.Map <MembershipUser, IMember>(membershipUser);

                return(member);
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Maps the property values to the persisted entity
        /// </summary>
        /// <param name="contentItem"></param>
        private void MapPropertyValues(MemberSave contentItem)
        {
            UpdateName(contentItem);

            //map the custom properties - this will already be set for new entities in our member binder
            contentItem.PersistedContent.Email    = contentItem.Email;
            contentItem.PersistedContent.Username = contentItem.Username;

            //use the base method to map the rest of the properties
            base.MapPropertyValuesForPersistence <IMember, MemberSave>(
                contentItem,
                contentItem.PropertyCollectionDto,
                (save, property) => property.GetValue(),     //get prop val
                (save, property, v) => property.SetValue(v), //set prop val
                null);                                       // member are all invariant
        }
        private async Task <bool> ValidateMemberDataAsync(MemberSave contentItem)
        {
            if (contentItem.Name.IsNullOrWhiteSpace())
            {
                ModelState.AddPropertyError(
                    new ValidationResult("Invalid user name", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login");
                return(false);
            }

            if (contentItem.Password != null && !contentItem.Password.NewPassword.IsNullOrWhiteSpace())
            {
                IdentityResult validPassword = await _memberManager.ValidatePasswordAsync(contentItem.Password.NewPassword);

                if (!validPassword.Succeeded)
                {
                    ModelState.AddPropertyError(
                        new ValidationResult("Invalid password: "******"value" }),
                        $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password");
                    return(false);
                }
            }

            IMember?byUsername = _memberService.GetByUsername(contentItem.Username);

            if (byUsername != null && byUsername.Key != contentItem.Key)
            {
                ModelState.AddPropertyError(
                    new ValidationResult("Username is already in use", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login");
                return(false);
            }

            IMember?byEmail = _memberService.GetByEmail(contentItem.Email);

            if (byEmail != null && byEmail.Key != contentItem.Key)
            {
                ModelState.AddPropertyError(
                    new ValidationResult("Email address is already in use", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email");
                return(false);
            }

            return(true);
        }
Esempio n. 9
0
    internal bool ValidateUniqueLogin(MemberSave model)
    {
        if (model == null)
        {
            throw new ArgumentNullException(nameof(model));
        }

        IMember?existingByName = _memberService.GetByUsername(model.Username.Trim());

        switch (model.Action)
        {
        case ContentSaveAction.Save:

            //ok, we're updating the member, we need to check if they are changing their login and if so, does it exist already ?
            if (model.PersistedContent?.Username.InvariantEquals(model.Username.Trim()) == false)
            {
                //they are changing their login name
                if (existingByName != null && existingByName.Username == model.Username.Trim())
                {
                    //the user cannot use this login
                    return(false);
                }
            }

            break;

        case ContentSaveAction.SaveNew:
            //check if the user's login already exists
            if (existingByName != null && existingByName.Username == model.Username.Trim())
            {
                //the user cannot use this login
                return(false);
            }

            break;

        default:
            //we don't support this for members
            throw new ArgumentOutOfRangeException();
        }

        return(true);
    }
        internal bool ValidateUniqueEmail(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext)
        {
            if (contentItem == null) throw new ArgumentNullException("contentItem");
            if (membershipProvider == null) throw new ArgumentNullException("membershipProvider");

            if (membershipProvider.RequiresUniqueEmail == false)
            {
                return true;
            }

            int totalRecs;
            var existingByEmail = membershipProvider.FindUsersByEmail(contentItem.Email.Trim(), 0, int.MaxValue, out totalRecs);            
            switch (contentItem.Action)
            {
                case ContentSaveAction.Save:
                    //ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ?
                    if (contentItem.PersistedContent.Email.InvariantEquals(contentItem.Email.Trim()) == false)
                    {
                        //they are changing their email
                        if (existingByEmail.Cast<MembershipUser>().Select(x => x.Email)
                                           .Any(x => x.InvariantEquals(contentItem.Email.Trim())))
                        {
                            //the user cannot use this email
                            return false;
                        }
                    }
                    break;
                case ContentSaveAction.SaveNew:
                    //check if the user's email already exists
                    if (existingByEmail.Cast<MembershipUser>().Select(x => x.Email)
                                       .Any(x => x.InvariantEquals(contentItem.Email.Trim())))
                    {
                        //the user cannot use this email
                        return false;
                    }
                    break;
                default:
                    //we don't support this for members
                    throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            return true;
        }
Esempio n. 11
0
    private static void Map(MemberSave source, IMember target, MapperContext context)
    {
        target.IsApproved = source.IsApproved;
        target.Name       = source.Name;
        target.Email      = source.Email;
        target.Key        = source.Key;
        target.Username   = source.Username;
        target.Comments   = source.Comments;
        target.CreateDate = source.CreateDate;
        target.UpdateDate = source.UpdateDate;
        target.Email      = source.Email;

        // TODO: ensure all properties are mapped as required
        // target.Id = source.Id;
        // target.ParentId = -1;
        // target.Path = "-1," + source.Id;

        // TODO: add groups as required
    }
Esempio n. 12
0
        /// <summary>
        /// Gets an instance of IMember used when creating a member
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <remarks>
        /// Depending on whether a custom membership provider is configured this will return different results.
        /// </remarks>
        private IMember CreateNew(MemberSave model)
        {
            var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();

            if (provider.IsUmbracoMembershipProvider())
            {
                var contentType = _services.MemberTypeService.Get(model.ContentTypeAlias);
                if (contentType == null)
                {
                    throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
                }

                //remove all membership properties, these values are set with the membership provider.
                FilterMembershipProviderProperties(contentType);

                //return the new member with the details filled in
                return(new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType));
            }
            else
            {
                //A custom membership provider is configured

                //NOTE: Below we are assigning the password to just a new GUID because we are not actually storing the password, however that
                // field is mandatory in the database so we need to put something there.

                //If the default Member type exists, we'll use that to create the IMember - that way we can associate the custom membership
                // provider to our data - eventually we can support editing custom properties with a custom provider.
                var memberType = _services.MemberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
                if (memberType != null)
                {
                    FilterContentTypeProperties(memberType, memberType.PropertyTypes.Select(x => x.Alias).ToArray());
                    return(new Member(model.Name, model.Email, model.Username, Guid.NewGuid().ToString("N"), memberType));
                }

                //generate a member for a generic membership provider
                var member = MemberService.CreateGenericMembershipProviderMember(model.Name, model.Email, model.Username, Guid.NewGuid().ToString("N"));
                //we'll just remove all properties here otherwise we'll end up with validation errors, we don't want to persist any property data anyways
                // in this case.
                memberType = _services.MemberTypeService.Get(member.ContentTypeId);
                FilterContentTypeProperties(memberType, memberType.PropertyTypes.Select(x => x.Alias).ToArray());
                return(member);
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Maps the property values to the persisted entity
        /// </summary>
        /// <param name="contentItem">The member content item to map properties from</param>
        private void MapPropertyValues(MemberSave contentItem)
        {
            // Don't update the name if it is empty
            if (contentItem.Name.IsNullOrWhiteSpace() == false)
            {
                contentItem.PersistedContent.Name = contentItem.Name;
            }

            // map the custom properties - this will already be set for new entities in our member binder
            contentItem.PersistedContent.IsApproved = contentItem.IsApproved;
            contentItem.PersistedContent.Email      = contentItem.Email.Trim();
            contentItem.PersistedContent.Username   = contentItem.Username;

            // use the base method to map the rest of the properties
            MapPropertyValuesForPersistence <IMember, MemberSave>(
                contentItem,
                contentItem.PropertyCollectionDto,
                (save, property) => property.GetValue(),     // get prop val
                (save, property, v) => property.SetValue(v), // set prop val
                null);                                       // member are all invariant
        }
        /// <summary>
        /// We need to manually validate a few things here like email and login to make sure they are valid and aren't duplicates
        /// </summary>
        /// <param name="model"></param>
        /// <param name="dto"></param>
        /// <param name="modelState"></param>
        /// <param name="modelWithProperties"></param>
        /// <returns></returns>
        public override bool ValidatePropertiesData(MemberSave model, IContentProperties <ContentPropertyBasic> modelWithProperties, ContentPropertyCollectionDto dto, ModelStateDictionary modelState)
        {
            if (model.Username.IsNullOrWhiteSpace())
            {
                modelState.AddPropertyError(
                    new ValidationResult("Invalid user name", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login");
            }

            if (model.Email.IsNullOrWhiteSpace() || new EmailAddressAttribute().IsValid(model.Email) == false)
            {
                modelState.AddPropertyError(
                    new ValidationResult("Invalid email", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email");
            }

            //default provider!
            var membershipProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();

            var validEmail = ValidateUniqueEmail(model, membershipProvider);

            if (validEmail == false)
            {
                modelState.AddPropertyError(
                    new ValidationResult("Email address is already in use", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email");
            }

            var validLogin = ValidateUniqueLogin(model, membershipProvider);

            if (validLogin == false)
            {
                modelState.AddPropertyError(
                    new ValidationResult("Username is already in use", new[] { "value" }),
                    $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login");
            }

            return(base.ValidatePropertiesData(model, modelWithProperties, dto, modelState));
        }
        /// <summary>
        /// This ensures that the internal membership property types are removed from validation before processing the validation
        /// since those properties are actually mapped to real properties of the IMember.
        /// This also validates any posted data for fields that are sensitive.
        /// </summary>
        /// <param name="model"></param>
        /// <param name="modelWithProperties"></param>
        /// <param name="actionContext"></param>
        /// <returns></returns>
        public override bool ValidateProperties(MemberSave model, IContentProperties <ContentPropertyBasic> modelWithProperties, HttpActionContext actionContext)
        {
            var propertiesToValidate = model.Properties.ToList();
            var defaultProps         = ConventionsHelper.GetStandardPropertyTypeStubs();
            var exclude = defaultProps.Select(x => x.Value.Alias).ToArray();

            foreach (var remove in exclude)
            {
                propertiesToValidate.RemoveAll(property => property.Alias == remove);
            }

            //if the user doesn't have access to sensitive values, then we need to validate the incoming properties to check
            //if a sensitive value is being submitted.
            if (UmbracoContextAccessor.UmbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
            {
                var contentType         = _memberTypeService.Get(model.PersistedContent.ContentTypeId);
                var sensitiveProperties = contentType
                                          .PropertyTypes.Where(x => contentType.IsSensitiveProperty(x.Alias))
                                          .ToList();

                foreach (var sensitiveProperty in sensitiveProperties)
                {
                    var prop = propertiesToValidate.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias);

                    if (prop != null)
                    {
                        //this should not happen, this means that there was data posted for a sensitive property that
                        //the user doesn't have access to, which means that someone is trying to hack the values.

                        var message = $"property with alias: {prop.Alias} cannot be posted";
                        actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, new InvalidOperationException(message));
                        return(false);
                    }
                }
            }

            return(ValidateProperties(propertiesToValidate, model.PersistedContent.Properties.ToList(), actionContext));
        }
Esempio n. 16
0
 /// <summary>
 /// Quick check to see if the 'normal' settable properties for the membership provider have changed
 /// </summary>
 /// <param name="membershipUser"></param>
 /// <param name="contentItem"></param>
 /// <returns></returns>
 /// <remarks>
 /// By 'normal' we mean that they can simply be set on the membership user and don't require method calls like ChangePassword or UnlockUser
 /// </remarks>
 private bool HasMembershipUserChanged(MembershipUser membershipUser, MemberSave contentItem)
 {
     if (contentItem.Email.Trim().InvariantEquals(membershipUser.Email) == false
         || contentItem.IsApproved != membershipUser.IsApproved
         || contentItem.Comments != membershipUser.Comment)
     {
         return true;
     }
     return false;
 }
Esempio n. 17
0
        /// <summary>
        /// This is going to create the user with the membership provider and check for validation
        /// </summary>
        /// <param name="contentItem"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        /// <remarks>
        /// Depending on if the Umbraco membership provider is active or not, the process differs slightly:
        /// 
        /// * If the umbraco membership provider is used - we create the membership user first with the membership provider, since 
        ///     it's the umbraco membership provider, this writes to the umbraco tables. When that is complete we re-fetch the IMember
        ///     model data from the db. In this case we don't care what the provider user key is.
        /// * If we're using a non-umbraco membership provider - we check if there is a 'Member' member type - if so 
        ///     we create an empty IMember instance first (of type 'Member'), this gives us a unique ID (GUID)
        ///     that we then use to create the member in the custom membership provider. This acts as the link between Umbraco data and 
        ///     the custom membership provider data. This gives us the ability to eventually have custom membership properties but still use
        ///     a custom memberhip provider. If there is no 'Member' member type, then we will simply just create the membership provider member
        ///     with no link to our data.
        /// 
        /// If this is successful, it will go and re-fetch the IMember from the db because it will now have an ID because the Umbraco provider 
        /// uses the umbraco data store - then of course we need to re-map it to the saved property values.
        /// </remarks>
        private MembershipUser CreateWithMembershipProvider(MemberSave contentItem, out MembershipCreateStatus status)
        {
            MembershipUser membershipUser;

            switch (MembershipScenario)
            {
                case MembershipScenario.NativeUmbraco:
                    //We are using the umbraco membership provider, create the member using the membership provider first.
                    var umbracoMembershipProvider = (global::umbraco.providers.members.UmbracoMembershipProvider)Membership.Provider;
                    //TODO: We are not supporting q/a - passing in empty here
                    membershipUser = umbracoMembershipProvider.CreateUser(
                        contentItem.ContentTypeAlias, contentItem.Username,
                        contentItem.Password.NewPassword,
                        contentItem.Email, "", "",
                        contentItem.IsApproved,
                        Guid.NewGuid(), //since it's the umbraco provider, the user key here doesn't make any difference
                        out status);
                    break;
                case MembershipScenario.CustomProviderWithUmbracoLink:
                    //We are using a custom membership provider, we'll create an empty IMember first to get the unique id to use
                    // as the provider user key.                    
                    //create it - this persisted item has already been set in the MemberBinder based on the 'Member' member type:
                    Services.MemberService.Save(contentItem.PersistedContent);

                    //TODO: We are not supporting q/a - passing in empty here
                    membershipUser = Membership.CreateUser(
                        contentItem.Username,
                        contentItem.Password.NewPassword,
                        contentItem.Email,
                        "TEMP", //some membership provider's require something here even if q/a is disabled!
                        "TEMP", //some membership provider's require something here even if q/a is disabled!
                        contentItem.IsApproved,
                        contentItem.PersistedContent.Key, //custom membership provider, we'll link that based on the IMember unique id (GUID)
                        out status);

                    break;
                case MembershipScenario.StandaloneCustomProvider:
                    // we don't have a member type to use so we will just create the basic membership user with the provider with no
                    // link back to the umbraco data
                   
                    var newKey = Guid.NewGuid();
                    //TODO: We are not supporting q/a - passing in empty here
                    membershipUser = Membership.CreateUser(
                        contentItem.Username,
                        contentItem.Password.NewPassword,
                        contentItem.Email,
                        "TEMP", //some membership provider's require something here even if q/a is disabled!
                        "TEMP", //some membership provider's require something here even if q/a is disabled!
                        contentItem.IsApproved,
                        newKey, 
                        out status);

                    //we need to set the key back on the PersistedContent property so that the display model is returned correctly
                    contentItem.PersistedContent.Key = newKey;

                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            //TODO: Localize these!
            switch (status)
            {
                case MembershipCreateStatus.Success:

                    //if the comments are there then we need to save them
                    if (contentItem.Comments.IsNullOrWhiteSpace() == false)
                    {
                        membershipUser.Comment = contentItem.Comments;
                        Membership.UpdateUser(membershipUser);
                    }

                    //if we're using the umbraco provider, we'll have to go re-fetch the IMember since the provider
                    // has now updated it separately but we need to maintain all the correct bound data
                    if (MembershipScenario == MembershipScenario.NativeUmbraco)
                    {
                        //Go and re-fetch the persisted item
                        contentItem.PersistedContent = Services.MemberService.GetByUsername(contentItem.Username.Trim());
                        //remap the values to save
                        MapPropertyValues(contentItem);
                    }


                    break;
                case MembershipCreateStatus.InvalidUserName:
                    ModelState.AddPropertyError(
                        new ValidationResult("Invalid user name", new[] { "value" }),
                        string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                    break;
                case MembershipCreateStatus.InvalidPassword:
                    ModelState.AddPropertyError(
                        new ValidationResult("Invalid password", new[] { "value" }),
                        string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                    break;
                case MembershipCreateStatus.InvalidQuestion:
                case MembershipCreateStatus.InvalidAnswer:
                    throw new NotSupportedException("Currently the member editor does not support providers that have RequiresQuestionAndAnswer specified");
                case MembershipCreateStatus.InvalidEmail:
                    ModelState.AddPropertyError(
                        new ValidationResult("Invalid email", new[] { "value" }),
                        string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                    break;
                case MembershipCreateStatus.DuplicateUserName:
                    ModelState.AddPropertyError(
                        new ValidationResult("Username is already in use", new[] { "value" }),
                        string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                    break;
                case MembershipCreateStatus.DuplicateEmail:
                    ModelState.AddPropertyError(
                        new ValidationResult("Email address is already in use", new[] { "value" }),
                        string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                    break;
                case MembershipCreateStatus.InvalidProviderUserKey:
                    ModelState.AddPropertyError(
                        //specify 'default' just so that it shows up as a notification - is not assigned to a property
                       new ValidationResult("Invalid provider user key"), "default");
                    break;
                case MembershipCreateStatus.DuplicateProviderUserKey:
                    ModelState.AddPropertyError(
                        //specify 'default' just so that it shows up as a notification - is not assigned to a property
                       new ValidationResult("Duplicate provider user key"), "default");
                    break;
                case MembershipCreateStatus.ProviderError:
                case MembershipCreateStatus.UserRejected:
                    ModelState.AddPropertyError(
                        //specify 'default' just so that it shows up as a notification - is not assigned to a property
                        new ValidationResult("User could not be created (rejected by provider)"), "default");
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            return membershipUser;
        }
Esempio n. 18
0
 /// <summary>
 /// Returns an IMember instance used to bind values to and save (depending on the membership scenario)
 /// </summary>
 /// <param name="model"></param>
 /// <returns></returns>
 private IMember GetExisting(MemberSave model)
 {
     return(GetExisting(model.Key));
 }
Esempio n. 19
0
    /// <summary>
    ///     Setup all standard member data for test
    /// </summary>
    private Member SetupMemberTestData(
        out MemberSave fakeMemberData,
        out MemberDisplay memberDisplay,
        ContentSaveAction contentAction)
    {
        // arrange
        var memberType = MemberTypeBuilder.CreateSimpleMemberType();
        var member     = MemberBuilder.CreateSimpleMember(memberType, "Test Member", "*****@*****.**", "123", "test");
        var memberId   = 123;

        member.Id = memberId;

        // TODO: replace with builder for MemberSave and MemberDisplay
        fakeMemberData = new MemberSave
        {
            Id            = memberId,
            SortOrder     = member.SortOrder,
            ContentTypeId = memberType.Id,
            Key           = member.Key,
            Password      = new ChangingPasswordModel {
                Id = 456, NewPassword = member.RawPasswordValue, OldPassword = null
            },
            Name                  = member.Name,
            Email                 = member.Email,
            Username              = member.Username,
            PersistedContent      = member,
            PropertyCollectionDto = new ContentPropertyCollectionDto(),
            Groups                = new List <string>(),

            // Alias = "fakeAlias",
            ContentTypeAlias = member.ContentTypeAlias,
            Action           = contentAction,
            Icon             = "icon-document",
            Path             = member.Path,
        };

        memberDisplay = new MemberDisplay
        {
            Id            = memberId,
            SortOrder     = member.SortOrder,
            ContentTypeId = memberType.Id,
            Key           = member.Key,
            Name          = member.Name,
            Email         = member.Email,
            Username      = member.Username,

            // Alias = "fakeAlias",
            ContentTypeAlias = member.ContentTypeAlias,
            ContentType      = new ContentTypeBasic(),
            ContentTypeName  = member.ContentType.Name,
            Icon             = fakeMemberData.Icon,
            Path             = member.Path,
            Tabs             = new List <Tab <ContentPropertyDisplay> >
            {
                new()
                {
                    Alias      = "test",
                    Id         = 77,
                    Properties = new List <ContentPropertyDisplay>
                    {
                        new() { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login" },
                        new() { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email" },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}membergroup",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}failedPasswordAttempts",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}approved",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}lockedOut",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}lastLockoutDate",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}lastLoginDate",
                        },
                        new()
                        {
                            Alias =
                                $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}lastPasswordChangeDate",
                        },
                    },
                },
            },
        };

        return(member);
    }
Esempio n. 20
0
 /// <summary>
 /// Re-fetches the database data to map to the PersistedContent object and re-maps the posted properties so that the display object is up-to-date
 /// </summary>
 /// <param name="contentItem"></param>
 /// <remarks>
 /// This is done during an update if the membership provider has changed some underlying data - we need to ensure that our model is consistent with that data
 /// </remarks>
 private void RefetchMemberData(MemberSave contentItem)
 {
     switch (MembershipScenario)
     {
         case MembershipScenario.NativeUmbraco:
             //Go and re-fetch the persisted item
             contentItem.PersistedContent = Services.MemberService.GetByKey(contentItem.Key);
             //remap the values to save
             MapPropertyValues(contentItem);
             break;
         case MembershipScenario.CustomProviderWithUmbracoLink:
         case MembershipScenario.StandaloneCustomProvider:
         default:
             var membershipUser = Membership.GetUser(contentItem.Key, false);
             //Go and re-fetch the persisted item
             contentItem.PersistedContent = Mapper.Map<MembershipUser, IMember>(membershipUser);
             //remap the values to save
             MapPropertyValues(contentItem);
             break;
     }
 }
Esempio n. 21
0
 /// <summary>
 /// Following a refresh of member data called during an update if the membership provider has changed some underlying data,
 /// we don't want to lose the provided, and potentially changed, username
 /// </summary>
 /// <param name="contentItem"></param>
 /// <param name="providedUserName"></param>
 private static void RestoreProvidedUserName(MemberSave contentItem, string providedUserName)
 {
     contentItem.PersistedContent.Username = providedUserName;
 }
        public async Task <ActionResult <MemberDisplay?> > PostSave([ModelBinder(typeof(MemberBinder))] MemberSave contentItem)
        {
            if (contentItem == null)
            {
                throw new ArgumentNullException("The member content item was null");
            }

            // If we've reached here it means:
            // * Our model has been bound
            // * and validated
            // * any file attachments have been saved to their temporary location for us to use
            // * we have a reference to the DTO object and the persisted object
            // * Permissions are valid

            // map the properties to the persisted entity
            MapPropertyValues(contentItem);

            await ValidateMemberDataAsync(contentItem);

            // Unlike content/media - if there are errors for a member, we do NOT proceed to save them, we cannot so return the errors
            if (ModelState.IsValid == false)
            {
                MemberDisplay?forDisplay = _umbracoMapper.Map <MemberDisplay>(contentItem.PersistedContent);
                return(ValidationProblem(forDisplay, ModelState));
            }

            // Create a scope here which will wrap all child data operations in a single transaction.
            // We'll complete this at the end of this method if everything succeeeds, else
            // all data operations will roll back.
            using ICoreScope scope = _scopeProvider.CreateCoreScope();

            // Depending on the action we need to first do a create or update using the membership manager
            // this ensures that passwords are formatted correctly and also performs the validation on the provider itself.
            switch (contentItem.Action)
            {
            case ContentSaveAction.Save:
                ActionResult <bool> updateSuccessful = await UpdateMemberAsync(contentItem);

                if (!(updateSuccessful.Result is null))
                {
                    return(updateSuccessful.Result);
                }

                break;

            case ContentSaveAction.SaveNew:
                ActionResult <bool> createSuccessful = await CreateMemberAsync(contentItem);

                if (!(createSuccessful.Result is null))
                {
                    return(createSuccessful.Result);
                }

                break;

            default:
                // we don't support anything else for members
                return(NotFound());
            }

            // return the updated model
            MemberDisplay?display = _umbracoMapper.Map <MemberDisplay>(contentItem.PersistedContent);

            // lastly, if it is not valid, add the model state to the outgoing object and throw a 403
            if (!ModelState.IsValid)
            {
                return(ValidationProblem(display, ModelState, StatusCodes.Status403Forbidden));
            }

            // put the correct messages in
            switch (contentItem.Action)
            {
            case ContentSaveAction.Save:
            case ContentSaveAction.SaveNew:
                display?.AddSuccessNotification(
                    _localizedTextService.Localize("speechBubbles", "editMemberSaved"),
                    _localizedTextService.Localize("speechBubbles", "editMemberSaved"));
                break;
            }

            // Mark transaction to commit all changes
            scope.Complete();

            return(display);
        }
        /// <summary>
        /// Update existing member data
        /// </summary>
        /// <param name="contentItem">The member to save</param>
        /// <remarks>
        /// We need to use both IMemberService and ASP.NET Identity to do our updates because Identity is responsible for passwords/security.
        /// When this method is called, the IMember will already have updated/mapped values from the http POST.
        /// So then we do this in order:
        /// 1. Deal with sensitive property values on IMember
        /// 2. Use IMemberService to persist all changes
        /// 3. Use ASP.NET and MemberUserManager to deal with lockouts
        /// 4. Use ASP.NET, MemberUserManager and password changer to deal with passwords
        /// 5. Deal with groups/roles
        /// </remarks>
        private async Task <ActionResult <bool> > UpdateMemberAsync(MemberSave contentItem)
        {
            if (contentItem.PersistedContent is not null)
            {
                contentItem.PersistedContent.WriterId = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1;
            }

            // If the user doesn't have access to sensitive values, then we need to check if any of the built in member property types
            // have been marked as sensitive. If that is the case we cannot change these persisted values no matter what value has been posted.
            // There's only 3 special ones we need to deal with that are part of the MemberSave instance: Comments, IsApproved, IsLockedOut
            // but we will take care of this in a generic way below so that it works for all props.
            if (!_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.HasAccessToSensitiveData() ?? true)
            {
                IMemberType?memberType          = contentItem.PersistedContent is null ? null : _memberTypeService.Get(contentItem.PersistedContent.ContentTypeId);
                var         sensitiveProperties = memberType?
                                                  .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias))
                                                  .ToList();

                if (sensitiveProperties is not null)
                {
                    foreach (IPropertyType sensitiveProperty in sensitiveProperties)
                    {
                        // TODO: This logic seems to deviate from the logic that is in v8 where we are explitly checking
                        // against 3 properties: Comments, IsApproved, IsLockedOut, is the v8 version incorrect?

                        ContentPropertyBasic?destProp = contentItem.Properties.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias);
                        if (destProp != null)
                        {
                            // if found, change the value of the contentItem model to the persisted value so it remains unchanged
                            object?origValue = contentItem.PersistedContent?.GetValue(sensitiveProperty.Alias);
                            destProp.Value = origValue;
                        }
                    }
                }
            }

            if (contentItem.PersistedContent is not null)
            {
                // First save the IMember with mapped values before we start updating data with aspnet identity
                _memberService.Save(contentItem.PersistedContent);
            }

            bool needsResync = false;

            MemberIdentityUser identityMember = await _memberManager.FindByIdAsync(contentItem.Id?.ToString());

            if (identityMember == null)
            {
                return(ValidationProblem("Member was not found"));
            }

            // Handle unlocking with the member manager (takes care of other nuances)
            if (identityMember.IsLockedOut && contentItem.IsLockedOut == false)
            {
                IdentityResult unlockResult = await _memberManager.SetLockoutEndDateAsync(identityMember, DateTimeOffset.Now.AddMinutes(-1));

                if (unlockResult.Succeeded == false)
                {
                    return(ValidationProblem(
                               $"Could not unlock for member {contentItem.Id} - error {unlockResult.Errors.ToErrorMessage()}"));
                }
                needsResync = true;
            }
            else if (identityMember.IsLockedOut == false && contentItem.IsLockedOut)
            {
                // NOTE: This should not ever happen unless someone is mucking around with the request data.
                // An admin cannot simply lock a user, they get locked out by password attempts, but an admin can unlock them
                return(ValidationProblem("An admin cannot lock a member"));
            }

            // If we're changing the password...
            // Handle changing with the member manager & password changer (takes care of other nuances)
            if (contentItem.Password != null)
            {
                IdentityResult validatePassword = await _memberManager.ValidatePasswordAsync(contentItem.Password.NewPassword);

                if (validatePassword.Succeeded == false)
                {
                    return(ValidationProblem(validatePassword.Errors.ToErrorMessage()));
                }

                if (!int.TryParse(identityMember.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intId))
                {
                    return(ValidationProblem("Member ID was not valid"));
                }

                var changingPasswordModel = new ChangingPasswordModel
                {
                    Id          = intId,
                    OldPassword = contentItem.Password.OldPassword,
                    NewPassword = contentItem.Password.NewPassword,
                };

                // Change and persist the password
                Attempt <PasswordChangedModel?> passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _memberManager);

                if (!passwordChangeResult.Success)
                {
                    foreach (string memberName in passwordChangeResult.Result?.ChangeError?.MemberNames ?? Enumerable.Empty <string>())
                    {
                        ModelState.AddModelError(memberName, passwordChangeResult.Result?.ChangeError?.ErrorMessage ?? string.Empty);
                    }
                    return(ValidationProblem(ModelState));
                }

                needsResync = true;
            }

            // Update the roles and check for changes
            ActionResult <bool> rolesChanged = await AddOrUpdateRoles(contentItem.Groups, identityMember);

            if (!rolesChanged.Value && rolesChanged.Result != null)
            {
                return(rolesChanged.Result);
            }
            else
            {
                needsResync = true;
            }

            // If there have been underlying changes made by ASP.NET Identity, then we need to resync the
            // IMember on the PersistedContent with what is stored since it will be mapped to display.
            if (needsResync && contentItem.PersistedContent is not null)
            {
                contentItem.PersistedContent = _memberService.GetById(contentItem.PersistedContent.Id) !;
            }

            return(true);
        }
        /// <summary>
        /// Create a member from the supplied member content data
        ///
        /// All member password processing and creation is done via the identity manager
        /// </summary>
        /// <param name="contentItem">Member content data</param>
        /// <returns>The identity result of the created member</returns>
        private async Task <ActionResult <bool> > CreateMemberAsync(MemberSave contentItem)
        {
            IMemberType?memberType = _memberTypeService.Get(contentItem.ContentTypeAlias);

            if (memberType == null)
            {
                throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}");
            }

            var identityMember = MemberIdentityUser.CreateNew(
                contentItem.Username,
                contentItem.Email,
                memberType.Alias,
                contentItem.IsApproved,
                contentItem.Name);

            IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password?.NewPassword);

            if (created.Succeeded == false)
            {
                MemberDisplay?forDisplay = _umbracoMapper.Map <MemberDisplay>(contentItem.PersistedContent);
                foreach (IdentityError error in created.Errors)
                {
                    switch (error.Code)
                    {
                    case nameof(IdentityErrorDescriber.InvalidUserName):
                        ModelState.AddPropertyError(
                            new ValidationResult(error.Description, new[] { "value" }),
                            string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                        break;

                    case nameof(IdentityErrorDescriber.PasswordMismatch):
                    case nameof(IdentityErrorDescriber.PasswordRequiresDigit):
                    case nameof(IdentityErrorDescriber.PasswordRequiresLower):
                    case nameof(IdentityErrorDescriber.PasswordRequiresNonAlphanumeric):
                    case nameof(IdentityErrorDescriber.PasswordRequiresUniqueChars):
                    case nameof(IdentityErrorDescriber.PasswordRequiresUpper):
                    case nameof(IdentityErrorDescriber.PasswordTooShort):
                        ModelState.AddPropertyError(
                            new ValidationResult(error.Description, new[] { "value" }),
                            string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                        break;

                    case nameof(IdentityErrorDescriber.InvalidEmail):
                        ModelState.AddPropertyError(
                            new ValidationResult(error.Description, new[] { "value" }),
                            string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                        break;

                    case nameof(IdentityErrorDescriber.DuplicateUserName):
                        ModelState.AddPropertyError(
                            new ValidationResult(error.Description, new[] { "value" }),
                            string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                        break;

                    case nameof(IdentityErrorDescriber.DuplicateEmail):
                        ModelState.AddPropertyError(
                            new ValidationResult(error.Description, new[] { "value" }),
                            string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                        break;
                    }
                }
                return(ValidationProblem(forDisplay, ModelState));
            }

            // now re-look up the member, which will now exist
            IMember?member = _memberService.GetByEmail(contentItem.Email);

            if (member is null)
            {
                return(false);
            }

            // map the save info over onto the user
            member = _umbracoMapper.Map <MemberSave, IMember>(contentItem, member);

            int creatorId = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1;

            member.CreatorId = creatorId;

            // assign the mapped property values that are not part of the identity properties
            string[] builtInAliases = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper).Select(x => x.Key).ToArray();
            foreach (ContentPropertyBasic property in contentItem.Properties)
            {
                if (builtInAliases.Contains(property.Alias) == false)
                {
                    member.Properties[property.Alias]?.SetValue(property.Value);
                }
            }

            // now the member has been saved via identity, resave the member with mapped content properties
            _memberService.Save(member);
            contentItem.PersistedContent = member;

            ActionResult <bool> rolesChanged = await AddOrUpdateRoles(contentItem.Groups, identityMember);

            if (!rolesChanged.Value && rolesChanged.Result != null)
            {
                return(rolesChanged.Result);
            }

            return(true);
        }
Esempio n. 25
0
        public MemberDisplay PostSave(
            [ModelBinder(typeof(MemberBinder))]
            MemberSave contentItem)
        {
            //If we've reached here it means:
            // * Our model has been bound
            // * and validated
            // * any file attachments have been saved to their temporary location for us to use
            // * we have a reference to the DTO object and the persisted object
            // * Permissions are valid

            //This is a special case for when we're not using the umbraco membership provider - when this is the case
            // we will not have a ContentTypeAlias set which means the model state will be invalid but we don't care about that
            // so we'll remove that model state value
            if (MembershipScenario != MembershipScenario.NativeUmbraco)
            {
                ModelState.Remove("ContentTypeAlias");

                // TODO: We're removing this because we are not displaying it but when we support the CustomProviderWithUmbracoLink scenario
                // we will be able to have a real name associated so do not remove this state once that is implemented!
                ModelState.Remove("Name");
            }

            //map the properties to the persisted entity
            MapPropertyValues(contentItem);

            //Unlike content/media - if there are errors for a member, we do NOT proceed to save them, we cannot so return the errors
            if (ModelState.IsValid == false)
            {
                var forDisplay = Mapper.Map <MemberDisplay>(contentItem.PersistedContent);
                forDisplay.Errors = ModelState.ToErrorDictionary();
                throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
            }

            // TODO: WE need to support this! - requires UI updates, etc...
            if (_provider.RequiresQuestionAndAnswer)
            {
                throw new NotSupportedException("Currently the member editor does not support providers that have RequiresQuestionAndAnswer specified");
            }

            //We're gonna look up the current roles now because the below code can cause
            // events to be raised and developers could be manually adding roles to members in
            // their handlers. If we don't look this up now there's a chance we'll just end up
            // removing the roles they've assigned.
            var currRoles = Roles.GetRolesForUser(contentItem.PersistedContent.Username);
            //find the ones to remove and remove them
            var rolesToRemove = currRoles.Except(contentItem.Groups).ToArray();

            string generatedPassword = null;

            //Depending on the action we need to first do a create or update using the membership provider
            // this ensures that passwords are formatted correctly and also performs the validation on the provider itself.
            switch (contentItem.Action)
            {
            case ContentSaveAction.Save:
                generatedPassword = UpdateWithMembershipProvider(contentItem);
                break;

            case ContentSaveAction.SaveNew:
                MembershipCreateStatus status;
                CreateWithMembershipProvider(contentItem, out status);

                // save the ID of the creator
                contentItem.PersistedContent.CreatorId = Security.CurrentUser.Id;
                break;

            default:
                //we don't support anything else for members
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            //If we've had problems creating/updating the user with the provider then return the error
            if (ModelState.IsValid == false)
            {
                var forDisplay = Mapper.Map <MemberDisplay>(contentItem.PersistedContent);
                forDisplay.Errors = ModelState.ToErrorDictionary();
                throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
            }

            //save the IMember -
            // TODO: When we support the CustomProviderWithUmbracoLink scenario, we'll need to save the custom properties for that here too
            if (MembershipScenario == MembershipScenario.NativeUmbraco)
            {
                //save the item
                //NOTE: We are setting the password to NULL - this indicates to the system to not actually save the password
                // so it will not get overwritten!
                contentItem.PersistedContent.RawPasswordValue = null;

                //create/save the IMember
                Services.MemberService.Save(contentItem.PersistedContent);
            }

            //Now let's do the role provider stuff - now that we've saved the content item (that is important since
            // if we are changing the username, it must be persisted before looking up the member roles).
            if (rolesToRemove.Any())
            {
                Roles.RemoveUserFromRoles(contentItem.PersistedContent.Username, rolesToRemove);
            }
            //find the ones to add and add them
            var toAdd = contentItem.Groups.Except(currRoles).ToArray();

            if (toAdd.Any())
            {
                //add the ones submitted
                Roles.AddUserToRoles(contentItem.PersistedContent.Username, toAdd);
            }

            //set the generated password (if there was one) - in order to do this we'll chuck the generated password into the
            // additional data of the IUmbracoEntity of the persisted item - then we can retrieve this in the model mapper and set
            // the value to be given to the UI. Hooray for AdditionalData :)
            contentItem.PersistedContent.AdditionalData["GeneratedPassword"] = generatedPassword;

            //return the updated model
            var display = Mapper.Map <MemberDisplay>(contentItem.PersistedContent);

            //lastly, if it is not valid, add the model state to the outgoing object and throw a 403
            HandleInvalidModelState(display);

            var localizedTextService = Services.TextService;

            //put the correct messages in
            switch (contentItem.Action)
            {
            case ContentSaveAction.Save:
            case ContentSaveAction.SaveNew:
                display.AddSuccessNotification(localizedTextService.Localize("speechBubbles/editMemberSaved"), localizedTextService.Localize("speechBubbles/editMemberSaved"));
                break;
            }

            return(display);
        }
Esempio n. 26
0
        /// <summary>
        /// Update the membership user using the membership provider (for things like email, etc...)
        /// If a password change is detected then we'll try that too.
        /// </summary>
        /// <param name="contentItem"></param>
        /// <returns>
        /// If the password has been reset then this method will return the reset/generated password, otherwise will return null.
        /// </returns>
        private string UpdateWithMembershipProvider(MemberSave contentItem)
        {
            //Get the member from the provider

            var membershipUser = _provider.GetUser(contentItem.PersistedContent.Key, false);

            if (membershipUser == null)
            {
                //This should never happen! so we'll let it YSOD if it does.
                throw new InvalidOperationException("Could not get member from membership provider " + _provider.Name + " with key " + contentItem.PersistedContent.Key);
            }

            var shouldReFetchMember = false;
            var providedUserName    = contentItem.PersistedContent.Username;

            //Update the membership user if it has changed
            try
            {
                var requiredUpdating = Members.UpdateMember(membershipUser, _provider,
                                                            contentItem.Email.Trim(),
                                                            contentItem.IsApproved,
                                                            comment: contentItem.Comments);

                if (requiredUpdating.Success)
                {
                    //re-map these values
                    shouldReFetchMember = true;
                }
            }
            catch (Exception ex)
            {
                LogHelper.WarnWithException <MemberController>("Could not update member, the provider returned an error", ex);
                ModelState.AddPropertyError(
                    //specify 'default' just so that it shows up as a notification - is not assigned to a property
                    new ValidationResult("Could not update member, the provider returned an error: " + ex.Message + " (see log for full details)"), "default");
            }

            //if they were locked but now they are trying to be unlocked
            if (membershipUser.IsLockedOut && contentItem.IsLockedOut == false)
            {
                try
                {
                    var result = _provider.UnlockUser(membershipUser.UserName);
                    if (result == false)
                    {
                        //it wasn't successful - but it won't really tell us why.
                        ModelState.AddModelError("custom", "Could not unlock the user");
                    }
                    else
                    {
                        shouldReFetchMember = true;
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError("custom", ex);
                }
            }
            else if (membershipUser.IsLockedOut == false && contentItem.IsLockedOut)
            {
                //NOTE: This should not ever happen unless someone is mucking around with the request data.
                //An admin cannot simply lock a user, they get locked out by password attempts, but an admin can un-approve them
                ModelState.AddModelError("custom", "An admin cannot lock a user");
            }

            //password changes ?
            if (contentItem.Password == null)
            {
                //If the provider has changed some values, these values need to be reflected in the member object
                //that will get mapped to the display object
                if (shouldReFetchMember)
                {
                    RefetchMemberData(contentItem, LookupType.ByKey);
                    RestoreProvidedUserName(contentItem, providedUserName);
                }

                return(null);
            }

            var passwordChangeResult = Members.ChangePassword(membershipUser.UserName, contentItem.Password, _provider);

            if (passwordChangeResult.Success)
            {
                //If the provider has changed some values, these values need to be reflected in the member object
                //that will get mapped to the display object
                if (shouldReFetchMember)
                {
                    RefetchMemberData(contentItem, LookupType.ByKey);
                    RestoreProvidedUserName(contentItem, providedUserName);
                }

                //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword
                return(passwordChangeResult.Result.ResetPassword);
            }

            //it wasn't successful, so add the change error to the model state
            ModelState.AddPropertyError(
                passwordChangeResult.Result.ChangeError,
                string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));

            return(null);
        }
Esempio n. 27
0
        /// <summary>
        /// Update the membership user using the membership provider (for things like email, etc...)
        /// If a password change is detected then we'll try that too.
        /// </summary>
        /// <param name="contentItem"></param>
        /// <returns>
        /// If the password has been reset then this method will return the reset/generated password, otherwise will return null.
        /// </returns>
        private string UpdateWithMembershipProvider(MemberSave contentItem)
        {
            //Get the member from the provider

            var membershipUser = _provider.GetUser(contentItem.PersistedContent.Key, false);

            if (membershipUser == null)
            {
                //This should never happen! so we'll let it YSOD if it does.
                throw new InvalidOperationException("Could not get member from membership provider " + _provider.Name + " with key " + contentItem.PersistedContent.Key);
            }

            var shouldReFetchMember = false;
            var providedUserName    = contentItem.PersistedContent.Username;

            //if the user doesn't have access to sensitive values, then we need to check if any of the built in member property types
            //have been marked as sensitive. If that is the case we cannot change these persisted values no matter what value has been posted.
            //There's only 3 special ones we need to deal with that are part of the MemberSave instance
            if (Security.CurrentUser.HasAccessToSensitiveData() == false)
            {
                var memberType          = Services.MemberTypeService.Get(contentItem.PersistedContent.ContentTypeId);
                var sensitiveProperties = memberType
                                          .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias))
                                          .ToList();

                foreach (var sensitiveProperty in sensitiveProperties)
                {
                    //if found, change the value of the contentItem model to the persisted value so it remains unchanged
                    switch (sensitiveProperty.Alias)
                    {
                    case Constants.Conventions.Member.Comments:
                        contentItem.Comments = contentItem.PersistedContent.Comments;
                        break;

                    case Constants.Conventions.Member.IsApproved:
                        contentItem.IsApproved = contentItem.PersistedContent.IsApproved;
                        break;

                    case Constants.Conventions.Member.IsLockedOut:
                        contentItem.IsLockedOut = contentItem.PersistedContent.IsLockedOut;
                        break;
                    }
                }
            }

            //Update the membership user if it has changed
            try
            {
                var requiredUpdating = Members.UpdateMember(membershipUser, _provider,
                                                            contentItem.Email.Trim(),
                                                            contentItem.IsApproved,
                                                            comment: contentItem.Comments);

                if (requiredUpdating.Success)
                {
                    //re-map these values
                    shouldReFetchMember = true;
                }
            }
            catch (Exception ex)
            {
                Logger.Warn <MemberController>(ex, "Could not update member, the provider returned an error");
                ModelState.AddPropertyError(
                    //specify 'default' just so that it shows up as a notification - is not assigned to a property
                    new ValidationResult("Could not update member, the provider returned an error: " + ex.Message + " (see log for full details)"), "default");
            }

            //if they were locked but now they are trying to be unlocked
            if (membershipUser.IsLockedOut && contentItem.IsLockedOut == false)
            {
                try
                {
                    var result = _provider.UnlockUser(membershipUser.UserName);
                    if (result == false)
                    {
                        //it wasn't successful - but it won't really tell us why.
                        ModelState.AddModelError("custom", "Could not unlock the user");
                    }
                    else
                    {
                        shouldReFetchMember = true;
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError("custom", ex);
                }
            }
            else if (membershipUser.IsLockedOut == false && contentItem.IsLockedOut)
            {
                //NOTE: This should not ever happen unless someone is mucking around with the request data.
                //An admin cannot simply lock a user, they get locked out by password attempts, but an admin can un-approve them
                ModelState.AddModelError("custom", "An admin cannot lock a user");
            }

            //password changes ?
            if (contentItem.Password == null)
            {
                //If the provider has changed some values, these values need to be reflected in the member object
                //that will get mapped to the display object
                if (shouldReFetchMember)
                {
                    RefetchMemberData(contentItem, LookupType.ByKey);
                    RestoreProvidedUserName(contentItem, providedUserName);
                }

                return(null);
            }

            var passwordChangeResult = Members.ChangePassword(membershipUser.UserName, contentItem.Password, _provider);

            if (passwordChangeResult.Success)
            {
                //If the provider has changed some values, these values need to be reflected in the member object
                //that will get mapped to the display object
                if (shouldReFetchMember)
                {
                    RefetchMemberData(contentItem, LookupType.ByKey);
                    RestoreProvidedUserName(contentItem, providedUserName);
                }

                //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword
                return(passwordChangeResult.Result.ResetPassword);
            }

            //it wasn't successful, so add the change error to the model state
            ModelState.AddPropertyError(
                passwordChangeResult.Result.ChangeError,
                string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));

            return(null);
        }
Esempio n. 28
0
        /// <summary>
        /// This is going to create the user with the membership provider and check for validation
        /// </summary>
        /// <param name="contentItem"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        /// <remarks>
        /// Depending on if the Umbraco membership provider is active or not, the process differs slightly:
        ///
        /// * If the umbraco membership provider is used - we create the membership user first with the membership provider, since
        ///     it's the umbraco membership provider, this writes to the umbraco tables. When that is complete we re-fetch the IMember
        ///     model data from the db. In this case we don't care what the provider user key is.
        /// * If we're using a non-umbraco membership provider - we check if there is a 'Member' member type - if so
        ///     we create an empty IMember instance first (of type 'Member'), this gives us a unique ID (GUID)
        ///     that we then use to create the member in the custom membership provider. This acts as the link between Umbraco data and
        ///     the custom membership provider data. This gives us the ability to eventually have custom membership properties but still use
        ///     a custom membership provider. If there is no 'Member' member type, then we will simply just create the membership provider member
        ///     with no link to our data.
        ///
        /// If this is successful, it will go and re-fetch the IMember from the db because it will now have an ID because the Umbraco provider
        /// uses the umbraco data store - then of course we need to re-map it to the saved property values.
        /// </remarks>
        private MembershipUser CreateWithMembershipProvider(MemberSave contentItem, out MembershipCreateStatus status)
        {
            MembershipUser membershipUser;

            switch (MembershipScenario)
            {
            case MembershipScenario.NativeUmbraco:
                //We are using the umbraco membership provider, create the member using the membership provider first.
                var umbracoMembershipProvider = (UmbracoMembershipProviderBase)_provider;
                // TODO: We are not supporting q/a - passing in empty here
                membershipUser = umbracoMembershipProvider.CreateUser(
                    contentItem.ContentTypeAlias, contentItem.Username,
                    contentItem.Password.NewPassword,
                    contentItem.Email, "", "",
                    contentItem.IsApproved,
                    Guid.NewGuid(),     //since it's the umbraco provider, the user key here doesn't make any difference
                    out status);

                break;

            case MembershipScenario.CustomProviderWithUmbracoLink:
                //We are using a custom membership provider, we'll create an empty IMember first to get the unique id to use
                // as the provider user key.
                //create it - this persisted item has already been set in the MemberBinder based on the 'Member' member type:
                Services.MemberService.Save(contentItem.PersistedContent);

                // TODO: We are not supporting q/a - passing in empty here
                membershipUser = _provider.CreateUser(
                    contentItem.Username,
                    contentItem.Password.NewPassword,
                    contentItem.Email,
                    "TEMP",                           //some membership provider's require something here even if q/a is disabled!
                    "TEMP",                           //some membership provider's require something here even if q/a is disabled!
                    contentItem.IsApproved,
                    contentItem.PersistedContent.Key, //custom membership provider, we'll link that based on the IMember unique id (GUID)
                    out status);

                break;

            case MembershipScenario.StandaloneCustomProvider:
                // we don't have a member type to use so we will just create the basic membership user with the provider with no
                // link back to the umbraco data

                var newKey = Guid.NewGuid();
                // TODO: We are not supporting q/a - passing in empty here
                membershipUser = _provider.CreateUser(
                    contentItem.Username,
                    contentItem.Password.NewPassword,
                    contentItem.Email,
                    "TEMP",     //some membership provider's require something here even if q/a is disabled!
                    "TEMP",     //some membership provider's require something here even if q/a is disabled!
                    contentItem.IsApproved,
                    newKey,
                    out status);

                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            // TODO: Localize these!
            switch (status)
            {
            case MembershipCreateStatus.Success:

                //map the key back
                contentItem.Key = membershipUser.ProviderUserKey.TryConvertTo <Guid>().Result;
                contentItem.PersistedContent.Key = contentItem.Key;

                //if the comments are there then we need to save them
                if (contentItem.Comments.IsNullOrWhiteSpace() == false)
                {
                    membershipUser.Comment = contentItem.Comments;
                    _provider.UpdateUser(membershipUser);
                }

                RefetchMemberData(contentItem, LookupType.ByUserName);

                break;

            case MembershipCreateStatus.InvalidUserName:
                ModelState.AddPropertyError(
                    new ValidationResult("Invalid user name", new[] { "value" }),
                    string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                break;

            case MembershipCreateStatus.InvalidPassword:
                ModelState.AddPropertyError(
                    new ValidationResult("Invalid password", new[] { "value" }),
                    string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                break;

            case MembershipCreateStatus.InvalidQuestion:
            case MembershipCreateStatus.InvalidAnswer:
                throw new NotSupportedException("Currently the member editor does not support providers that have RequiresQuestionAndAnswer specified");

            case MembershipCreateStatus.InvalidEmail:
                ModelState.AddPropertyError(
                    new ValidationResult("Invalid email", new[] { "value" }),
                    string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                break;

            case MembershipCreateStatus.DuplicateUserName:
                ModelState.AddPropertyError(
                    new ValidationResult("Username is already in use", new[] { "value" }),
                    string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                break;

            case MembershipCreateStatus.DuplicateEmail:
                ModelState.AddPropertyError(
                    new ValidationResult("Email address is already in use", new[] { "value" }),
                    string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
                break;

            case MembershipCreateStatus.InvalidProviderUserKey:
                ModelState.AddPropertyError(
                    //specify 'default' just so that it shows up as a notification - is not assigned to a property
                    new ValidationResult("Invalid provider user key"), "default");
                break;

            case MembershipCreateStatus.DuplicateProviderUserKey:
                ModelState.AddPropertyError(
                    //specify 'default' just so that it shows up as a notification - is not assigned to a property
                    new ValidationResult("Duplicate provider user key"), "default");
                break;

            case MembershipCreateStatus.ProviderError:
            case MembershipCreateStatus.UserRejected:
                ModelState.AddPropertyError(
                    //specify 'default' just so that it shows up as a notification - is not assigned to a property
                    new ValidationResult("User could not be created (rejected by provider)"), "default");
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(membershipUser);
        }
Esempio n. 29
0
        /// <summary>
        /// Maps the property values to the persisted entity
        /// </summary>
        /// <param name="contentItem"></param>
        private void MapPropertyValues(MemberSave contentItem)
        {
            UpdateName(contentItem);

            //map the custom properties - this will already be set for new entities in our member binder
            contentItem.PersistedContent.Email = contentItem.Email;
            contentItem.PersistedContent.Username = contentItem.Username;

            //use the base method to map the rest of the properties
            base.MapPropertyValues(contentItem);
        }
Esempio n. 30
0
        /// <summary>
        /// Update the membership user using the membership provider (for things like email, etc...)
        /// If a password change is detected then we'll try that too.
        /// </summary>
        /// <param name="contentItem"></param>
        /// <returns>
        /// If the password has been reset then this method will return the reset/generated password, otherwise will return null.
        /// </returns>
        private string UpdateWithMembershipProvider(MemberSave contentItem)
        {
            //Get the member from the provider

            var membershipUser = Membership.Provider.GetUser(contentItem.PersistedContent.Key, false);
            if (membershipUser == null)
            {
                //This should never happen! so we'll let it YSOD if it does.
                throw new InvalidOperationException("Could not get member from membership provider " + Membership.Provider.Name + " with key " + contentItem.PersistedContent.Key);
            }

            var shouldReFetchMember = false;

            //Update the membership user if it has changed
            if (HasMembershipUserChanged(membershipUser, contentItem))
            {
                membershipUser.Email = contentItem.Email.Trim();
                membershipUser.IsApproved = contentItem.IsApproved;
                membershipUser.Comment = contentItem.Comments;
                try
                {
                    Membership.Provider.UpdateUser(membershipUser);
                    //re-map these values 
                    shouldReFetchMember = true;
                }
                catch (Exception ex)
                {
                    LogHelper.WarnWithException<MemberController>("Could not update member, the provider returned an error", ex);
                    ModelState.AddPropertyError(
                        //specify 'default' just so that it shows up as a notification - is not assigned to a property
                        new ValidationResult("Could not update member, the provider returned an error: " + ex.Message + " (see log for full details)"), "default");
                }
            }

            //if they were locked but now they are trying to be unlocked
            if (membershipUser.IsLockedOut && contentItem.IsLockedOut == false)
            {
                try
                {
                    var result = Membership.Provider.UnlockUser(membershipUser.UserName);
                    if (result == false)
                    {
                        //it wasn't successful - but it won't really tell us why.
                        ModelState.AddModelError("custom", "Could not unlock the user");
                    }
                    else
                    {
                        shouldReFetchMember = true;
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError("custom", ex);
                }
            }
            else if (membershipUser.IsLockedOut == false && contentItem.IsLockedOut)
            {
                //NOTE: This should not ever happen unless someone is mucking around with the request data.
                //An admin cannot simply lock a user, they get locked out by password attempts, but an admin can un-approve them
                ModelState.AddModelError("custom", "An admin cannot lock a user");
            }

            //password changes ?           
            if (contentItem.Password == null)
            {
                //If the provider has changed some values, these values need to be reflected in the member object 
                //that will get mapped to the display object
                if (shouldReFetchMember)
                {
                    RefetchMemberData(contentItem);
                }
                return null;
            }

            var passwordChangeResult = Security.ChangePassword(membershipUser.UserName, contentItem.Password, Membership.Provider);
            if (passwordChangeResult.Success)
            {
                //If the provider has changed some values, these values need to be reflected in the member object 
                //that will get mapped to the display object
                if (shouldReFetchMember)
                {
                    RefetchMemberData(contentItem);
                }

                //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword
                return passwordChangeResult.Result.ResetPassword;
            }

            //it wasn't successful, so add the change error to the model state
            ModelState.AddPropertyError(
                passwordChangeResult.Result.ChangeError,
                string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));


            return null;
        }
Esempio n. 31
0
        /// <summary>
        /// Setup all standard member data for test
        /// </summary>
        private Member SetupMemberTestData(
            out MemberSave fakeMemberData,
            out MemberDisplay memberDisplay,
            ContentSaveAction contentAction)
        {
            // arrange
            MemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            Member     member     = MemberBuilder.CreateSimpleMember(memberType, "Test Member", "*****@*****.**", "123", "test");
            var        memberId   = 123;

            member.Id = memberId;

            // TODO: replace with builder for MemberSave and MemberDisplay
            fakeMemberData = new MemberSave()
            {
                Id            = memberId,
                SortOrder     = member.SortOrder,
                ContentTypeId = memberType.Id,
                Key           = member.Key,
                Password      = new ChangingPasswordModel()
                {
                    Id          = 456,
                    NewPassword = member.RawPasswordValue,
                    OldPassword = null
                },
                Name                  = member.Name,
                Email                 = member.Email,
                Username              = member.Username,
                PersistedContent      = member,
                PropertyCollectionDto = new ContentPropertyCollectionDto()
                {
                },
                Groups = new List <string>(),
                //Alias = "fakeAlias",
                ContentTypeAlias = member.ContentTypeAlias,
                Action           = contentAction,
                Icon             = "icon-document",
                Path             = member.Path
            };

            memberDisplay = new MemberDisplay()
            {
                Id            = memberId,
                SortOrder     = member.SortOrder,
                ContentTypeId = memberType.Id,
                Key           = member.Key,
                Name          = member.Name,
                Email         = member.Email,
                Username      = member.Username,
                //Alias = "fakeAlias",
                ContentTypeAlias = member.ContentTypeAlias,
                ContentType      = new ContentTypeBasic(),
                ContentTypeName  = member.ContentType.Name,
                Icon             = fakeMemberData.Icon,
                Path             = member.Path,
                Tabs             = new List <Tab <ContentPropertyDisplay> >()
                {
                    new Tab <ContentPropertyDisplay>()
                    {
                        Alias      = "test",
                        Id         = 77,
                        Properties = new List <ContentPropertyDisplay>()
                        {
                            new ContentPropertyDisplay()
                            {
                                Alias = "_umb_id",
                                View  = "idwithguid",
                                Value = new []
                                {
                                    "123",
                                    "guid"
                                }
                            },
                            new ContentPropertyDisplay()
                            {
                                Alias = "_umb_doctype"
                            },
                            new ContentPropertyDisplay()
                            {
                                Alias = "_umb_login"
                            },
                            new ContentPropertyDisplay()
                            {
                                Alias = "_umb_email"
                            },
                            new ContentPropertyDisplay()
                            {
                                Alias = "_umb_password"
                            },
                            new ContentPropertyDisplay()
                            {
                                Alias = "_umb_membergroup"
                            }
                        }
                    }
                }
            };

            return(member);
        }
Esempio n. 32
0
 /// <summary>
 ///     Returns an IMember instance used to bind values to and save (depending on the membership scenario)
 /// </summary>
 /// <param name="model"></param>
 /// <returns></returns>
 private IMember GetExisting(MemberSave model) => GetExisting(model.Key);