From 8749b030cf5024b508bdab1d35867c4e31a7cf73 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 12 Mar 2018 12:04:54 +0100 Subject: [PATCH] Added custom resolver to deal with sensitive values on member list view, some cleanup --- src/Umbraco.Web/Editors/MemberController.cs | 51 ++++++++----------- .../Models/Mapping/MemberModelMapper.cs | 49 +++++++++++++++--- .../Mapping/SensitivePropertiesResolver.cs | 47 +++++++++++++++++ .../Mapping/TabsAndPropertiesResolver.cs | 9 ++-- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 5 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 src/Umbraco.Web/Models/Mapping/SensitivePropertiesResolver.cs diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index a9e02a0fa8..ab137e9614 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -1,29 +1,18 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; -using System.Reflection; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Web; using System.Web.Http; using System.Web.Http.ModelBinding; using System.Web.Security; using AutoMapper; -using Examine.LuceneEngine.SearchCriteria; -using Examine.SearchCriteria; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Security; using Umbraco.Core.Services; @@ -33,10 +22,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Binders; using Umbraco.Web.WebApi.Filters; -using umbraco; using Constants = Umbraco.Core.Constants; -using Examine; -using Newtonsoft.Json; namespace Umbraco.Web.Editors { @@ -96,16 +82,18 @@ namespace Umbraco.Web.Editors if (MembershipScenario == MembershipScenario.NativeUmbraco) { - long totalRecords; var members = Services.MemberService - .GetAll((pageNumber - 1), pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray(); + .GetAll((pageNumber - 1), pageSize, out var totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray(); if (totalRecords == 0) { return new PagedResult(0, 0, 0); } - var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize); - pagedResult.Items = members - .Select(Mapper.Map); + + var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize) + { + Items = members + .Select(Mapper.Map) + }; return pagedResult; } else @@ -133,10 +121,13 @@ namespace Umbraco.Web.Editors { return new PagedResult(0, 0, 0); } - var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize); - pagedResult.Items = members - .Cast() - .Select(Mapper.Map); + + var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize) + { + Items = members + .Cast() + .Select(Mapper.Map) + }; return pagedResult; } @@ -437,7 +428,7 @@ namespace Umbraco.Web.Editors 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 @@ -665,7 +656,7 @@ namespace Umbraco.Web.Editors 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.IsApproved, contentItem.PersistedContent.Key, //custom membership provider, we'll link that based on the IMember unique id (GUID) out status); @@ -682,7 +673,7 @@ namespace Umbraco.Web.Editors 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.IsApproved, newKey, out status); @@ -828,17 +819,17 @@ namespace Umbraco.Web.Editors var member = ((MemberService)Services.MemberService).ExportMember(key); var fileName = $"{member.Name}_{member.Email}.txt"; - - httpResponseMessage.Content = new ObjectContent(member, new JsonMediaTypeFormatter {Indent = true}); + + httpResponseMessage.Content = new ObjectContent(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; } } - + } diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index 5c72879210..b449cff983 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -80,17 +80,21 @@ namespace Umbraco.Web.Models.Mapping //FROM IMember TO MemberBasic config.CreateMap() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) + .ForMember(display => display.Udi, + expression => + expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver())) .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember(dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)) .ForMember(dto => dto.Email, expression => expression.MapFrom(content => content.Email)) .ForMember(dto => dto.Username, expression => expression.MapFrom(content => content.Username)) .ForMember(dto => dto.Trashed, expression => expression.Ignore()) .ForMember(dto => dto.Published, expression => expression.Ignore()) .ForMember(dto => dto.Updater, expression => expression.Ignore()) .ForMember(dto => dto.Alias, expression => expression.Ignore()) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); + .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()) + .ForMember(dto => dto.Properties, expression => expression.ResolveUsing(new MemberSensitivePropertiesResolver())); //FROM MembershipUser TO MemberBasic config.CreateMap() @@ -477,8 +481,41 @@ namespace Umbraco.Web.Models.Mapping return AutoMapperExtensions.MapWithUmbracoContext(member, context.GetUmbracoContext()); } } + + internal class MemberSensitivePropertiesResolver : SensitivePropertiesResolver + { + + /// + /// Overridden to assign the IsSensitive property values + /// + /// + /// + /// + /// + protected override List MapProperties(UmbracoContext umbracoContext, IContentBase content, List properties) + { + var result = base.MapProperties(umbracoContext, content, properties); + var member = (IMember)content; + var memberType = member.ContentType; + + //now update the IsSensitive value + foreach (var prop in result) + { + //check if this property is flagged as sensitive + var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias); + //check permissions for viewing sensitive data + if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false) + { + //mark this property as readonly so that it does not post any data + prop.Readonly = true; + //replace this editor with a sensitivevalue + prop.View = "sensitivevalue"; + //clear the value + prop.Value = null; + } + } + return result; + } + } } } - - - diff --git a/src/Umbraco.Web/Models/Mapping/SensitivePropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/SensitivePropertiesResolver.cs new file mode 100644 index 0000000000..087f2674be --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/SensitivePropertiesResolver.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal class SensitivePropertiesResolver : IValueResolver where TSource : IContentBase + { + public ResolutionResult Resolve(ResolutionResult source) + { + if (source.Value != null && (source.Value is TSource) == false) + throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", new object[] + { + source.Value.GetType(), + typeof (TSource) + })); + return source.New( + //perform the mapping with the current umbraco context + ResolveCore(source.Context.GetUmbracoContext(), (TSource)source.Value), typeof(IEnumerable)); + } + + protected virtual IEnumerable ResolveCore(UmbracoContext umbracoContext, TSource content) + { + var properties = new List(); + + properties.AddRange(content.Properties); + + //map the properties + var mappedProperties = MapProperties(umbracoContext, content, properties); + + return mappedProperties; + } + + protected virtual List MapProperties(UmbracoContext umbracoContext, IContentBase content, List properties) + { + var result = Mapper.Map, IEnumerable>( + // Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties. + properties.OrderBy(prop => prop.PropertyType.SortOrder)) + .ToList(); + + return result; + } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index 7d54650c96..82d4855f32 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Globalization; using System.Linq; using AutoMapper; using Umbraco.Core; @@ -9,7 +7,6 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; -using umbraco; namespace Umbraco.Web.Models.Mapping { @@ -53,7 +50,7 @@ namespace Umbraco.Web.Models.Mapping //perform the mapping with the current umbraco context ResolveCore(source.Context.GetUmbracoContext(), (TSource)source.Value), typeof(List>)); } - + /// /// Adds the container (listview) tab to the document /// @@ -275,7 +272,7 @@ namespace Umbraco.Web.Models.Mapping //now add the user props contentProps.AddRange(currProps); - + //re-assign genericProps.Properties = contentProps; @@ -308,6 +305,6 @@ namespace Umbraco.Web.Models.Mapping return result; } - + } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c6d528bb3d..ea249f7662 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -336,6 +336,7 @@ +