private IEnumerable <AssignedUserGroupPermissions> GetDetailedPermissions(IContent content, IEnumerable <IUserGroup> allUserGroups) { //get all user groups and map their default permissions to the AssignedUserGroupPermissions model. //we do this because not all groups will have true assigned permissions for this node so if they don't have assigned permissions, we need to show the defaults. var defaultPermissionsByGroup = Mapper.Map <IEnumerable <AssignedUserGroupPermissions> >(allUserGroups).ToArray(); var defaultPermissionsAsDictionary = defaultPermissionsByGroup .ToDictionary(x => Convert.ToInt32(x.Id), x => x); //get the actual assigned permissions var assignedPermissionsByGroup = Services.ContentService.GetPermissionsForEntity(content).ToArray(); //iterate over assigned and update the defaults with the real values foreach (var assignedGroupPermission in assignedPermissionsByGroup) { var defaultUserGroupPermissions = defaultPermissionsAsDictionary[assignedGroupPermission.UserGroupId]; //clone the default permissions model to the assigned ones defaultUserGroupPermissions.AssignedPermissions = AssignedUserGroupPermissions.ClonePermissions(defaultUserGroupPermissions.DefaultPermissions); //since there is custom permissions assigned to this node for this group, we need to clear all of the default permissions //and we'll re-check it if it's one of the explicitly assigned ones foreach (var permission in defaultUserGroupPermissions.AssignedPermissions.SelectMany(x => x.Value)) { permission.Checked = false; permission.Checked = assignedGroupPermission.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture); } } return(defaultPermissionsByGroup); }
// Umbraco.Code.MapAll -Udi -Trashed -AdditionalData -AssignedPermissions private void Map(IUserGroup source, AssignedUserGroupPermissions target, MapperContext context) { target.Id = source.Id; target.Alias = source.Alias; target.Icon = source.Icon; target.Key = source.Key; target.Name = source.Name; target.ParentId = -1; target.Path = "-1," + source.Id; target.DefaultPermissions = MapUserGroupDefaultPermissions(source); if (target.Icon.IsNullOrWhiteSpace()) { target.Icon = "icon-users"; } }
// Umbraco.Code.MapAll -ContentStartNode -MediaStartNode -Sections -Notifications -Udi // Umbraco.Code.MapAll -Trashed -AdditionalData -Users -AssignedPermissions private void Map(IUserGroup source, UserGroupDisplay target, MapperContext context) { target.Alias = source.Alias; target.DefaultPermissions = MapUserGroupDefaultPermissions(source); target.Icon = source.Icon; target.Id = source.Id; target.Key = source.Key; target.Name = source.Name; target.ParentId = -1; target.Path = "-1," + source.Id; target.UserCount = source.UserCount; MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); //Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that // this will cause an N+1 and we'll need to change how this works. var users = _userService.GetAllInGroup(source.Id); target.Users = context.MapEnumerable <IUser, UserBasic>(users); //Deal with assigned permissions: var allContentPermissions = _userService.GetPermissions(source, true) .ToDictionary(x => x.EntityId, x => x); IEntitySlim[] contentEntities; if (allContentPermissions.Keys.Count == 0) { contentEntities = Array.Empty <IEntitySlim>(); } else { // a group can end up with way more than 2000 assigned permissions, // so we need to break them into groups in order to avoid breaking // the entity service due to too many Sql parameters. var list = new List <IEntitySlim>(); foreach (var idGroup in allContentPermissions.Keys.InGroupsOf(2000)) { list.AddRange(_entityService.GetAll(UmbracoObjectTypes.Document, idGroup.ToArray())); } contentEntities = list.ToArray(); } var allAssignedPermissions = new List <AssignedContentPermissions>(); foreach (var entity in contentEntities) { var contentPermissions = allContentPermissions[entity.Id]; var assignedContentPermissions = context.Map <AssignedContentPermissions>(entity); assignedContentPermissions.AssignedPermissions = AssignedUserGroupPermissions.ClonePermissions(target.DefaultPermissions); //since there is custom permissions assigned to this node for this group, we need to clear all of the default permissions //and we'll re-check it if it's one of the explicitly assigned ones foreach (var permission in assignedContentPermissions.AssignedPermissions.SelectMany(x => x.Value)) { permission.Checked = false; permission.Checked = contentPermissions.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture); } allAssignedPermissions.Add(assignedContentPermissions); } target.AssignedPermissions = allAssignedPermissions; }
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap <UserGroupSave, IUserGroup>() .ConstructUsing((UserGroupSave save) => new UserGroup() { CreateDate = DateTime.Now }) .IgnoreDeletableEntityCommonProperties() .ForMember(dest => dest.Id, map => map.Condition(source => GetIntId(source.Id) > 0)) .ForMember(dest => dest.Id, map => map.MapFrom(source => GetIntId(source.Id))) .ForMember(dest => dest.Permissions, map => map.MapFrom(source => source.DefaultPermissions)) .AfterMap((save, userGroup) => { userGroup.ClearAllowedSections(); foreach (var section in save.Sections) { userGroup.AddAllowedSection(section); } }); //Used for merging existing UserSave to an existing IUser instance - this will not create an IUser instance! config.CreateMap <UserSave, IUser>() .IgnoreDeletableEntityCommonProperties() .ForMember(dest => dest.Id, map => map.Condition(source => GetIntId(source.Id) > 0)) .ForMember(detail => detail.SessionTimeout, opt => opt.Ignore()) .ForMember(detail => detail.EmailConfirmedDate, opt => opt.Ignore()) .ForMember(detail => detail.UserType, opt => opt.Ignore()) .ForMember(detail => detail.StartContentId, opt => opt.Ignore()) .ForMember(detail => detail.StartMediaId, opt => opt.Ignore()) .ForMember(detail => detail.InvitedDate, opt => opt.Ignore()) .ForMember(detail => detail.SecurityStamp, opt => opt.Ignore()) .ForMember(detail => detail.Avatar, opt => opt.Ignore()) .ForMember(detail => detail.ProviderUserKey, opt => opt.Ignore()) .ForMember(detail => detail.RawPasswordValue, opt => opt.Ignore()) .ForMember(detail => detail.RawPasswordAnswerValue, opt => opt.Ignore()) .ForMember(detail => detail.PasswordQuestion, opt => opt.Ignore()) .ForMember(detail => detail.Comments, opt => opt.Ignore()) .ForMember(detail => detail.IsApproved, opt => opt.Ignore()) .ForMember(detail => detail.IsLockedOut, opt => opt.Ignore()) .ForMember(detail => detail.LastLoginDate, opt => opt.Ignore()) .ForMember(detail => detail.LastPasswordChangeDate, opt => opt.Ignore()) .ForMember(detail => detail.LastLockoutDate, opt => opt.Ignore()) .ForMember(detail => detail.FailedPasswordAttempts, opt => opt.Ignore()) .ForMember(user => user.Language, expression => expression.MapFrom(save => save.Culture)) .AfterMap((save, user) => { user.ClearGroups(); var foundGroups = applicationContext.Services.UserService.GetUserGroupsByAlias(save.UserGroups.ToArray()); foreach (var group in foundGroups) { user.AddGroup(group.ToReadOnlyGroup()); } }); config.CreateMap <UserInvite, IUser>() .IgnoreDeletableEntityCommonProperties() .ForMember(detail => detail.Id, opt => opt.Ignore()) .ForMember(detail => detail.StartContentIds, opt => opt.Ignore()) .ForMember(detail => detail.StartMediaIds, opt => opt.Ignore()) .ForMember(detail => detail.UserType, opt => opt.Ignore()) .ForMember(detail => detail.StartContentId, opt => opt.Ignore()) .ForMember(detail => detail.StartMediaId, opt => opt.Ignore()) .ForMember(detail => detail.Language, opt => opt.Ignore()) .ForMember(detail => detail.Username, opt => opt.Ignore()) .ForMember(detail => detail.PasswordQuestion, opt => opt.Ignore()) .ForMember(detail => detail.SessionTimeout, opt => opt.Ignore()) .ForMember(detail => detail.EmailConfirmedDate, opt => opt.Ignore()) .ForMember(detail => detail.InvitedDate, opt => opt.Ignore()) .ForMember(detail => detail.SecurityStamp, opt => opt.Ignore()) .ForMember(detail => detail.Avatar, opt => opt.Ignore()) .ForMember(detail => detail.ProviderUserKey, opt => opt.Ignore()) .ForMember(detail => detail.RawPasswordValue, opt => opt.Ignore()) .ForMember(detail => detail.RawPasswordAnswerValue, opt => opt.Ignore()) .ForMember(detail => detail.Comments, opt => opt.Ignore()) .ForMember(detail => detail.IsApproved, opt => opt.Ignore()) .ForMember(detail => detail.IsLockedOut, opt => opt.Ignore()) .ForMember(detail => detail.LastLoginDate, opt => opt.Ignore()) .ForMember(detail => detail.LastPasswordChangeDate, opt => opt.Ignore()) .ForMember(detail => detail.LastLockoutDate, opt => opt.Ignore()) .ForMember(detail => detail.FailedPasswordAttempts, opt => opt.Ignore()) //all invited users will not be approved, completing the invite will approve the user .ForMember(user => user.IsApproved, expression => expression.UseValue(false)) .AfterMap((invite, user) => { user.ClearGroups(); var foundGroups = applicationContext.Services.UserService.GetUserGroupsByAlias(invite.UserGroups.ToArray()); foreach (var group in foundGroups) { user.AddGroup(group.ToReadOnlyGroup()); } }); config.CreateMap <IReadOnlyUserGroup, UserGroupBasic>() .ForMember(detail => detail.ContentStartNode, opt => opt.Ignore()) .ForMember(detail => detail.UserCount, opt => opt.Ignore()) .ForMember(detail => detail.MediaStartNode, opt => opt.Ignore()) .ForMember(detail => detail.Key, opt => opt.Ignore()) .ForMember(detail => detail.Sections, opt => opt.Ignore()) .ForMember(detail => detail.Notifications, opt => opt.Ignore()) .ForMember(detail => detail.Udi, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id)) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()) .AfterMap((group, display) => { MapUserGroupBasic(applicationContext.Services, group, display); }); config.CreateMap <IUserGroup, UserGroupBasic>() .ForMember(detail => detail.ContentStartNode, opt => opt.Ignore()) .ForMember(detail => detail.MediaStartNode, opt => opt.Ignore()) .ForMember(detail => detail.Sections, opt => opt.Ignore()) .ForMember(detail => detail.Notifications, opt => opt.Ignore()) .ForMember(detail => detail.Udi, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id)) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()) .AfterMap((group, display) => { MapUserGroupBasic(applicationContext.Services, group, display); }); //create a map to assign a user group's default permissions to the AssignedUserGroupPermissions instance config.CreateMap <IUserGroup, AssignedUserGroupPermissions>() .ForMember(detail => detail.Udi, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()) .ForMember(detail => detail.Id, opt => opt.MapFrom(group => group.Id)) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id)) .ForMember(detail => detail.DefaultPermissions, expression => expression.ResolveUsing(new UserGroupDefaultPermissionsResolver(applicationContext.Services.TextService))) //these will be manually mapped and by default they are null .ForMember(detail => detail.AssignedPermissions, opt => opt.Ignore()) .AfterMap((group, display) => { if (display.Icon.IsNullOrWhiteSpace()) { display.Icon = "icon-users"; } }); config.CreateMap <UmbracoEntity, AssignedContentPermissions>() .ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key))) .ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon)) .ForMember(dto => dto.Trashed, expression => expression.Ignore()) .ForMember(x => x.Alias, expression => expression.Ignore()) .ForMember(x => x.AssignedPermissions, expression => expression.Ignore()) .AfterMap((entity, basic) => { if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid && basic.Icon.IsNullOrWhiteSpace()) { basic.Icon = "icon-user"; } }); config.CreateMap <IUserGroup, UserGroupDisplay>() .ForMember(detail => detail.ContentStartNode, opt => opt.Ignore()) .ForMember(detail => detail.MediaStartNode, opt => opt.Ignore()) .ForMember(detail => detail.Sections, opt => opt.Ignore()) .ForMember(detail => detail.Notifications, opt => opt.Ignore()) .ForMember(detail => detail.Udi, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id)) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()) .ForMember(detail => detail.Users, opt => opt.Ignore()) .ForMember(detail => detail.DefaultPermissions, expression => expression.ResolveUsing(new UserGroupDefaultPermissionsResolver(applicationContext.Services.TextService))) .ForMember(detail => detail.AssignedPermissions, opt => opt.Ignore()) .AfterMap((group, display) => { MapUserGroupBasic(applicationContext.Services, group, display); //Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that // this will cause an N+1 and we'll need to change how this works. var users = applicationContext.Services.UserService.GetAllInGroup(group.Id); display.Users = Mapper.Map <IEnumerable <UserBasic> >(users); //Deal with assigned permissions: var allContentPermissions = applicationContext.Services.UserService.GetPermissions(@group, true) .ToDictionary(x => x.EntityId, x => x); var contentEntities = allContentPermissions.Keys.Count == 0 ? new IUmbracoEntity[0] : applicationContext.Services.EntityService.GetAll(UmbracoObjectTypes.Document, allContentPermissions.Keys.ToArray()); var allAssignedPermissions = new List <AssignedContentPermissions>(); foreach (var entity in contentEntities) { var contentPermissions = allContentPermissions[entity.Id]; var assignedContentPermissions = Mapper.Map <AssignedContentPermissions>(entity); assignedContentPermissions.AssignedPermissions = AssignedUserGroupPermissions.ClonePermissions(display.DefaultPermissions); //since there is custom permissions assigned to this node for this group, we need to clear all of the default permissions //and we'll re-check it if it's one of the explicitly assigned ones foreach (var permission in assignedContentPermissions.AssignedPermissions.SelectMany(x => x.Value)) { permission.Checked = false; permission.Checked = contentPermissions.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture); } allAssignedPermissions.Add(assignedContentPermissions); } display.AssignedPermissions = allAssignedPermissions; }); //Important! Currently we are never mapping to multiple UserDisplay objects but if we start doing that // this will cause an N+1 and we'll need to change how this works. config.CreateMap <IUser, UserDisplay>() .ForMember(detail => detail.Avatars, opt => opt.MapFrom(user => user.GetCurrentUserAvatarUrls(applicationContext.Services.UserService, applicationContext.ApplicationCache.RuntimeCache))) .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) .ForMember(detail => detail.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?)user.LastLoginDate)) .ForMember(detail => detail.UserGroups, opt => opt.MapFrom(user => user.Groups)) .ForMember( detail => detail.CalculatedStartContentIds, opt => opt.MapFrom(user => GetStartNodeValues( user.CalculateContentStartNodeIds(applicationContext.Services.EntityService), applicationContext.Services.TextService, applicationContext.Services.EntityService, UmbracoObjectTypes.Document, "content/contentRoot"))) .ForMember( detail => detail.CalculatedStartMediaIds, opt => opt.MapFrom(user => GetStartNodeValues( user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService), applicationContext.Services.TextService, applicationContext.Services.EntityService, UmbracoObjectTypes.Media, "media/mediaRoot"))) .ForMember( detail => detail.StartContentIds, opt => opt.MapFrom(user => GetStartNodeValues( user.StartContentIds.ToArray(), applicationContext.Services.TextService, applicationContext.Services.EntityService, UmbracoObjectTypes.Document, "content/contentRoot"))) .ForMember( detail => detail.StartMediaIds, opt => opt.MapFrom(user => GetStartNodeValues( user.StartMediaIds.ToArray(), applicationContext.Services.TextService, applicationContext.Services.EntityService, UmbracoObjectTypes.Media, "media/mediaRoot"))) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService))) .ForMember( detail => detail.AvailableCultures, opt => opt.MapFrom(user => applicationContext.Services.TextService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName))) .ForMember( detail => detail.EmailHash, opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash())) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(user => "-1," + user.Id)) .ForMember(detail => detail.Notifications, opt => opt.Ignore()) .ForMember(detail => detail.Udi, opt => opt.Ignore()) .ForMember(detail => detail.Icon, opt => opt.Ignore()) .ForMember(detail => detail.IsCurrentUser, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.ResetPasswordValue, opt => opt.Ignore()) .ForMember(detail => detail.Alias, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()); config.CreateMap <IUser, UserBasic>() //Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost //Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look //like the load time is waiting. .ForMember(detail => detail.Avatars, opt => opt.MapFrom(user => user.GetCurrentUserAvatarUrls(applicationContext.Services.UserService, applicationContext.ApplicationCache.RuntimeCache))) .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) .ForMember(detail => detail.UserGroups, opt => opt.MapFrom(user => user.Groups)) .ForMember(detail => detail.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?)user.LastLoginDate)) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService))) .ForMember( detail => detail.EmailHash, opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5())) .ForMember(detail => detail.ParentId, opt => opt.UseValue(-1)) .ForMember(detail => detail.Path, opt => opt.MapFrom(user => "-1," + user.Id)) .ForMember(detail => detail.Notifications, opt => opt.Ignore()) .ForMember(detail => detail.IsCurrentUser, opt => opt.Ignore()) .ForMember(detail => detail.Udi, opt => opt.Ignore()) .ForMember(detail => detail.Icon, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.Alias, opt => opt.Ignore()) .ForMember(detail => detail.Trashed, opt => opt.Ignore()) .ForMember(detail => detail.AdditionalData, opt => opt.Ignore()); config.CreateMap <IUser, UserDetail>() .ForMember(detail => detail.Avatars, opt => opt.MapFrom(user => user.GetCurrentUserAvatarUrls(applicationContext.Services.UserService, applicationContext.ApplicationCache.RuntimeCache))) .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id))) .ForMember(detail => detail.StartContentIds, opt => opt.MapFrom(user => user.CalculateContentStartNodeIds(applicationContext.Services.EntityService))) .ForMember(detail => detail.StartMediaIds, opt => opt.MapFrom(user => user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService))) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService))) .ForMember( detail => detail.EmailHash, opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash())) .ForMember(detail => detail.SecondsUntilTimeout, opt => opt.Ignore()) .ForMember(detail => detail.UserGroups, opt => opt.Ignore()) .AfterMap((user, detail) => { //we need to map the legacy UserType //the best we can do here is to return the user's first user group as a IUserType object //but we should attempt to return any group that is the built in ones first var groups = user.Groups.ToArray(); detail.UserGroups = user.Groups.Select(x => x.Alias).ToArray(); if (groups.Length == 0) { //In backwards compatibility land, a user type cannot be null! so we need to return a fake one. detail.UserType = "temp"; } else { var builtIns = new[] { Constants.Security.AdminGroupAlias, "writer", "editor", Constants.Security.TranslatorGroupAlias }; var foundBuiltIn = groups.FirstOrDefault(x => builtIns.Contains(x.Alias)); if (foundBuiltIn != null) { detail.UserType = foundBuiltIn.Alias; } else { //otherwise return the first detail.UserType = groups[0].Alias; } } }); config.CreateMap <IProfile, UserProfile>() .ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id))); config.CreateMap <IUser, UserData>() .ConstructUsing((IUser user) => new UserData()) .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections.ToArray())) .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups.Select(x => x.Alias).ToArray())) .ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.CalculateContentStartNodeIds(applicationContext.Services.EntityService))) .ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService))) .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService))) .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp)); }