Merge pull request #2514 from umbraco/temp-U4-11065

Fix - U4-11065 Sensitive data is shown in listviews
This commit is contained in:
Shannon Deminick
2018-03-12 17:25:49 -07:00
committed by GitHub
8 changed files with 120 additions and 46 deletions

View File

@@ -47,7 +47,17 @@
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<span title="{{column.header}}: {{item[column.alias]}}">{{item[column.alias]}}</span>
<span title="{{column.header}}: {{item[column.alias]}}">
<div ng-if="!column.isSensitive">
{{item[column.alias]}}
</div>
<em ng-show="column.isSensitive" class="muted">
<localize key="content_isSensitiveValue_short"></localize>
</em>
</span>
</div>
</div>
</div>

View File

@@ -30,15 +30,14 @@
vm.dragLeave = dragLeave;
vm.onFilesQueue = onFilesQueue;
vm.onUploadComplete = onUploadComplete;
markAsSensitive();
function activate() {
if ($scope.entityType === 'media') {
mediaTypeHelper.getAllowedImagetypes(vm.nodeId).then(function (types) {
vm.acceptedMediatypes = types;
});
}
}
function selectAll($event) {
@@ -87,6 +86,27 @@
$scope.getContent($scope.contentId);
}
function markAsSensitive() {
angular.forEach($scope.options.includeProperties, function (option) {
option.isSensitive = false;
angular.forEach($scope.items,
function (item) {
angular.forEach(item.properties,
function (property) {
if (option.alias === property.alias) {
option.isSensitive = property.isSensitive;
}
});
});
});
}
activate();
}

View File

@@ -229,6 +229,7 @@
<key alias="removeTextBox">Remove this text box</key>
<key alias="contentRoot">Content root</key>
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
<key alias="isSensitiveValue_short">This value is hidden.</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom">Create a new Content Template from '%0%'</key>

View File

@@ -229,6 +229,7 @@
<key alias="removeTextBox">Remove this text box</key>
<key alias="contentRoot">Content root</key>
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
<key alias="isSensitiveValue_short">This value is hidden.</key>
</area>
<area alias="blueprints">
<key alias="createBlueprintFrom">Create a new Content Template from '%0%'</key>

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(x => AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberBasic>(x, UmbracoContext))
};
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

@@ -30,6 +30,11 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "editor", IsRequired = false)]
public string Editor { get; set; }
/// <summary>
/// Flags the property to denote that it can contain sensitive data
/// </summary>
[DataMember(Name = "isSensitive", IsRequired = false)]
public bool IsSensitive { get; set; }
/// <summary>
/// Used internally during model mapping
@@ -38,4 +43,4 @@ namespace Umbraco.Web.Models.ContentEditing
internal PropertyEditor PropertyEditor { get; set; }
}
}
}

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 MemberBasicPropertiesResolver()));
//FROM MembershipUser TO MemberBasic
config.CreateMap<MembershipUser, MemberBasic>()
@@ -395,6 +399,8 @@ namespace Umbraco.Web.Models.Mapping
//check permissions for viewing sensitive data
if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
{
//mark this property as sensitive
prop.IsSensitive = true;
//mark this property as readonly so that it does not post any data
prop.Readonly = true;
//replace this editor with a sensitivevalue
@@ -477,8 +483,51 @@ namespace Umbraco.Web.Models.Mapping
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(member, context.GetUmbracoContext());
}
}
/// <summary>
/// A resolver to map <see cref="IMember"/> properties to a collection of <see cref="ContentPropertyBasic"/>
/// </summary>
internal class MemberBasicPropertiesResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
if (source.Value != null && (source.Value is IMember) == 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 (IMember)
}));
return source.New(
//perform the mapping with the current umbraco context
ResolveCore(source.Context.GetUmbracoContext(), (IMember)source.Value), typeof(IEnumerable<ContentPropertyDisplay>));
}
private IEnumerable<ContentPropertyBasic> ResolveCore(UmbracoContext umbracoContext, IMember content)
{
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyBasic>>(
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
content.Properties.OrderBy(prop => prop.PropertyType.SortOrder))
.ToList();
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 sensitive
prop.IsSensitive = true;
//clear the value
prop.Value = null;
}
}
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;
}
}
}