Files
Umbraco-CMS/src/Umbraco.Web/Editors/MemberController.cs

848 lines
40 KiB
C#

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Security;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.WebApi;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi.Binders;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
using System.Collections.Generic;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.Editors
{
/// <remarks>
/// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting
/// access to ALL of the methods on this controller will need access to the member application.
/// </remarks>
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorizeAttribute(Constants.Applications.Members)]
[OutgoingNoHyphenGuidFormat]
public class MemberController : ContentControllerBase
{
public MemberController(PropertyEditorCollection propertyEditors)
{
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
}
private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
private readonly PropertyEditorCollection _propertyEditors;
/// <summary>
/// Returns the currently configured membership scenario for members in umbraco
/// </summary>
/// <value></value>
protected MembershipScenario MembershipScenario
{
get { return Services.MemberService.GetMembershipScenario(); }
}
public PagedResult<MemberBasic> GetPagedResults(
int pageNumber = 1,
int pageSize = 100,
string orderBy = "username",
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "",
string memberTypeAlias = null)
{
if (pageNumber <= 0 || pageSize <= 0)
{
throw new NotSupportedException("Both pageNumber and pageSize must be greater than zero");
}
if (MembershipScenario == MembershipScenario.NativeUmbraco)
{
var members = Services.MemberService
.GetAll((pageNumber - 1), pageSize, out var totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray();
if (totalRecords == 0)
{
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Select(x => ContextMapper.Map<IMember, MemberBasic>(x, UmbracoContext))
};
return pagedResult;
}
else
{
int totalRecords;
MembershipUserCollection members;
if (filter.IsNullOrWhiteSpace())
{
members = _provider.GetAllUsers((pageNumber - 1), pageSize, out totalRecords);
}
else
{
//we need to search!
//try by name first
members = _provider.FindUsersByName(filter, (pageNumber - 1), pageSize, out totalRecords);
if (totalRecords == 0)
{
//try by email then
members = _provider.FindUsersByEmail(filter, (pageNumber - 1), pageSize, out totalRecords);
}
}
if (totalRecords == 0)
{
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Cast<MembershipUser>()
.Select(Mapper.Map<MembershipUser, MemberBasic>)
};
return pagedResult;
}
}
/// <summary>
/// Returns a display node with a list view to render members
/// </summary>
/// <param name="listName"></param>
/// <returns></returns>
public MemberListDisplay GetListNodeDisplay(string listName)
{
var foundType = Services.MemberTypeService.Get(listName);
var name = foundType != null ? foundType.Name : listName;
var apps = new List<ContentApp>();
apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, listName, "member");
apps[0].Active = true;
var display = new MemberListDisplay
{
ContentTypeAlias = listName,
ContentTypeName = name,
Id = listName,
IsContainer = true,
Name = listName == Constants.Conventions.MemberTypes.AllMembersListId ? "All Members" : name,
Path = "-1," + listName,
ParentId = -1,
ContentApps = apps
};
return display;
}
/// <summary>
/// Gets the content json for the member
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
public MemberDisplay GetByKey(Guid key)
{
MembershipUser foundMembershipMember;
MemberDisplay display;
IMember foundMember;
switch (MembershipScenario)
{
case MembershipScenario.NativeUmbraco:
foundMember = Services.MemberService.GetByKey(key);
if (foundMember == null)
{
HandleContentNotFound(key);
}
return ContextMapper.Map<IMember, MemberDisplay>(foundMember, UmbracoContext);
case MembershipScenario.CustomProviderWithUmbracoLink:
//TODO: Support editing custom properties for members with a custom membership provider here.
//foundMember = Services.MemberService.GetByKey(key);
//if (foundMember == null)
//{
// HandleContentNotFound(key);
//}
//foundMembershipMember = Membership.GetUser(key, false);
//if (foundMembershipMember == null)
//{
// HandleContentNotFound(key);
//}
//display = Mapper.Map<MembershipUser, MemberDisplay>(foundMembershipMember);
////map the name over
//display.Name = foundMember.Name;
//return display;
case MembershipScenario.StandaloneCustomProvider:
default:
foundMembershipMember = _provider.GetUser(key, false);
if (foundMembershipMember == null)
{
HandleContentNotFound(key);
}
display = Mapper.Map<MembershipUser, MemberDisplay>(foundMembershipMember);
return display;
}
}
/// <summary>
/// Gets an empty content item for the
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
public MemberDisplay GetEmpty(string contentTypeAlias = null)
{
IMember emptyContent;
switch (MembershipScenario)
{
case MembershipScenario.NativeUmbraco:
if (contentTypeAlias == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var contentType = Services.MemberTypeService.Get(contentTypeAlias);
if (contentType == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
emptyContent = new Member(contentType);
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters);
return ContextMapper.Map<IMember, MemberDisplay>(emptyContent, UmbracoContext);
case MembershipScenario.CustomProviderWithUmbracoLink:
//TODO: Support editing custom properties for members with a custom membership provider here.
case MembershipScenario.StandaloneCustomProvider:
default:
//we need to return a scaffold of a 'simple' member - basically just what a membership provider can edit
emptyContent = MemberService.CreateGenericMembershipProviderMember("", "", "", "");
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(Membership.MinRequiredPasswordLength, Membership.MinRequiredNonAlphanumericCharacters);
return ContextMapper.Map<IMember, MemberDisplay>(emptyContent, UmbracoContext);
}
}
/// <summary>
/// Saves member
/// </summary>
/// <returns></returns>
[FileUploadCleanupFilter]
[OutgoingEditorModelEvent]
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 = ContextMapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent, UmbracoContext);
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 correclty 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 = ContextMapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent, UmbracoContext);
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 gen'd 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 = ContextMapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent, UmbracoContext);
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
HandleInvalidModelState(display);
var localizedTextService = Services.TextService;
//put the correct msgs in
switch (contentItem.Action)
{
case ContentSaveAction.Save:
case ContentSaveAction.SaveNew:
display.AddSuccessNotification(localizedTextService.Localize("speechBubbles/editMemberSaved"), localizedTextService.Localize("speechBubbles/editMemberSaved"));
break;
}
return display;
}
/// <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<IMember, MemberSave>(
contentItem,
(save, property) => property.GetValue(), //get prop val
(save, property, v) => property.SetValue(v)); //set prop val
}
/// <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 sensitiveProperties = contentItem.PersistedContent.ContentType
.PropertyTypes.Where(x => contentItem.PersistedContent.ContentType.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;
}
private enum LookupType
{
ByKey,
ByUserName
}
/// <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>
/// <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;
}
}
}
/// <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 potentiallly changed, username
/// </summary>
/// <param name="contentItem"></param>
/// <param name="providedUserName"></param>
private static void RestoreProvidedUserName(MemberSave contentItem, string providedUserName)
{
contentItem.PersistedContent.Username = providedUserName;
}
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>
/// 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 = (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;
}
/// <summary>
/// Permanently deletes a member
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
///
[HttpPost]
public HttpResponseMessage DeleteByKey(Guid key)
{
IMember foundMember;
MembershipUser foundMembershipUser;
switch (MembershipScenario)
{
case MembershipScenario.NativeUmbraco:
foundMember = Services.MemberService.GetByKey(key);
if (foundMember == null)
{
return HandleContentNotFound(key, false);
}
Services.MemberService.Delete(foundMember);
break;
case MembershipScenario.CustomProviderWithUmbracoLink:
foundMember = Services.MemberService.GetByKey(key);
if (foundMember != null)
{
Services.MemberService.Delete(foundMember);
}
foundMembershipUser = _provider.GetUser(key, false);
if (foundMembershipUser != null)
{
_provider.DeleteUser(foundMembershipUser.UserName, true);
}
break;
case MembershipScenario.StandaloneCustomProvider:
foundMembershipUser = _provider.GetUser(key, false);
if (foundMembershipUser != null)
{
_provider.DeleteUser(foundMembershipUser.UserName, true);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
/// <summary>
/// Exports member data based on their unique Id
/// </summary>
/// <param name="key">The unique <see cref="Guid">member identifier</see></param>
/// <returns><see cref="HttpResponseMessage"/></returns>
[HttpGet]
public HttpResponseMessage ExportMemberData(Guid key)
{
var currentUser = Security.CurrentUser;
var httpResponseMessage = Request.CreateResponse();
if (currentUser.HasAccessToSensitiveData() == false)
{
httpResponseMessage.StatusCode = HttpStatusCode.Forbidden;
return httpResponseMessage;
}
var member = ((MemberService)Services.MemberService).ExportMember(key);
var fileName = $"{member.Name}_{member.Email}.txt";
httpResponseMessage.Content = new ObjectContent<MemberExportModel>(member, new JsonMediaTypeFormatter { Indent = true });
httpResponseMessage.Content.Headers.Add("x-filename", fileName);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
httpResponseMessage.StatusCode = HttpStatusCode.OK;
return httpResponseMessage;
}
}
}