Added custom resolver to deal with sensitive values on member list view, some cleanup

This commit is contained in:
Robert
2018-03-12 12:04:54 +01:00
parent 304e574bbc
commit 8749b030cf
5 changed files with 115 additions and 42 deletions

View File

@@ -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<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize);
pagedResult.Items = members
.Select(Mapper.Map<IMember, MemberBasic>);
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Select(Mapper.Map<IMember, MemberBasic>)
};
return pagedResult;
}
else
@@ -133,10 +121,13 @@ namespace Umbraco.Web.Editors
{
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize);
pagedResult.Items = members
.Cast<MembershipUser>()
.Select(Mapper.Map<MembershipUser, MemberBasic>);
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Cast<MembershipUser>()
.Select(Mapper.Map<MembershipUser, MemberBasic>)
};
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<MemberExportModel>(member, new JsonMediaTypeFormatter {Indent = true});
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;
}
}
}

View File

@@ -80,17 +80,21 @@ namespace Umbraco.Web.Models.Mapping
//FROM IMember TO MemberBasic
config.CreateMap<IMember, MemberBasic>()
.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<IMember>()))
.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<MembershipUser, MemberBasic>()
@@ -477,8 +481,41 @@ namespace Umbraco.Web.Models.Mapping
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(member, context.GetUmbracoContext());
}
}
internal class MemberSensitivePropertiesResolver : SensitivePropertiesResolver<IMember>
{
/// <summary>
/// Overridden to assign the IsSensitive property values
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="content"></param>
/// <param name="properties"></param>
/// <returns></returns>
protected override List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> 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;
}
}
}
}

View File

@@ -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<TSource> : 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<ContentPropertyDisplay>));
}
protected virtual IEnumerable<ContentPropertyDisplay> ResolveCore(UmbracoContext umbracoContext, TSource content)
{
var properties = new List<Property>();
properties.AddRange(content.Properties);
//map the properties
var mappedProperties = MapProperties(umbracoContext, content, properties);
return mappedProperties;
}
protected virtual List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties)
{
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(
// 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;
}
}
}

View File

@@ -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<Tab<ContentPropertyDisplay>>));
}
/// <summary>
/// Adds the container (listview) tab to the document
/// </summary>
@@ -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;
}
}
}

View File

@@ -336,6 +336,7 @@
<Compile Include="Models\Mapping\AutoMapperExtensions.cs" />
<Compile Include="Models\Mapping\ContentTreeNodeUrlResolver.cs" />
<Compile Include="Models\Mapping\MemberTreeNodeUrlResolver.cs" />
<Compile Include="Models\Mapping\SensitivePropertiesResolver.cs" />
<Compile Include="Models\Trees\ExportMember.cs" />
<Compile Include="TourFilterResolver.cs" />
<Compile Include="Editors\UserEditorAuthorizationHelper.cs" />