Files
Umbraco-CMS/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs

477 lines
22 KiB
C#
Raw Normal View History

// using System;
// using System.Collections.Generic;
// 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.Http;
// using System.Web.Http.ModelBinding;
// using Microsoft.AspNetCore.Identity;
// using Microsoft.AspNetCore.Mvc;
// using Umbraco.Core;
// using Umbraco.Core.Cache;
// using Umbraco.Core.Configuration;
// using Umbraco.Core.Dictionary;
// using Umbraco.Core.Logging;
// using Umbraco.Core.Models;
// using Umbraco.Core.Models.ContentEditing;
// using Umbraco.Core.Models.Membership;
// using Umbraco.Core.Persistence;
// using Umbraco.Core.PropertyEditors;
// using Umbraco.Core.Security;
// using Umbraco.Core.Services;
// using Umbraco.Core.Services.Implement;
// using Umbraco.Core.Strings;
// using Umbraco.Web.ContentApps;
// using Umbraco.Web.Editors.Binders;
// using Umbraco.Web.Editors.Filters;
// using Umbraco.Web.Models.ContentEditing;
// using Umbraco.Web.Mvc;
// using Umbraco.Web.WebApi;
// using Umbraco.Web.WebApi.Filters;
// using Constants = Umbraco.Core.Constants;
// using Umbraco.Core.Mapping;
// using Umbraco.Extensions;
// using Umbraco.Web.BackOffice.Filters;
// using Umbraco.Web.Common.Attributes;
// using Umbraco.Web.Common.Exceptions;
// using Umbraco.Web.Routing;
//
// 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")]
// [UmbracoApplicationAuthorize(Constants.Applications.Members)]
// [OutgoingNoHyphenGuidFormat]
// public class MemberController : ContentControllerBase
// {
// public MemberController(
// IMemberPasswordConfiguration passwordConfig,
// ICultureDictionary cultureDictionary,
// PropertyEditorCollection propertyEditors,
// IGlobalSettings globalSettings,
// IUmbracoContextAccessor umbracoContextAccessor,
// ISqlContext sqlContext,
// ServiceContext services,
// AppCaches appCaches,
// IProfilingLogger logger,
// IRuntimeState runtimeState,
// IShortStringHelper shortStringHelper,
// UmbracoMapper umbracoMapper,
// IPublishedUrlProvider publishedUrlProvider)
// : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider)
// {
// _passwordConfig = passwordConfig ?? throw new ArgumentNullException(nameof(passwordConfig));
// _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
// _passwordSecurity = new LegacyPasswordSecurity(_passwordConfig);
// _passwordValidator = new ConfiguredPasswordValidator();
// }
//
// private readonly IMemberPasswordConfiguration _passwordConfig;
// private readonly PropertyEditorCollection _propertyEditors;
// private readonly LegacyPasswordSecurity _passwordSecurity;
// private readonly IPasswordValidator<> _passwordValidator;
//
// 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");
// }
//
// 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 => Mapper.Map<MemberBasic>(x))
// };
// 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.Add(ListViewContentAppFactory.CreateContentApp(Services.DataTypeService, _propertyEditors, listName, "member", Core.Constants.DataTypes.DefaultMembersListView));
// 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] // TODO introduce when moved to .NET Core
// public MemberDisplay GetByKey(Guid key)
// {
// var foundMember = Services.MemberService.GetByKey(key);
// if (foundMember == null)
// {
// HandleContentNotFound(key);
// }
// return Mapper.Map<MemberDisplay>(foundMember);
// }
//
// /// <summary>
// /// Gets an empty content item for the
// /// </summary>
// /// <param name="contentTypeAlias"></param>
// /// <returns></returns>
// // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
// public MemberDisplay GetEmpty(string contentTypeAlias = null)
// {
// IMember emptyContent;
// if (contentTypeAlias == null)
// {
// throw new HttpResponseException(HttpStatusCode.NotFound);
// }
//
// var contentType = Services.MemberTypeService.Get(contentTypeAlias);
// if (contentType == null)
// {
// throw new HttpResponseException(HttpStatusCode.NotFound);
// }
//
// var passwordGenerator = new PasswordGenerator(_passwordConfig);
//
// emptyContent = new Member(contentType);
// emptyContent.AdditionalData["NewPassword"] = passwordGenerator.GeneratePassword();
// return Mapper.Map<MemberDisplay>(emptyContent);
// }
//
// /// <summary>
// /// Saves member
// /// </summary>
// /// <returns></returns>
// [FileUploadCleanupFilter]
// // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
// [MemberSaveValidation]
// public async Task<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
//
// //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)
// {
// var forDisplay = Mapper.Map<MemberDisplay>(contentItem.PersistedContent);
// forDisplay.Errors = ModelState.ToErrorDictionary();
// throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
// }
//
// //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 = Services.MemberService.GetAllRoles(contentItem.PersistedContent.Username);
// //find the ones to remove and remove them
// var rolesToRemove = currRoles.Except(contentItem.Groups).ToArray();
//
// //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:
// UpdateMemberData(contentItem);
// break;
// case ContentSaveAction.SaveNew:
// contentItem.PersistedContent = CreateMemberData(contentItem);
// break;
// default:
// //we don't support anything else for members
// throw new HttpResponseException(HttpStatusCode.NotFound);
// }
//
// //TODO: There's 3 things saved here and we should do this all in one transaction, which we can do here by wrapping in a scope
// // but it would be nicer to have this taken care of within the Save method itself
//
// //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())
// {
// Services.MemberService.DissociateRoles(new[] { 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
// Services.MemberService.AssignRoles(new[] { contentItem.PersistedContent.Username }, toAdd);
// }
//
// //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;
// }
//
// /// <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 IMember CreateMemberData(MemberSave contentItem)
// {
// var memberType = Services.MemberTypeService.Get(contentItem.ContentTypeAlias);
// if (memberType == null)
// throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}");
// var member = new Member(contentItem.Name, contentItem.Email, contentItem.Username, memberType, true)
// {
// CreatorId = Security.CurrentUser.Id,
// RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword),
// Comments = contentItem.Comments,
// IsApproved = contentItem.IsApproved
// };
//
// return member;
// }
//
// /// <summary>
// /// Update the member security data
// /// </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 void UpdateMemberData(MemberSave contentItem)
// {
// contentItem.PersistedContent.WriterId = Security.CurrentUser.Id;
//
// // 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 (!Security.CurrentUser.HasAccessToSensitiveData())
// {
// var memberType = Services.MemberTypeService.Get(contentItem.PersistedContent.ContentTypeId);
// var sensitiveProperties = memberType
// .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias))
// .ToList();
//
// foreach (var sensitiveProperty in sensitiveProperties)
// {
// var 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
// var origValue = contentItem.PersistedContent.GetValue(sensitiveProperty.Alias);
// destProp.Value = origValue;
// }
// }
// }
//
// var isLockedOut = contentItem.IsLockedOut;
//
// //if they were locked but now they are trying to be unlocked
// if (contentItem.PersistedContent.IsLockedOut && isLockedOut == false)
// {
// contentItem.PersistedContent.IsLockedOut = false;
// contentItem.PersistedContent.FailedPasswordAttempts = 0;
// }
// else if (!contentItem.PersistedContent.IsLockedOut && 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");
// }
//
// //no password changes then exit ?
// if (contentItem.Password == null)
// return;
//
// // set the password
// contentItem.PersistedContent.RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword);
// }
//
// 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;
// }
// }
//
// // TODO: This logic should be pulled into the service layer
// private async Task<bool> ValidateMemberDataAsync(MemberSave contentItem)
// {
// if (contentItem.Name.IsNullOrWhiteSpace())
// {
// ModelState.AddPropertyError(
// new ValidationResult("Invalid user name", new[] { "value" }),
// string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
// return false;
// }
//
// if (contentItem.Password != null && !contentItem.Password.NewPassword.IsNullOrWhiteSpace())
// {
// var validPassword = await _passwordValidator.ValidateAsync(_passwordConfig, contentItem.Password.NewPassword);
// if (!validPassword)
// {
// ModelState.AddPropertyError(
// new ValidationResult("Invalid password: " + string.Join(", ", validPassword.Result), new[] { "value" }),
// string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
// return false;
// }
// }
//
// var byUsername = Services.MemberService.GetByUsername(contentItem.Username);
// if (byUsername != null && byUsername.Key != contentItem.Key)
// {
// ModelState.AddPropertyError(
// new ValidationResult("Username is already in use", new[] { "value" }),
// string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
// return false;
// }
//
// var byEmail = Services.MemberService.GetByEmail(contentItem.Email);
// if (byEmail != null && byEmail.Key != contentItem.Key)
// {
// ModelState.AddPropertyError(
// new ValidationResult("Email address is already in use", new[] { "value" }),
// string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
// return false;
// }
//
// return true;
// }
//
// /// <summary>
// /// Permanently deletes a member
// /// </summary>
// /// <param name="key"></param>
// /// <returns></returns>
// ///
// [HttpPost]
// public HttpResponseMessage DeleteByKey(Guid key)
// {
// var foundMember = Services.MemberService.GetByKey(key);
// if (foundMember == null)
// {
// return HandleContentNotFound(key, false);
// }
// Services.MemberService.Delete(foundMember);
//
// 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;
// }
// }
//
//
// }