From cbb36a900a08cb5dec2df6a923aafd2a694dbf04 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Wed, 26 Oct 2016 22:38:54 +0200 Subject: [PATCH 01/15] Management of user groups with assignment of users. Migration for creation of user group tables and data amends for moving from permissions on users to those on groups. --- .../Models/Membership/EntityPermission.cs | 16 +- src/Umbraco.Core/Models/Membership/IUser.cs | 21 +- .../Models/Membership/IUserGroup.cs | 13 + .../Models/Membership/IUserType.cs | 24 +- .../Models/Membership/IUserTypeGroupBase.cs | 26 ++ src/Umbraco.Core/Models/Membership/User.cs | 41 +- .../Models/Membership/UserGroup.cs | 36 +- .../Models/Membership/UserType.cs | 62 +-- .../Models/Membership/UserTypeGroupBase.cs | 68 ++++ .../Models/Rdbms/User2UserGroupDto.cs | 19 + .../Models/Rdbms/UserGroup2AppDto.cs | 19 + .../Rdbms/UserGroup2NodePermissionDto.cs | 22 ++ src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs | 32 ++ .../Persistence/Factories/UserFactory.cs | 54 +-- .../Persistence/Factories/UserGroupFactory.cs | 77 ++++ .../Persistence/Mappers/UserGroupMapper.cs | 41 ++ .../Initial/DatabaseSchemaCreation.cs | 10 +- .../AddUserGroupTables.cs | 138 +++++++ .../Relators/UserGroupSectionRelator.cs | 50 +++ .../Interfaces/IUserGroupRepository.cs | 52 +++ .../Interfaces/IUserRepository.cs | 23 +- .../Repositories/UserGroupRepository.cs | 263 +++++++++++++ .../Repositories/UserRepository.cs | 128 +++++- .../Persistence/RepositoryFactory.cs | 8 + src/Umbraco.Core/Services/IUserService.cs | 95 ++++- src/Umbraco.Core/Services/UserService.cs | 372 +++++++++++++++++- src/Umbraco.Core/Umbraco.Core.csproj | 13 + .../Persistence/BaseTableByTableTest.cs | 126 +++--- .../Services/SectionServiceTests.cs | 102 +++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/Umbraco.Web.UI.Client/src/less/forms.less | 5 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 + .../config/trees.Release.config | 5 +- src/Umbraco.Web.UI/config/trees.config | 5 +- .../umbraco/config/create/UI.Release.xml | 20 +- .../umbraco/config/create/UI.xml | 8 + src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 8 +- .../umbraco/config/lang/en_us.xml | 4 + .../umbraco/users/EditUserGroup.aspx | 52 +++ src/Umbraco.Web/Security/WebSecurity.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 12 + .../umbraco/Trees/UserGroups.cs | 50 +++ .../umbraco/Trees/UserTypes.cs | 8 - .../umbraco/users/EditUser.aspx.cs | 108 ++++- .../umbraco/users/EditUserGroup.aspx | 52 +++ .../umbraco/users/EditUserGroup.aspx.cs | 175 ++++++++ .../users/EditUserGroup.aspx.designer.cs | 195 +++++++++ .../umbraco/users/EditUserGroupsBase.cs | 39 ++ src/umbraco.businesslogic/User.cs | 62 +-- src/umbraco.businesslogic/UserGroup.cs | 309 +++++++++++++++ .../umbraco.businesslogic.csproj | 1 + 51 files changed, 2812 insertions(+), 263 deletions(-) create mode 100644 src/Umbraco.Core/Models/Membership/IUserGroup.cs create mode 100644 src/Umbraco.Core/Models/Membership/IUserTypeGroupBase.cs create mode 100644 src/Umbraco.Core/Models/Membership/UserTypeGroupBase.cs create mode 100644 src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs create mode 100644 src/Umbraco.Core/Models/Rdbms/UserGroup2AppDto.cs create mode 100644 src/Umbraco.Core/Models/Rdbms/UserGroup2NodePermissionDto.cs create mode 100644 src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs create mode 100644 src/Umbraco.Core/Persistence/Factories/UserGroupFactory.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs create mode 100644 src/Umbraco.Core/Persistence/Relators/UserGroupSectionRelator.cs create mode 100644 src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs create mode 100644 src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs create mode 100644 src/Umbraco.Tests/Services/SectionServiceTests.cs create mode 100644 src/Umbraco.Web.UI/umbraco/users/EditUserGroup.aspx create mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserGroups.cs create mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx create mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.cs create mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.designer.cs create mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroupsBase.cs create mode 100644 src/umbraco.businesslogic/UserGroup.cs diff --git a/src/Umbraco.Core/Models/Membership/EntityPermission.cs b/src/Umbraco.Core/Models/Membership/EntityPermission.cs index 7ab1ddc817..520a9b8d37 100644 --- a/src/Umbraco.Core/Models/Membership/EntityPermission.cs +++ b/src/Umbraco.Core/Models/Membership/EntityPermission.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Linq; namespace Umbraco.Core.Models.Membership { @@ -21,6 +21,20 @@ namespace Umbraco.Core.Models.Membership /// The assigned permissions for the user/entity combo /// public string[] AssignedPermissions { get; private set; } + + /// + /// Adds additional permissions to an existing instance of + /// ensuring that only ones that aren't already assigned are added + /// + /// + public void AddAdditionalPermissions(string[] additionalPermissions) + { + var newPermissions = AssignedPermissions.ToList(); + newPermissions.AddRange(additionalPermissions); + AssignedPermissions = newPermissions + .Distinct() + .ToArray(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index f1f9c23971..095044c47e 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models.Membership { @@ -15,12 +14,28 @@ namespace Umbraco.Core.Models.Membership int StartContentId { get; set; } int StartMediaId { get; set; } string Language { get; set; } - + /// /// Gets/sets the user type for the user /// IUserType UserType { get; set; } + /// + /// Gets the groups that user is part of + /// + IEnumerable Groups { get; } + + /// + /// Indicates if the groups for a user have been loaded + /// + bool GroupsLoaded { get; } + + void RemoveGroup(IUserGroup group); + + void AddGroup(IUserGroup group); + + void SetGroupsLoaded(); + //TODO: This should be a private set /// /// The default permission set for the user @@ -31,7 +46,9 @@ namespace Umbraco.Core.Models.Membership IEnumerable DefaultPermissions { get; set; } IEnumerable AllowedSections { get; } + void RemoveAllowedSection(string sectionAlias); + void AddAllowedSection(string sectionAlias); /// diff --git a/src/Umbraco.Core/Models/Membership/IUserGroup.cs b/src/Umbraco.Core/Models/Membership/IUserGroup.cs new file mode 100644 index 0000000000..fc1af97912 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/IUserGroup.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Models.Membership +{ + public interface IUserGroup : IUserTypeGroupBase + { + IEnumerable AllowedSections { get; } + + void RemoveAllowedSection(string sectionAlias); + + void AddAllowedSection(string sectionAlias); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IUserType.cs b/src/Umbraco.Core/Models/Membership/IUserType.cs index fe678afd2b..ccfdc729f5 100644 --- a/src/Umbraco.Core/Models/Membership/IUserType.cs +++ b/src/Umbraco.Core/Models/Membership/IUserType.cs @@ -1,28 +1,6 @@ -using System.Collections.Generic; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; - namespace Umbraco.Core.Models.Membership { - - public interface IUserType : IAggregateRoot + public interface IUserType : IUserTypeGroupBase { - /// - /// The user type alias - /// - string Alias { get; set; } - - /// - /// The user type name - /// - string Name { get; set; } - - /// - /// The set of default permissions for the user type - /// - /// - /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. - /// - IEnumerable Permissions { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IUserTypeGroupBase.cs b/src/Umbraco.Core/Models/Membership/IUserTypeGroupBase.cs new file mode 100644 index 0000000000..06769b1866 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/IUserTypeGroupBase.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models.Membership +{ + public interface IUserTypeGroupBase : IAggregateRoot + { + /// + /// The alias + /// + string Alias { get; set; } + + /// + /// The name + /// + string Name { get; set; } + + /// + /// The set of default permissions + /// + /// + /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. + /// + IEnumerable Permissions { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index cadda14508..6fad1a6f78 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -8,7 +8,6 @@ using System.Runtime.Serialization; using Umbraco.Core.Configuration; using Umbraco.Core.Models.EntityBase; - namespace Umbraco.Core.Models.Membership { /// @@ -29,6 +28,7 @@ namespace Umbraco.Core.Models.Membership _sectionCollection = new ObservableCollection(); _addedSections = new List(); _removedSections = new List(); + _groupCollection = new List(); _language = GlobalSettings.DefaultUILanguage; _sectionCollection.CollectionChanged += SectionCollectionChanged; _isApproved = true; @@ -58,6 +58,8 @@ namespace Umbraco.Core.Models.Membership private List _addedSections; private List _removedSections; private ObservableCollection _sectionCollection; + private List _groupCollection; + private bool _groupsLoaded; private int _sessionTimeout; private int _startContentId; private int _startMediaId; @@ -324,6 +326,41 @@ namespace Umbraco.Core.Models.Membership } } + /// + /// Gets or sets the groups that user is part of + /// + [DataMember] + public IEnumerable Groups + { + get { return _groupCollection; } + } + + /// + /// Indicates if the groups for a user have been loaded + /// + public bool GroupsLoaded { get { return _groupsLoaded; } } + + public void RemoveGroup(IUserGroup group) + { + if (_groupCollection.Select(x => x.Id).Contains(group.Id)) + { + _groupCollection.Remove(group); + } + } + + public void AddGroup(IUserGroup group) + { + if (_groupCollection.Select(x => x.Id).Contains(group.Id) == false) + { + _groupCollection.Add(group); + } + } + + public void SetGroupsLoaded() + { + _groupsLoaded = true; + } + #endregion /// @@ -365,7 +402,6 @@ namespace Umbraco.Core.Models.Membership { _removedSections.Add(item); } - } } @@ -378,6 +414,7 @@ namespace Umbraco.Core.Models.Membership clone._addedSections = new List(); clone._removedSections = new List(); clone._sectionCollection = new ObservableCollection(_sectionCollection.ToList()); + clone._groupCollection = new List(_groupCollection.ToList()); clone._defaultPermissions = new List(_defaultPermissions.ToList()); //re-create the event handler clone._sectionCollection.CollectionChanged += clone.SectionCollectionChanged; diff --git a/src/Umbraco.Core/Models/Membership/UserGroup.cs b/src/Umbraco.Core/Models/Membership/UserGroup.cs index b0f10d9ff1..403e40c8df 100644 --- a/src/Umbraco.Core/Models/Membership/UserGroup.cs +++ b/src/Umbraco.Core/Models/Membership/UserGroup.cs @@ -1,20 +1,42 @@ using System; +using System.Collections.Generic; using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models.Membership { /// /// Represents a Group for a Backoffice User /// - /// - /// Should be internal until a proper user/membership implementation - /// is part of the roadmap. - /// [Serializable] [DataContract(IsReference = true)] - internal class UserGroup : Entity + internal class UserGroup : UserTypeGroupBase, IUserGroup { - //Add UserCollection ? + private List _sectionCollection; + + public UserGroup() + { + _sectionCollection = new List(); + } + + public IEnumerable AllowedSections + { + get { return _sectionCollection; } + } + + public void RemoveAllowedSection(string sectionAlias) + { + if (_sectionCollection.Contains(sectionAlias)) + { + _sectionCollection.Remove(sectionAlias); + } + } + + public void AddAllowedSection(string sectionAlias) + { + if (_sectionCollection.Contains(sectionAlias) == false) + { + _sectionCollection.Add(sectionAlias); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserType.cs b/src/Umbraco.Core/Models/Membership/UserType.cs index 01183e9d9d..92e0032bd6 100644 --- a/src/Umbraco.Core/Models/Membership/UserType.cs +++ b/src/Umbraco.Core/Models/Membership/UserType.cs @@ -1,72 +1,14 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Strings; namespace Umbraco.Core.Models.Membership { /// - /// Represents the Type for a Backoffice User + /// Represents the Type for a back-office User /// [Serializable] [DataContract(IsReference = true)] - internal class UserType : Entity, IUserType + internal class UserType : UserTypeGroupBase, IUserType { - private string _alias; - private string _name; - private IEnumerable _permissions; - - private static readonly Lazy Ps = new Lazy(); - - private class PropertySelectors - { - public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); - public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); - public readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.Permissions); - } - - [DataMember] - public string Alias - { - get { return _alias; } - set - { - SetPropertyValueAndDetectChanges( - value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase), - ref _alias, - Ps.Value.AliasSelector); - } - } - - [DataMember] - public string Name - { - get { return _name; } - set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); } - } - - /// - /// The set of default permissions for the user type - /// - /// - /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. - /// - [DataMember] - public IEnumerable Permissions - { - get { return _permissions; } - set - { - SetPropertyValueAndDetectChanges(value, ref _permissions, Ps.Value.PermissionsSelector, - //Custom comparer for enumerable - new DelegateEqualityComparer>( - (enum1, enum2) => enum1.UnsortedSequenceEqual(enum2), - enum1 => enum1.GetHashCode())); - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserTypeGroupBase.cs b/src/Umbraco.Core/Models/Membership/UserTypeGroupBase.cs new file mode 100644 index 0000000000..b858747eb1 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/UserTypeGroupBase.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Strings; + +namespace Umbraco.Core.Models.Membership +{ + /// + /// Base class representing common fields for back-office user types and groups + /// + internal abstract class UserTypeGroupBase : Entity + { + private string _alias; + private string _name; + private IEnumerable _permissions; + + private static readonly Lazy Ps = new Lazy(); + + private class PropertySelectors + { + public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); + public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); + public readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.Permissions); + } + + [DataMember] + public string Alias + { + get { return _alias; } + set + { + SetPropertyValueAndDetectChanges( + value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase), + ref _alias, + Ps.Value.AliasSelector); + } + } + + [DataMember] + public string Name + { + get { return _name; } + set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); } + } + + /// + /// The set of default permissions for the user type + /// + /// + /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. + /// + [DataMember] + public IEnumerable Permissions + { + get { return _permissions; } + set + { + SetPropertyValueAndDetectChanges(value, ref _permissions, Ps.Value.PermissionsSelector, + //Custom comparer for enumerable + new DelegateEqualityComparer>( + (enum1, enum2) => enum1.UnsortedSequenceEqual(enum2), + enum1 => enum1.GetHashCode())); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs new file mode 100644 index 0000000000..afdbf661b5 --- /dev/null +++ b/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs @@ -0,0 +1,19 @@ +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("umbracoUser2UserGroup")] + [ExplicitColumns] + internal class User2UserGroupDto + { + [Column("userId")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2userGroup", OnColumns = "userId, userGroupId")] + [ForeignKey(typeof(UserDto))] + public int UserId { get; set; } + + [Column("userGroupId")] + [ForeignKey(typeof(UserGroupDto))] + public int UserGroupId { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/UserGroup2AppDto.cs b/src/Umbraco.Core/Models/Rdbms/UserGroup2AppDto.cs new file mode 100644 index 0000000000..14fc60e197 --- /dev/null +++ b/src/Umbraco.Core/Models/Rdbms/UserGroup2AppDto.cs @@ -0,0 +1,19 @@ +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("umbracoUserGroup2App")] + [ExplicitColumns] + internal class UserGroup2AppDto + { + [Column("userGroupId")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_userGroup2App", OnColumns = "userGroupId, app")] + [ForeignKey(typeof(UserGroupDto))] + public int UserGroupId { get; set; } + + [Column("app")] + [Length(50)] + public string AppAlias { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/UserGroup2NodePermissionDto.cs b/src/Umbraco.Core/Models/Rdbms/UserGroup2NodePermissionDto.cs new file mode 100644 index 0000000000..657aad2f31 --- /dev/null +++ b/src/Umbraco.Core/Models/Rdbms/UserGroup2NodePermissionDto.cs @@ -0,0 +1,22 @@ +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("umbracoUserGroup2NodePermission")] + [ExplicitColumns] + internal class UserGroup2NodePermissionDto + { + [Column("userGroupId")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoUserGroup2NodePermission", OnColumns = "userGroupId, nodeId, permission")] + [ForeignKey(typeof(UserGroupDto))] + public int UserGroupId { get; set; } + + [Column("nodeId")] + [ForeignKey(typeof(NodeDto))] + public int NodeId { get; set; } + + [Column("permission")] + public string Permission { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs new file mode 100644 index 0000000000..dd1023b68a --- /dev/null +++ b/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("umbracoUserGroup")] + [PrimaryKey("id")] + [ExplicitColumns] + internal class UserGroupDto + { + [Column("id")] + [PrimaryKeyColumn(IdentitySeed = 5)] + public int Id { get; set; } + + [Column("userGroupAlias")] + [Length(200)] + public string Alias { get; set; } + + [Column("userGroupName")] + [Length(200)] + public string Name { get; set; } + + [Column("userGroupDefaultPermissions")] + [Length(50)] + [NullSetting(NullSetting = NullSettings.Null)] + public string DefaultPermissions { get; set; } + + [ResultColumn] + public List UserGroup2AppDtos { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index 0a13c82447..322e8b5b65 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; @@ -44,9 +43,12 @@ namespace Umbraco.Core.Persistence.Factories user.LastLoginDate = dto.LastLoginDate ?? DateTime.MinValue; user.LastPasswordChangeDate = dto.LastPasswordChangeDate ?? DateTime.MinValue; - foreach (var app in dto.User2AppDtos) + if (dto.User2AppDtos != null) { - user.AddAllowedSection(app.AppAlias); + foreach (var app in dto.User2AppDtos) + { + user.AddAllowedSection(app.AppAlias); + } } //on initial construction we don't want to have dirty properties tracked @@ -64,41 +66,43 @@ namespace Umbraco.Core.Persistence.Factories public UserDto BuildDto(IUser entity) { var dto = new UserDto - { - ContentStartId = entity.StartContentId, - MediaStartId = entity.StartMediaId, - Disabled = entity.IsApproved == false, - Email = entity.Email, - Login = entity.Username, - NoConsole = entity.IsLockedOut, - Password = entity.RawPasswordValue, - UserLanguage = entity.Language, - UserName = entity.Name, - Type = short.Parse(entity.UserType.Id.ToString(CultureInfo.InvariantCulture)), - User2AppDtos = new List(), - SecurityStampToken = entity.SecurityStamp, - FailedLoginAttempts = entity.FailedPasswordAttempts, - LastLockoutDate = entity.LastLockoutDate == DateTime.MinValue ? (DateTime?)null : entity.LastLockoutDate, - LastLoginDate = entity.LastLoginDate == DateTime.MinValue ? (DateTime?)null : entity.LastLoginDate, - LastPasswordChangeDate = entity.LastPasswordChangeDate == DateTime.MinValue ? (DateTime?)null : entity.LastPasswordChangeDate, - }; + { + ContentStartId = entity.StartContentId, + MediaStartId = entity.StartMediaId, + Disabled = entity.IsApproved == false, + Email = entity.Email, + Login = entity.Username, + NoConsole = entity.IsLockedOut, + Password = entity.RawPasswordValue, + UserLanguage = entity.Language, + UserName = entity.Name, + Type = short.Parse(entity.UserType.Id.ToString(CultureInfo.InvariantCulture)), + User2AppDtos = new List(), + SecurityStampToken = entity.SecurityStamp, + FailedLoginAttempts = entity.FailedPasswordAttempts, + LastLockoutDate = entity.LastLockoutDate == DateTime.MinValue ? (DateTime?)null : entity.LastLockoutDate, + LastLoginDate = entity.LastLoginDate == DateTime.MinValue ? (DateTime?)null : entity.LastLoginDate, + LastPasswordChangeDate = entity.LastPasswordChangeDate == DateTime.MinValue ? (DateTime?)null : entity.LastPasswordChangeDate, + }; foreach (var app in entity.AllowedSections) { var appDto = new User2AppDto - { - AppAlias = app - }; + { + AppAlias = app + }; if (entity.HasIdentity) { - appDto.UserId = (int) entity.Id; + appDto.UserId = entity.Id; } dto.User2AppDtos.Add(appDto); } if (entity.HasIdentity) + { dto.Id = entity.Id.SafeCast(); + } return dto; } diff --git a/src/Umbraco.Core/Persistence/Factories/UserGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserGroupFactory.cs new file mode 100644 index 0000000000..39a4d13f7f --- /dev/null +++ b/src/Umbraco.Core/Persistence/Factories/UserGroupFactory.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class UserGroupFactory + { + #region Implementation of IEntityFactory + + public IUserGroup BuildEntity(UserGroupDto dto) + { + var userGroup = new UserGroup(); + + try + { + userGroup.DisableChangeTracking(); + + userGroup.Alias = dto.Alias; + userGroup.Id = dto.Id; + userGroup.Name = dto.Name; + userGroup.Permissions = dto.DefaultPermissions.IsNullOrWhiteSpace() + ? Enumerable.Empty() + : dto.DefaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); + + if (dto.UserGroup2AppDtos != null) + { + foreach (var app in dto.UserGroup2AppDtos) + { + userGroup.AddAllowedSection(app.AppAlias); + } + } + + userGroup.ResetDirtyProperties(false); + return userGroup; + } + finally + { + userGroup.EnableChangeTracking(); + } + } + + public UserGroupDto BuildDto(IUserGroup entity) + { + var dto = new UserGroupDto + { + Alias = entity.Alias, + DefaultPermissions = entity.Permissions == null ? "" : string.Join("", entity.Permissions), + Name = entity.Name, + UserGroup2AppDtos = new List(), + }; + + foreach (var app in entity.AllowedSections) + { + var appDto = new UserGroup2AppDto + { + AppAlias = app + }; + if (entity.HasIdentity) + { + appDto.UserGroupId = entity.Id; + } + + dto.UserGroup2AppDtos.Add(appDto); + } + + if (entity.HasIdentity) + dto.Id = short.Parse(entity.Id.ToString()); + + return dto; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs new file mode 100644 index 0000000000..69386063db --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs @@ -0,0 +1,41 @@ +using System.Collections.Concurrent; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + [MapperFor(typeof(IUserGroup))] + [MapperFor(typeof(UserGroup))] + public sealed class UserGroupMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public UserGroupMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.Alias, dto => dto.Alias); + CacheMap(src => src.Name, dto => dto.Name); + CacheMap(src => src.Permissions, dto => dto.DefaultPermissions); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index 423c847c47..6c06d1f06c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -73,9 +73,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial {34, typeof (TaskDto)}, {35, typeof (ContentType2ContentTypeDto)}, {36, typeof (ContentTypeAllowedContentTypeDto)}, - {37, typeof (User2AppDto)}, + // Removed in 7.6 {37, typeof (User2AppDto)}, {38, typeof (User2NodeNotifyDto)}, - {39, typeof (User2NodePermissionDto)}, + // Removed in 7.6 {39, typeof (User2NodePermissionDto)}, {40, typeof (ServerRegistrationDto)}, {41, typeof (AccessDto)}, {42, typeof (AccessRuleDto)}, @@ -84,7 +84,11 @@ namespace Umbraco.Core.Persistence.Migrations.Initial {45, typeof (MigrationDto)}, {46, typeof (UmbracoDeployChecksumDto)}, {47, typeof (UmbracoDeployDependencyDto)}, - {48, typeof (RedirectUrlDto) } + {48, typeof (RedirectUrlDto) }, + {49, typeof (UserGroupDto) }, + {50, typeof (User2UserGroupDto) }, + {51, typeof (UserGroup2NodePermissionDto) }, + {52, typeof (UserGroup2AppDto) }, }; #endregion diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs new file mode 100644 index 0000000000..ac5a121b9c --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs @@ -0,0 +1,138 @@ +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; +using System.Data; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero +{ + [Migration("7.6.0", 100, GlobalSettings.UmbracoMigrationName)] + public class AddUserGroupTables : MigrationBase + { + public AddUserGroupTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { } + + public override void Up() + { + var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray(); + + AddNewTables(tables); + MigratePermissionsData(); + DeleteOldTables(tables); + } + + private void AddNewTables(string[] tables) + { + if (tables.InvariantContains("umbracoUserGroup") == false) + { + Create.Table("umbracoUserGroup") + .WithColumn("id").AsInt32().Identity().PrimaryKey("PK_umbracoUserGroup") + .WithColumn("userGroupAlias").AsString(200).NotNullable() + .WithColumn("userGroupName").AsString(200).NotNullable() + .WithColumn("userGroupDefaultPermissions").AsString(50).Nullable(); + } + + if (tables.InvariantContains("umbracoUser2UserGroup") == false) + { + Create.Table("umbracoUser2UserGroup") + .WithColumn("userId").AsInt32().NotNullable() + .WithColumn("userGroupId").AsInt32().NotNullable(); + Create.PrimaryKey("PK_user2userGroup") + .OnTable("umbracoUser2UserGroup") + .Columns(new[] {"userId", "userGroupId"}); + Create.ForeignKey("FK_umbracoUser2UserGroup_userId") + .FromTable("umbracoUser2UserGroup").ForeignColumn("userId") + .ToTable("umbracoUser").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); + Create.ForeignKey("FK_umbracoUser2UserGroup_userGroupId") + .FromTable("umbracoUser2UserGroup").ForeignColumn("userGroupId") + .ToTable("umbracoUserGroup").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); + } + + if (tables.InvariantContains("umbracoUserGroup2App") == false) + { + Create.Table("umbracoUserGroup2App") + .WithColumn("userGroupId").AsInt32().NotNullable() + .WithColumn("app").AsString(50).NotNullable(); + Create.PrimaryKey("PK_userGroup2App") + .OnTable("umbracoUserGroup2App") + .Columns(new[] {"userGroupId", "app"}); + Create.ForeignKey("FK_umbracoUserGroup2App_umbracoGroupUser_id") + .FromTable("umbracoUserGroup2App").ForeignColumn("userGroupId") + .ToTable("umbracoUserGroup").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); + } + + if (tables.InvariantContains("umbracoUserGroup2NodePermission") == false) + { + Create.Table("umbracoUserGroup2NodePermission") + .WithColumn("userGroupId").AsInt32().NotNullable() + .WithColumn("nodeId").AsInt32().NotNullable() + .WithColumn("permission").AsString(10).NotNullable(); + Create.PrimaryKey("PK_umbracoUserGroup2NodePermission") + .OnTable("umbracoUserGroup2NodePermission") + .Columns(new[] {"userGroupId", "nodeId", "permission"}); + Create.ForeignKey("FK_umbracoUserGroup2NodePermission_umbracoNode_id") + .FromTable("umbracoUserGroup2NodePermission").ForeignColumn("nodeId") + .ToTable("umbracoNode").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); + Create.ForeignKey("FK_umbracoUserGroup2NodePermission_umbracoUserGroup_id") + .FromTable("umbracoUserGroup2NodePermission").ForeignColumn("userGroupId") + .ToTable("umbracoUserGroup").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); + } + } + + private void MigratePermissionsData() + { + // TODO: review for MySQL and CE (tested only on SQL Express) + + // Create user group records for all non-admin users that have specific permissions set + Execute.Sql(@"INSERT INTO umbracoUserGroup(userGroupAlias, userGroupName) + SELECT userName + 'Group', 'Group for ' + userName + FROM umbracoUser + WHERE (id IN ( + SELECT [user] + FROM umbracoUser2app + ) OR id IN ( + SELECT userid + FROM umbracoUser2NodePermission + )) + AND id > 0"); + + // Associate those groups with the users + Execute.Sql(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId) + SELECT (SELECT id from umbracoUser WHERE userName + 'Group' = umbracoUserGroup.userGroupAlias), id + FROM umbracoUserGroup"); + + // Create node permissions on the groups + Execute.Sql(@"INSERT INTO umbracoUserGroup2NodePermission (userGroupId,nodeId,permission) + SELECT ug.id, nodeId, permission + FROM umbracoUserGroup ug + INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id + INNER JOIN umbracoUser u ON u.id = u2ug.userId + INNER JOIN umbracoUser2NodePermission u2np ON u2np.userId = u.id"); + + // Create app permissions on the groups + Execute.Sql(@"INSERT INTO umbracoUserGroup2app (userGroupId,app) + SELECT ug.id, app + FROM umbracoUserGroup ug + INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id + INNER JOIN umbracoUser u ON u.id = u2ug.userId + INNER JOIN umbracoUser2app u2a ON u2a.[user] = u.id"); + } + + private void DeleteOldTables(string[] tables) + { + if (tables.InvariantContains("umbracoUser2App")) + { + Delete.Table("umbracoUser2App"); + } + + if (tables.InvariantContains("umbracoUser2NodePermission")) + { + Delete.Table("umbracoUser2NodePermission"); + } + } + + public override void Down() + { } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Relators/UserGroupSectionRelator.cs b/src/Umbraco.Core/Persistence/Relators/UserGroupSectionRelator.cs new file mode 100644 index 0000000000..03c5bcd398 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Relators/UserGroupSectionRelator.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Relators +{ + internal class UserGroupSectionRelator + { + internal UserGroupDto Current; + + internal UserGroupDto Map(UserGroupDto a, UserGroup2AppDto p) + { + // Terminating call. Since we can return null from this function + // we need to be ready for PetaPoco to callback later with null + // parameters + if (a == null) + return Current; + + // Is this the same DictionaryItem as the current one we're processing + if (Current != null && Current.Id == a.Id) + { + if (p.AppAlias.IsNullOrWhiteSpace() == false) + { + // Yes, just add this UserGroup2AppDto to the current item's collection + Current.UserGroup2AppDtos.Add(p); + } + + // Return null to indicate we're not done with this User yet + return null; + } + + // This is a different user group to the current one, or this is the + // first time through and we don't have one yet + + // Save the current user group + var prev = Current; + + // Setup the new current user group + Current = a; + Current.UserGroup2AppDtos = new List(); + //this can be null since we are doing a left join + if (p.AppAlias.IsNullOrWhiteSpace() == false) + { + Current.UserGroup2AppDtos.Add(p); + } + + // Return the now populated previous user group (or null if first time through) + return prev; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs new file mode 100644 index 0000000000..e50d265228 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IUserGroupRepository : IRepositoryQueryable + { + /// + /// This is useful when an entire section is removed from config + /// + /// + IEnumerable GetGroupsAssignedToSection(string sectionAlias); + + /// + /// Removes all users from a group + /// + /// Id of group + void RemoveAllUsersFromGroup(int groupId); + + /// + /// Adds a set of users to a group + /// + /// Id of group + /// Ids of users + void AddUsersToGroup(int groupId, int[] userIds); + + /* + /// + /// Gets the group permissions for the specified entities + /// + /// Id of group + /// Array of entity Ids + IEnumerable GetPermissionsForEntities(int groupId, params int[] entityIds); + + /// + /// Replaces the same permission set for a single group to any number of entities + /// + /// Id of group + /// Permissions as enumerable list of + /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. + void ReplaceGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds); + + /// + /// Assigns the same permission set for a single group to any number of entities + /// + /// Id of group + /// Permissions as enumerable list of + /// Specify the nodes to replace permissions for + void AssignGroupPermission(int groupId, char permission, params int[] entityIds); + */ + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index 542cceac40..5645cafa2a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -28,6 +28,25 @@ namespace Umbraco.Core.Persistence.Repositories /// IEnumerable GetUsersAssignedToSection(string sectionAlias); + /// + /// Gets all groups for a given user + /// + /// Id of user + /// An enumerable list of + IEnumerable GetGroupsForUser(int userId); + + /// + /// Gets a list of objects associated with a given group + /// + /// Id of group + IEnumerable GetAllInGroup(int groupId); + + /// + /// Gets a list of objects not associated with a given group + /// + /// Id of group + IEnumerable GetAllNotInGroup(int groupId); + /// /// Gets paged member results /// @@ -38,8 +57,8 @@ namespace Umbraco.Core.Persistence.Repositories /// /// IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy); - - + + /// /// Gets the user permissions for the specified entities /// diff --git a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs new file mode 100644 index 0000000000..ba47270379 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Relators; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + /// + /// Represents the UserGroupRepository for doing CRUD operations for + /// + internal class UserGroupRepository : PetaPocoRepositoryBase, IUserGroupRepository + { + private readonly CacheHelper _cacheHelper; + + public UserGroupRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax) + : base(work, cacheHelper, logger, sqlSyntax) + { + _cacheHelper = cacheHelper; + } + + public IEnumerable GetGroupsAssignedToSection(string sectionAlias) + { + //Here we're building up a query that looks like this, a sub query is required because the resulting structure + // needs to still contain all of the section rows per user group. + + //SELECT * + //FROM [umbracoUserGroup] + //LEFT JOIN [umbracoUserGroup2App] + //ON [umbracoUserGroup].[id] = [umbracoUserGroup2App].[user] + //WHERE umbracoUserGroup.id IN (SELECT umbracoUserGroup.id + // FROM [umbracoUserGroup] + // LEFT JOIN [umbracoUserGroup2App] + // ON [umbracoUserGroup].[id] = [umbracoUserGroup2App].[user] + // WHERE umbracoUserGroup2App.app = 'content') + + var sql = GetBaseQuery(false); + var innerSql = GetBaseQuery("umbracoUserGroup.id"); + innerSql.Where("umbracoUserGroup2App.app = " + SqlSyntax.GetQuotedValue(sectionAlias)); + sql.Where(string.Format("umbracoUserGroup.id IN ({0})", innerSql.SQL)); + + return ConvertFromDtos(Database.Fetch(new UserGroupSectionRelator().Map, sql)); + } + + /// + /// Removes all users from a group + /// + /// Id of group + public void RemoveAllUsersFromGroup(int groupId) + { + Database.Delete("WHERE userGroupId = @GroupId", new { GroupId = groupId }); + } + + /// + /// Adds a set of users to a group + /// + /// Id of group + /// Ids of users + public void AddUsersToGroup(int groupId, int[] userIds) + { + foreach (var userId in userIds) + { + var dto = new User2UserGroupDto + { + UserGroupId = groupId, + UserId = userId, + }; + Database.Insert(dto); + } + } + + /* + /// + /// Gets the group permissions for the specified entities + /// + /// Id of group + /// Array of entity Ids + public IEnumerable GetGroupPermissionsForEntities(int groupId, params int[] entityIds) + { + var repo = new GroupPermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + return repo.GetGroupPermissionsForEntities(groupId, entityIds); + } + + /// + /// Replaces the same permission set for a single group to any number of entities + /// + /// Id of group + /// Permissions as enumerable list of + /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. + public void ReplaceGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds) + { + var repo = new GroupPermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + repo.ReplaceGroupPermissions(groupId, permissions, entityIds); + } + + /// + /// Assigns the same permission set for a single group to any number of entities + /// + /// Id of group + /// Permissions as enumerable list of + /// Specify the nodes to replace permissions for + public void AssignGroupPermission(int groupId, char permission, params int[] entityIds) + { + var repo = new GroupPermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + repo.AssignGroupPermission(groupId, permission, entityIds); + } + */ + + #region Overrides of RepositoryBase + + protected override IUserGroup PerformGet(int id) + { + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + + var dto = Database.Fetch(new UserGroupSectionRelator().Map, sql).FirstOrDefault(); + + if (dto == null) + return null; + + var userGroupFactory = new UserGroupFactory(); + var userGroup = userGroupFactory.BuildEntity(dto); + return userGroup; + } + + protected override IEnumerable PerformGetAll(params int[] ids) + { + var sql = GetBaseQuery(false); + + if (ids.Any()) + { + sql.Where("umbracoUserGroup.id in (@ids)", new { ids = ids }); + } + else + { + sql.Where(x => x.Id >= 0); + } + + var dtos = Database.Fetch(new UserGroupSectionRelator().Map, sql); + return ConvertFromDtos(dtos); + } + + protected override IEnumerable PerformGetByQuery(IQuery query) + { + var userGroupFactory = new UserGroupFactory(); + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate(); + + var dtos = Database.Fetch(new UserGroupSectionRelator().Map, sql); + return ConvertFromDtos(dtos); + } + + #endregion + + #region Overrides of PetaPocoRepositoryBase + + protected override Sql GetBaseQuery(bool isCount) + { + var sql = new Sql(); + if (isCount) + { + sql.Select("COUNT(*)").From(); + } + else + { + return GetBaseQuery("*"); + } + return sql; + } + + protected Sql GetBaseQuery(string columns) + { + var sql = new Sql(); + sql.Select(columns) + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserGroupId); + + return sql; + } + + protected override string GetBaseWhereClause() + { + return "umbracoUserGroup.id = @Id"; + } + + protected override IEnumerable GetDeleteClauses() + { + var list = new List + { + "DELETE FROM umbracoUserGroup2App WHERE userGroupId = @Id", + "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @Id", + "DELETE FROM umbracoUserGroup WHERE id = @Id" + }; + return list; + } + + protected override Guid NodeObjectTypeId + { + get { throw new NotImplementedException(); } + } + + protected override void PersistNewItem(IUserGroup entity) + { + var userGroupFactory = new UserGroupFactory(); + var userGroupDto = userGroupFactory.BuildDto(entity); + + var id = Convert.ToInt32(Database.Insert(userGroupDto)); + entity.Id = id; + + PersistAllowedSections(entity); + } + + protected override void PersistUpdatedItem(IUserGroup entity) + { + var userGroupFactory = new UserGroupFactory(); + var userGroupDto = userGroupFactory.BuildDto(entity); + + Database.Update(userGroupDto); + + PersistAllowedSections(entity); + } + + private void PersistAllowedSections(IUserGroup entity) + { + var userGroup = (UserGroup)entity; + + // First delete all + Database.Delete("WHERE UserGroupId = @UserGroupId", + new { UserGroupId = userGroup.Id }); + + // Then re-add any associated with the group + foreach (var app in userGroup.AllowedSections) + { + var dto = new UserGroup2AppDto + { + UserGroupId = userGroup.Id, + AppAlias = app + }; + Database.Insert(dto); + } + } + + #endregion + + private IEnumerable ConvertFromDtos(IEnumerable dtos) + { + return dtos.Select(dto => + { + var userGroupFactory = new UserGroupFactory(); + return userGroupFactory.BuildEntity(dto); + }); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 4652ce47be..72d20ff660 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -48,10 +48,23 @@ namespace Umbraco.Core.Persistence.Repositories var userType = _userTypeRepository.Get(dto.Type); var userFactory = new UserFactory(userType); var user = userFactory.BuildEntity(dto); - + AssociateGroupsWithUser(user); return user; } + private void AssociateGroupsWithUser(IUser user) + { + if (user != null) + { + foreach (var group in GetGroupsForUser(user.Id)) + { + user.AddGroup(group); + } + + user.SetGroupsLoaded(); + } + } + protected override IEnumerable PerformGetAll(params int[] ids) { var sql = GetBaseQuery(false); @@ -62,7 +75,7 @@ namespace Umbraco.Core.Persistence.Repositories } return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -74,8 +87,16 @@ namespace Umbraco.Core.Persistence.Repositories var dtos = Database.Fetch(new UserSectionRelator().Map, sql) .DistinctBy(x => x.Id); - return ConvertFromDtos(dtos) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + var users = ConvertFromDtos(dtos) + .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + + // If a single user found (most likely from a look-up by an alternate key like email or username) then populate the groups + if (users.Length == 1) + { + AssociateGroupsWithUser(users[0]); + } + + return users; } #endregion @@ -100,9 +121,9 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = new Sql(); sql.Select(columns) - .From() - .LeftJoin() - .On(left => left.Id, right => right.UserId); + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserId); return sql; } @@ -225,9 +246,9 @@ namespace Umbraco.Core.Persistence.Repositories //now we need to delete any applications that have been removed foreach (var section in user.RemovedSections) { - //we need to manually delete thsi record because it has a composite key - Database.Delete("WHERE app=@Section AND " + SqlSyntax.GetQuotedColumnName("user") + "=@UserId", - new { Section = section, UserId = (int)user.Id }); + //we need to manually delete this record because it has a composite key + Database.Delete("WHERE app = @Section AND " + SqlSyntax.GetQuotedColumnName("user") + "= @UserId", + new { Section = section, UserId = user.Id }); } //for any that exist on the object, we need to determine if we need to update or insert @@ -248,7 +269,21 @@ namespace Umbraco.Core.Persistence.Repositories } } - + //update the groups + //first delete all + Database.Delete("WHERE UserId = @UserId", + new { UserId = user.Id }); + + //then re-add any associated with the user + foreach (var group in user.Groups) + { + var dto = new User2UserGroupDto + { + UserGroupId = group.Id, + UserId = user.Id + }; + Database.Insert(dto); + } } entity.ResetDirtyProperties(); @@ -304,6 +339,68 @@ namespace Umbraco.Core.Persistence.Repositories return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)); } + /// + /// Gets all groups for a given user + /// + /// Id of user + /// An enumerable list of + public IEnumerable GetGroupsForUser(int userId) + { + var sql = new Sql(); + sql.Select("*") + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserGroupId); + + var innerSql = new Sql(); + innerSql.Select("umbracoUserGroup.id") + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserGroupId) + .Where("umbracoUser2UserGroup.userId = " + userId); + + sql.Where(string.Format("umbracoUserGroup.id IN ({0})", innerSql.SQL)); + var dtos = Database.Fetch(new UserGroupSectionRelator().Map, sql); + return ConvertFromDtos(dtos); + } + + /// + /// Gets a list of objects associated with a given group + /// + /// Id of group + public IEnumerable GetAllInGroup(int groupId) + { + return GetAllInOrNotInGroup(groupId, true); + } + + /// + /// Gets a list of objects not associated with a given group + /// + /// Id of group + public IEnumerable GetAllNotInGroup(int groupId) + { + return GetAllInOrNotInGroup(groupId, false); + } + + private IEnumerable GetAllInOrNotInGroup(int groupId, bool include) + { + var sql = new Sql(); + sql.Select("*") + .From(); + + var innerSql = new Sql(); + innerSql.Select("umbracoUser.id") + .From() + .LeftJoin() + .On(left => left.Id, right => right.UserId) + .Where("umbracoUser2UserGroup.userGroupId = " + groupId); + + sql.Where(string.Format("umbracoUser.id {0} ({1})", + include ? "IN" : "NOT IN", + innerSql.SQL)); + return ConvertFromDtos(Database.Fetch(sql)); + } + /// /// Gets paged user results /// @@ -418,6 +515,15 @@ namespace Umbraco.Core.Persistence.Repositories }); } + private IEnumerable ConvertFromDtos(IEnumerable dtos) + { + return dtos.Select(dto => + { + var userGroupFactory = new UserGroupFactory(); + return userGroupFactory.BuildEntity(dto); + }); + } + /// /// Dispose disposable properties /// diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 85eb00ea87..b422dda40d 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -265,6 +265,14 @@ namespace Umbraco.Core.Persistence _logger, _sqlSyntax); } + public virtual IUserGroupRepository CreateUserGroupRepository(IDatabaseUnitOfWork uow) + { + return new UserGroupRepository( + uow, + _cacheHelper, + _logger, _sqlSyntax); + } + public virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow) { return new UserRepository( diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 7353f44cb8..2a3ebf9828 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -71,6 +71,30 @@ namespace Umbraco.Core.Services /// An enumerable list of IEnumerable GetPermissions(IUser user, params int[] nodeIds); + /// + /// Get permissions set for a user group and optional node ids + /// + /// Group to retrieve permissions for + /// Specifiying nothing will return all group permissions for all nodes + /// An enumerable list of + IEnumerable GetPermissions(IUserGroup group, params int[] nodeIds); + + /// + /// Gets the permissions for the provided user and path + /// + /// User to check permissions for + /// Path to check permissions for + /// String indicating permissions for provided user and path + string GetPermissionsForPath(IUser user, string path); + + /// + /// Gets the permissions for the provided group and path + /// + /// User to check permissions for + /// Path to check permissions for + /// String indicating permissions for provided user and path + string GetPermissionsForPath(IUserGroup group, string path); + /// /// Replaces the same permission set for a single user to any number of entities /// @@ -91,10 +115,24 @@ namespace Umbraco.Core.Services /// Specify the nodes to replace permissions for void AssignUserPermission(int userId, char permission, params int[] entityIds); + /// + /// Gets a list of objects associated with a given group + /// + /// Id of group + /// + IEnumerable GetAllInGroup(int groupId); + + /// + /// Gets a list of objects not associated with a given group + /// + /// Id of group + /// + IEnumerable GetAllNotInGroup(int groupId); + #region User types /// - /// Gets all UserTypes or thosed specified as parameters + /// Gets all UserTypes or those specified as parameters /// /// Optional Ids of UserTypes to retrieve /// An enumerable list of @@ -136,5 +174,60 @@ namespace Umbraco.Core.Services void DeleteUserType(IUserType userType); #endregion + + #region User groups + + /// + /// Gets all UserGroups or those specified as parameters + /// + /// Optional Ids of UserGroups to retrieve + /// An enumerable list of + IEnumerable GetAllUserGroups(params int[] ids); + + /// + /// Gets all UserGroups for a given user + /// + /// Id of user + /// An enumerable list of + IEnumerable GetGroupsForUser(int userId); + + /// + /// Gets a UserGroup by its Alias + /// + /// Alias of the UserGroup to retrieve + /// + IUserGroup GetUserGroupByAlias(string alias); + + /// + /// Gets a UserGroup by its Id + /// + /// Id of the UserGroup to retrieve + /// + IUserGroup GetUserGroupById(int id); + + /// + /// Gets a UserGroup by its Name + /// + /// Name of the UserGroup to retrieve + /// + IUserGroup GetUserGroupByName(string name); + + /// + /// Saves a UserGroup + /// + /// UserGroup to save + /// Flag for whether to update the list of users in the group + /// List of user Ids + /// Optional parameter to raise events. + /// Default is True otherwise set to False to not raise events + void SaveUserGroup(IUserGroup userGroup, bool updateUsers = false, int[] userIds = null, bool raiseEvents = true); + + /// + /// Deletes a UserGroup + /// + /// UserGroup to delete + void DeleteUserGroup(IUserGroup userGroup); + + #endregion } } diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 8e984d1e5d..fcd8c45536 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -516,6 +516,34 @@ namespace Umbraco.Core.Services } } + /// + /// Gets a list of objects associated with a given group + /// + /// Id of group + /// + public IEnumerable GetAllInGroup(int groupId) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserRepository(uow)) + { + return repository.GetAllInGroup(groupId); + } + } + + /// + /// Gets a list of objects not associated with a given group + /// + /// Id of group + /// + public IEnumerable GetAllNotInGroup(int groupId) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserRepository(uow)) + { + return repository.GetAllNotInGroup(groupId); + } + } + #endregion #region Implementation of IUserService @@ -587,7 +615,7 @@ namespace Umbraco.Core.Services } /// - /// Gets all UserTypes or thosed specified as parameters + /// Gets all UserTypes or those specified as parameters /// /// Optional Ids of UserTypes to retrieve /// An enumerable list of @@ -687,6 +715,129 @@ namespace Umbraco.Core.Services DeletedUserType.RaiseEvent(new DeleteEventArgs(userType, false), this); } + /// + /// Gets all UserGroups or those specified as parameters + /// + /// Optional Ids of UserGroups to retrieve + /// An enumerable list of + public IEnumerable GetAllUserGroups(params int[] ids) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) + { + return repository.GetAll(ids).OrderBy(x => x.Name); + } + } + + /// + /// Gets all UserGroups for a given user + /// + /// Id of user + /// An enumerable list of + public IEnumerable GetGroupsForUser(int userId) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserRepository(uow)) + { + return repository.GetGroupsForUser(userId); + } + } + + /// + /// Gets a UserGroup by its Alias + /// + /// Alias of the UserGroup to retrieve + /// + public IUserGroup GetUserGroupByAlias(string alias) + { + using (var repository = RepositoryFactory.CreateUserGroupRepository(UowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Alias == alias); + var contents = repository.GetByQuery(query); + return contents.SingleOrDefault(); + } + } + + /// + /// Gets a UserGroup by its Id + /// + /// Id of the UserGroup to retrieve + /// + public IUserGroup GetUserGroupById(int id) + { + using (var repository = RepositoryFactory.CreateUserGroupRepository(UowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + + /// + /// Gets a UserGroup by its Name + /// + /// Name of the UserGroup to retrieve + /// + public IUserGroup GetUserGroupByName(string name) + { + using (var repository = RepositoryFactory.CreateUserGroupRepository(UowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Name == name); + var contents = repository.GetByQuery(query); + return contents.SingleOrDefault(); + } + } + + /// + /// Saves a UserGroup + /// + /// UserGroup to save + /// Flag for whether to update the list of users in the group + /// List of user Ids + /// Optional parameter to raise events. + /// Default is True otherwise set to False to not raise events + public void SaveUserGroup(IUserGroup userGroup, bool updateUsers = false, int[] userIds = null, bool raiseEvents = true) + { + if (raiseEvents) + { + if (SavingUserGroup.IsRaisedEventCancelled(new SaveEventArgs(userGroup), this)) + return; + } + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) + { + repository.AddOrUpdate(userGroup); + uow.Commit(); + + if (updateUsers) + { + repository.RemoveAllUsersFromGroup(userGroup.Id); + repository.AddUsersToGroup(userGroup.Id, userIds); + } + } + + if (raiseEvents) + SavedUserGroup.RaiseEvent(new SaveEventArgs(userGroup, false), this); + } + + /// + /// Deletes a UserGroup + /// + /// UserGroup to delete + public void DeleteUserGroup(IUserGroup userGroup) + { + if (DeletingUserGroup.IsRaisedEventCancelled(new DeleteEventArgs(userGroup), this)) + return; + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) + { + repository.Delete(userGroup); + uow.Commit(); + } + + DeletedUserGroup.RaiseEvent(new DeleteEventArgs(userGroup, false), this); + } + /// /// Removes a specific section from all users /// @@ -768,8 +919,205 @@ namespace Umbraco.Core.Services } } + /// + /// Get permissions set for a group and optional node ids + /// + /// Group to retrieve permissions for + /// Specifiying nothing will return all permissions for all nodes + /// An enumerable list of + public IEnumerable GetPermissions(IUserGroup group, params int[] nodeIds) + { + throw new NotImplementedException(); + } + + /// + /// Get permissions set for a user and optional node ids + /// + /// User repository + /// User to retrieve permissions for + /// Specifiying nothing will return all user permissions for all nodes + /// An enumerable list of + private IEnumerable GetPermissions(IUserRepository repository, IUser user, params int[] nodeIds) + { + var explicitPermissions = repository.GetUserPermissionsForEntities(user.Id, nodeIds); + var result = new List(explicitPermissions); + + // Save list of nodes that user has explicit permissions for - these take priority so we don't want + // to amend with any settings from groups (we'll take the special "null" action as not being defined + // as this is set if someone sets permissions on a user and then removes them) + var explicitlyDefinedEntityIds = result + .Where(IsNotNullActionPermission) + .Select(x => x.EntityId) + .ToArray(); + + // If no permissions are assigned to a particular node then, we will fill in those permissions with the user's defaults + var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); + foreach (var id in missingIds) + { + result.Add( + new EntityPermission( + user.Id, + id, + user.DefaultPermissions.ToArray())); + } + + // Add or amend the existing permissions based on these + foreach (var group in user.Groups) + { + var groupPermissions = GetPermissions(group, nodeIds).ToList(); + foreach (var groupPermission in groupPermissions) + { + // Check if we already have this from explicitly set user permissions, if so, ignore it + if (explicitlyDefinedEntityIds.Contains(groupPermission.EntityId) == false) + { + // Add group permission, ensuring we keep a unique value for the entity Id in the list + AddOrAmendPermission(result, groupPermission); + } + } + } + + return result; + } + + private void AddOrAmendPermission(IList permissions, EntityPermission groupPermission) + { + var existingPermission = permissions + .SingleOrDefault(x => x.EntityId == groupPermission.EntityId); + if (existingPermission != null) + { + existingPermission.AddAdditionalPermissions(groupPermission.AssignedPermissions); + } + else + { + permissions.Add(groupPermission); + } + } + + /// + /// Gets the permissions for the provided user and path + /// + /// User to check permissions for + /// Path to check permissions for + /// String indicating permissions for provided user and path + public string GetPermissionsForPath(IUser user, string path) + { + var nodeId = GetNodeIdFromPath(path); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserRepository(uow)) + { + // Check for permissions directly assigned to the node for the user (if we have any, they take priority + // over anything defined on groups; we'll take the special "null" action as not being defined + // as this is set if someone sets permissions on a user and then removes them) + var permissions = GetPermissions(repository, user, nodeId) + .Where(IsNotNullActionPermission) + .ToList(); + var gotDirectlyAssignedUserPermissions = permissions.Any(); + + // If none found, and checking groups, get permissions from the groups + if (gotDirectlyAssignedUserPermissions == false) + { + foreach (var group in user.Groups) + { + var groupPermissions = GetPermissions(group, nodeId).ToList(); + permissions.AddRange(groupPermissions); + } + } + + // If none found directly on the user, get those defined on the user type + if (gotDirectlyAssignedUserPermissions == false) + { + var typePermissions = GetPermissions(repository, user, nodeId).ToList(); + permissions.AddRange(typePermissions); + } + + // Extract the net permissions from the path from the set of permissions found + string assignedPermissions; + if (TryGetAssignedPermissionsForNode(permissions, nodeId, out assignedPermissions)) + { + return assignedPermissions; + } + + // Exception to everything. If default cruds is empty and we're on root node; allow browse of root node + if (path == "-1") + { + return "F"; + } + + return string.Empty; + } + } + + /// + /// Gets the permissions for the provided group and path + /// + /// User to check permissions for + /// Path to check permissions for + /// String indicating permissions for provided user and path + public string GetPermissionsForPath(IUserGroup group, string path) + { + var nodeId = GetNodeIdFromPath(path); + + var permissions = GetPermissions(group).ToList(); + + string assignedPermissions; + TryGetAssignedPermissionsForNode(permissions, nodeId, out assignedPermissions); + return assignedPermissions; + } + + private static int GetNodeIdFromPath(string path) + { + return path.Contains(",") + ? int.Parse(path.Substring(path.LastIndexOf(",", StringComparison.Ordinal) + 1)) + : int.Parse(path); + } + + private static bool IsNotNullActionPermission(EntityPermission x) + { + const string NullActionChar = "-"; + return string.Join(string.Empty, x.AssignedPermissions) != NullActionChar; + } + + /// + /// Checks in a set of permissions associated with a user for those related to a given nodeId + /// + /// The set of permissions + /// The node Id + /// The permissions to return + /// True if permissions for the given path are found + public static bool TryGetAssignedPermissionsForNode(IList permissions, + int nodeId, + out string assignedPermissions) + { + if (permissions.Any(x => x.EntityId == nodeId)) + { + var found = permissions.First(x => x.EntityId == nodeId); + var assignedPermissionsArray = found.AssignedPermissions.ToList(); + + // Working with permissions assigned directly to a user AND to their groups, so maybe several per node + // and we need to get the most permissive set + foreach (var permission in permissions.Where(x => x.EntityId == nodeId).Skip(1)) + { + AddAdditionalPermissions(assignedPermissionsArray, permission.AssignedPermissions); + } + + assignedPermissions = string.Join("", assignedPermissionsArray); + return true; + } + + assignedPermissions = string.Empty; + return false; + } + + private static void AddAdditionalPermissions(List assignedPermissions, string[] additionalPermissions) + { + var permissionsToAdd = additionalPermissions + .Where(x => assignedPermissions.Contains(x) == false); + assignedPermissions.AddRange(permissionsToAdd); + } + #endregion - + /// /// Occurs before Save /// @@ -809,5 +1157,25 @@ namespace Umbraco.Core.Services /// Occurs after Delete /// public static event TypedEventHandler> DeletedUserType; + + /// + /// Occurs before Save + /// + public static event TypedEventHandler> SavingUserGroup; + + /// + /// Occurs after Save + /// + public static event TypedEventHandler> SavedUserGroup; + + /// + /// Occurs before Delete + /// + public static event TypedEventHandler> DeletingUserGroup; + + /// + /// Occurs after Delete + /// + public static event TypedEventHandler> DeletedUserGroup; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index bf70f6afd6..555c76eb4c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -385,6 +385,9 @@ + + + @@ -412,6 +415,10 @@ + + + + @@ -421,11 +428,13 @@ + + @@ -438,6 +447,7 @@ + @@ -474,6 +484,7 @@ + @@ -489,11 +500,13 @@ + + diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index bed5582609..201fedfcad 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Persistence private DatabaseSchemaHelper _schemaHelper; public abstract Database Database { get; } - + protected abstract ISqlSyntaxProvider SqlSyntaxProvider { get; } protected DatabaseSchemaHelper DatabaseSchemaHelper @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Persistence public virtual void Initialize() { _logger = new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test-log4net.config"))); - + TestHelper.InitializeContentDirectories(); string path = TestHelper.CurrentAssemblyDirectory; @@ -64,9 +64,9 @@ namespace Umbraco.Tests.Persistence new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(_logger), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, _logger), cacheHelper, _logger, evtMsgs), cacheHelper, new ProfilingLogger(_logger, Mock.Of())) - { - IsReady = true - }; + { + IsReady = true + }; Resolution.Freeze(); } @@ -83,7 +83,7 @@ namespace Umbraco.Tests.Persistence //RepositoryResolver.Reset(); } - + [Test] public void Can_Create_umbracoNode_Table() @@ -135,7 +135,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoAppTree_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -147,7 +147,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsContentType2ContentType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -160,7 +160,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsContentTypeAllowedContentType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -174,7 +174,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsContentType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -187,7 +187,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsContentVersion_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -202,7 +202,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsContentXml_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -217,7 +217,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsDataType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -230,7 +230,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsDataTypePreValues_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -244,7 +244,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsDictionary_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsLanguageText_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -270,7 +270,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsTemplate_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -283,7 +283,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsDocument_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -299,7 +299,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsDocumentType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -314,7 +314,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoDomains_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -327,7 +327,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoLanguage_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -339,7 +339,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoLog_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -351,7 +351,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsMacro_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -359,11 +359,11 @@ namespace Umbraco.Tests.Persistence //transaction.Complete(); } } - + [Test] public void Can_Create_cmsMember_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -378,7 +378,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsMember2MemberGroup_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -394,7 +394,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsMemberType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -408,7 +408,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsPreviewXml_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -424,7 +424,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsPropertyData_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -441,7 +441,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsPropertyType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -457,7 +457,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsPropertyTypeGroup_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -471,7 +471,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoRelation_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -485,7 +485,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoRelationType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -497,7 +497,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsStylesheet_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -510,7 +510,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsStylesheetProperty_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -523,7 +523,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsTags_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -535,7 +535,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsTagRelationship_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -555,7 +555,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsTask_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -571,7 +571,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_cmsTaskType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -608,7 +608,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoUser_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -621,7 +621,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoUserType_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -631,14 +631,11 @@ namespace Umbraco.Tests.Persistence } [Test] - public void Can_Create_umbracoUser2app_Table() + public void Can_Create_umbracoUserGroup_Table() { - using (Transaction transaction = Database.GetTransaction()) { - DatabaseSchemaHelper.CreateTable(); - DatabaseSchemaHelper.CreateTable(); - DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); //transaction.Complete(); } @@ -647,7 +644,7 @@ namespace Umbraco.Tests.Persistence [Test] public void Can_Create_umbracoUser2NodeNotify_Table() { - + using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); @@ -660,15 +657,40 @@ namespace Umbraco.Tests.Persistence } [Test] - public void Can_Create_umbracoUser2NodePermission_Table() + public void Can_Create_umbracoUser2UserGroup_Table() + { + using (Transaction transaction = Database.GetTransaction()) + { + DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); + + //transaction.Complete(); + } + } + + public void Can_Create_umbracoGroupUser2app_Table() { - using (Transaction transaction = Database.GetTransaction()) { DatabaseSchemaHelper.CreateTable(); - DatabaseSchemaHelper.CreateTable(); - DatabaseSchemaHelper.CreateTable(); - DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); + + //transaction.Complete(); + } + } + + [Test] + public void Can_Create_umbracoUserGroup2NodePermission_Table() + { + + using (Transaction transaction = Database.GetTransaction()) + { + DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); + DatabaseSchemaHelper.CreateTable(); //transaction.Complete(); } diff --git a/src/Umbraco.Tests/Services/SectionServiceTests.cs b/src/Umbraco.Tests/Services/SectionServiceTests.cs new file mode 100644 index 0000000000..e574d749f4 --- /dev/null +++ b/src/Umbraco.Tests/Services/SectionServiceTests.cs @@ -0,0 +1,102 @@ +using NUnit.Framework; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using System; +using System.Linq; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Tests.Services +{ + /// + /// Tests covering the SectionService + /// + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + [TestFixture, RequiresSTA] + public class SectionServiceTests : BaseServiceTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + public override void CreateTestData() + { + base.CreateTestData(); + + ServiceContext.SectionService.MakeNew("Content", "content", "icon-content"); + ServiceContext.SectionService.MakeNew("Media", "media", "icon-media"); + ServiceContext.SectionService.MakeNew("Settings", "settings", "icon-settings"); + ServiceContext.SectionService.MakeNew("Developer", "developer", "icon-developer"); + } + + [Test] + public void SectionService_Can_Get_Allowed_Sections_For_User() + { + // Arrange + var user = CreateUser(); + + // Act + var result = ServiceContext.SectionService.GetAllowedSections(user.Id).ToList(); + + // Assert + Assert.AreEqual(2, result.Count); + } + + [Test] + public void SectionService_Can_Get_Allowed_Sections_For_User_With_Groups() + { + // Arrange + var user = CreateUser(true); + + // Act + var result = ServiceContext.SectionService.GetAllowedSections(user.Id).ToList(); + + // Assert + Assert.AreEqual(4, result.Count); + } + + private IUser CreateUser(bool withGroups = false) + { + var userType = new UserType + { + Alias = "TypeA", + Name = "Type A", + }; + ServiceContext.UserService.SaveUserType(userType, false); + + var user = new User(userType) + { + Name = "Test user", + Username = "testUser", + Email = "testuser@test.com", + }; + user.AddAllowedSection("content"); + user.AddAllowedSection("media"); + ServiceContext.UserService.Save(user, false); + + if (withGroups) + { + var userGroupA = new UserGroup + { + Alias = "GroupA", + Name = "Group A" + }; + userGroupA.AddAllowedSection("media"); + userGroupA.AddAllowedSection("settings"); + ServiceContext.UserService.SaveUserGroup(userGroupA, true, new[] { user.Id }, false); + + var userGroupB = new UserGroup + { + Alias = "GroupB", + Name = "Group B" + }; + userGroupB.AddAllowedSection("settings"); + userGroupB.AddAllowedSection("developer"); + ServiceContext.UserService.SaveUserGroup(userGroupB, true, new[] { user.Id }, false); + } + + return ServiceContext.UserService.GetUserById(user.Id); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 8659a73cd1..89053a2d5a 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -179,6 +179,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index d046ac7104..d68edb0ea4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -815,3 +815,8 @@ legend + .control-group { } } + +/* User/group selector */ +.group-selector .group-selector-list { float: left; } +.group-selector .group-selector-list div { height: 24px; } +.group-selector .group-selector-buttons { float: left; margin: 24px 16px; } diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 234b3a8f09..d679384a38 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -603,6 +603,7 @@ + diff --git a/src/Umbraco.Web.UI/config/trees.Release.config b/src/Umbraco.Web.UI/config/trees.Release.config index 9b2b8e8b6f..f53e3b7862 100644 --- a/src/Umbraco.Web.UI/config/trees.Release.config +++ b/src/Umbraco.Web.UI/config/trees.Release.config @@ -30,8 +30,9 @@ - - + + + diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 81c0c03bc8..30563647f8 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -26,7 +26,8 @@ - + + @@ -41,5 +42,5 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml index 9fca6d082a..7af0512ed7 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml @@ -1,5 +1,5 @@ - +
Template
/create/simple.ascx @@ -57,7 +57,7 @@ -
+
membergroup
/create/simple.ascx @@ -78,7 +78,7 @@ -
+
member
/create/member.ascx @@ -92,14 +92,14 @@ -
+
membergroup
/create/simple.ascx -
+
Stylesheet editor egenskab
/create/simple.ascx @@ -235,9 +235,17 @@
+ +
User Groups
+ /create/simple.ascx + + + + +
Macro
- /Create/PartialView.ascx + /Create/PartialView.ascx diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml index f6859c6423..ba3df007e7 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml @@ -221,6 +221,14 @@
+ +
User Groups
+ /create/simple.ascx + + + + +
Macro
/Create/PartialView.ascx diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index f710e54215..c4ffc9aefc 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -958,8 +958,8 @@ To manage your website, simply open the Umbraco back office and start adding con
Do not close this window during sorting]]>
- Validation - Validation errors must be fixed before the item can be saved + Validation + Validation errors must be fixed before the item can be saved Failed Insufficient user permissions, could not complete the operation Cancelled @@ -994,6 +994,7 @@ To manage your website, simply open the Umbraco back office and start adding con Error saving user (check log) User Saved User type saved + User group saved File not saved file could not be saved. Please check file permissions File saved @@ -1303,8 +1304,11 @@ To manage your website, simply open the Umbraco back office and start adding con Start Node in Content Name User permissions + User group permissions User type User types + User group + User groups Writer Translator Change diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 80c6faf07b..98075f00ac 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -993,6 +993,7 @@ To manage your website, simply open the Umbraco back office and start adding con Error saving user (check log) User Saved User type saved + User group saved File not saved file could not be saved. Please check file permissions File saved @@ -1308,8 +1309,11 @@ To manage your website, simply open the Umbraco back office and start adding con Start Node in Content Name User permissions + User group permissions User type User types + User group + User groups Writer Translator Change diff --git a/src/Umbraco.Web.UI/umbraco/users/EditUserGroup.aspx b/src/Umbraco.Web.UI/umbraco/users/EditUserGroup.aspx new file mode 100644 index 0000000000..6626f33eb1 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/users/EditUserGroup.aspx @@ -0,0 +1,52 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EditUserGroup.aspx.cs" MasterPageFile="../masterpages/umbracoPage.Master" + Inherits="umbraco.cms.presentation.user.EditUserGroup" %> + +<%@ Register TagPrefix="cc2" Namespace="umbraco.uicontrols" Assembly="controls" %> + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Users not in group:
+ +
+
+ + +
+
+
Users in group:
+ +
+
+
+
+
+
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index f0c0861059..a6c24913ad 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -66,8 +66,8 @@ namespace Umbraco.Web.Security { get { - //only load it once per instance! - if (_currentUser == null) + //only load it once per instance! (but make sure groups are loaded) + if (_currentUser == null || _currentUser.GroupsLoaded == false) { var id = GetUserId(); if (id == -1) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7fb022e848..aec0cdab68 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -417,6 +417,17 @@ + + + EditUserGroup.aspx + ASPXCodeBehind + + + EditUserGroup.aspx + + + ASPXCodeBehind + @@ -2029,6 +2040,7 @@ ASPXCodeBehind
+ diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserGroups.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserGroups.cs new file mode 100644 index 0000000000..98e87ce34b --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserGroups.cs @@ -0,0 +1,50 @@ +using System; +using umbraco.BusinessLogic; +using System.Collections.Generic; +using umbraco.businesslogic; +using Umbraco.Core; + +namespace umbraco.cms.presentation.Trees +{ + [Tree(Constants.Applications.Users, "userGroups", "User Groups", sortOrder: 2)] + public class UserGroups : BaseTree + { + + public UserGroups(string application) : base(application) { } + + public override void RenderJS(ref System.Text.StringBuilder Javascript) + { + Javascript.Append( + @" +function openUserGroups(id) { + UmbClientMgr.contentFrame('users/EditUserGroup.aspx?id=' + id); +} +"); + } + + public override void Render(ref XmlTree tree) + { + List userGroups = UserGroup.GetAllUserGroups(); + foreach (UserGroup userGroup in userGroups) + { + XmlTreeNode node = XmlTreeNode.Create(this); + node.NodeID = userGroup.Id.ToString(); + node.Action = string.Format("javascript:openUserGroups({0})", userGroup.Id.ToString()); + node.Icon = "icon-users"; + node.Text = userGroup.Name; + + OnBeforeNodeRender(ref tree, ref node, EventArgs.Empty); + if (node != null) + { + tree.Add(node); + OnAfterNodeRender(ref tree, ref node, EventArgs.Empty); + } + } + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + rootNode.Text = ui.Text("user", "userGroups"); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs index 8ac4771672..54dd21738b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs @@ -1,12 +1,4 @@ using System; -using System.Data; -using System.Configuration; -using System.Web; -using System.Web.Security; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.WebControls.WebParts; -using System.Web.UI.HtmlControls; using umbraco.BusinessLogic; using System.Collections.Generic; using umbraco.businesslogic; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs index d9605506b4..b128903fdc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -2,33 +2,25 @@ using System.Collections; using System.Configuration.Provider; using System.Globalization; -using System.IO; +using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; -using System.Xml; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; -using Umbraco.Core.Security; using Umbraco.Web; -using Umbraco.Web.Models; using Umbraco.Web.Security; using umbraco.BasePages; using umbraco.BusinessLogic; -using umbraco.businesslogic.Exceptions; -using umbraco.cms.businesslogic.media; using umbraco.cms.businesslogic.web; using umbraco.controls; using umbraco.presentation.channels.businesslogic; using umbraco.uicontrols; -using umbraco.providers; using umbraco.cms.presentation.Trees; using Umbraco.Core.IO; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Services; using PropertyType = umbraco.cms.businesslogic.propertytype.PropertyType; namespace umbraco.cms.presentation.user @@ -36,7 +28,7 @@ namespace umbraco.cms.presentation.user /// /// Summary description for EditUser. /// - public partial class EditUser : UmbracoEnsuredPage + public partial class EditUser : EditUserGroupsBase { public EditUser() { @@ -66,6 +58,13 @@ namespace umbraco.cms.presentation.user protected ContentPicker cContentPicker = new ContentPicker(); protected CustomValidator sectionValidator = new CustomValidator(); + protected UpdatePanel pnlGroups = new UpdatePanel(); + protected PlaceHolder pnlGroupControls = new PlaceHolder(); + protected ListBox lstInGroups = new ListBox(); + protected ListBox lstNotInGroups = new ListBox(); + protected Button btnAddGroup = new Button(); + protected Button btnRemoveGroup = new Button(); + protected Pane pp = new Pane(); private User u; @@ -154,7 +153,7 @@ namespace umbraco.cms.presentation.user var passwordChanger = (passwordChanger)LoadControl(SystemDirectories.Umbraco + "/controls/passwordChanger.ascx"); passwordChanger.MembershipProviderName = UmbracoSettings.DefaultBackofficeProvider; - //Add a custom validation message for the password changer + // Add a custom validation message for the password changer var passwordValidation = new CustomValidator { ID = "PasswordChangerValidator" @@ -180,22 +179,59 @@ namespace umbraco.cms.presentation.user pp.addProperty(ui.Text("user", "usertype", UmbracoUser), userType); pp.addProperty(ui.Text("user", "language", UmbracoUser), userLanguage); - //Media / content root nodes - Pane ppNodes = new Pane(); + // Media / content root nodes + var ppNodes = new Pane(); ppNodes.addProperty(ui.Text("user", "startnode", UmbracoUser), content); ppNodes.addProperty(ui.Text("user", "mediastartnode", UmbracoUser), medias); - //Generel umrbaco access - Pane ppAccess = new Pane(); + // General umbraco access + var ppAccess = new Pane(); ppAccess.addProperty(ui.Text("user", "noConsole", UmbracoUser), NoConsole); ppAccess.addProperty(ui.Text("user", "disabled", UmbracoUser), Disabled); - //access to which modules... - Pane ppModules = new Pane(); + // Access to which modules... + var ppModules = new Pane(); ppModules.addProperty(ui.Text("user", "modules", UmbracoUser), lapps); ppModules.addProperty(" ", sectionValidator); - TabPage userInfo = UserTabs.NewTabPage(u.Name); + // Groups + var ppGroups = new Pane(); + lstNotInGroups.SelectionMode = ListSelectionMode.Multiple; + btnAddGroup.Text = "Add"; + btnAddGroup.Click += btnAddToGroup_Click; + btnRemoveGroup.Text = "Remove"; + btnRemoveGroup.Click += btnRemoveFromGroup_Click; + lstInGroups.SelectionMode = ListSelectionMode.Multiple; + pnlGroups.ContentTemplateContainer.Controls.Add(pnlGroupControls); + pnlGroups.Attributes.Add("class", "group-selector"); + + var pnl1 = new Panel(); + pnl1.CssClass = "group-selector-list"; + var pnl1Header = new Panel(); + pnl1Header.Controls.Add(new Literal { Text = "Available groups" }); + pnl1.Controls.Add(pnl1Header); + pnl1.Controls.Add(lstNotInGroups); + + var pnl2 = new Panel(); + pnl2.CssClass = "group-selector-buttons"; + pnl2.Controls.Add(btnAddGroup); + pnl2.Controls.Add(btnRemoveGroup); + + var pnl3 = new Panel(); + pnl3.CssClass = "group-selector-list"; + var pnl3Header = new Panel(); + pnl3Header.Controls.Add(new Literal { Text = "Selected groups" }); + pnl3.Controls.Add(pnl3Header); + pnl3.Controls.Add(lstInGroups); + + pnlGroups.ContentTemplateContainer.Controls.Add(pnl1); + pnlGroups.ContentTemplateContainer.Controls.Add(pnl2); + pnlGroups.ContentTemplateContainer.Controls.Add(pnl3); + + ppModules.addProperty(ui.Text("user", "userGroups", UmbracoUser), pnlGroups); + BindGroups(); + + var userInfo = UserTabs.NewTabPage(u.Name); userInfo.Controls.Add(pp); @@ -203,6 +239,7 @@ namespace umbraco.cms.presentation.user userInfo.Controls.Add(ppNodes); userInfo.Controls.Add(ppModules); + userInfo.Controls.Add(ppGroups); userInfo.HasMenu = true; @@ -227,6 +264,33 @@ namespace umbraco.cms.presentation.user .SyncTree(UID.ToString(), IsPostBack); } + private void BindGroups() + { + var userService = ApplicationContext.Current.Services.UserService; + var allGroups = userService.GetAllUserGroups(); + var groupsForUser = userService.GetGroupsForUser(u.Id); + + lstInGroups.DataSource = groupsForUser; + lstInGroups.DataValueField = "Id"; + lstInGroups.DataTextField = "Name"; + lstInGroups.DataBind(); + + lstNotInGroups.DataSource = allGroups + .Where(x => groupsForUser.Select(y => y.Id).Contains(x.Id) == false); + lstNotInGroups.DataValueField = "Id"; + lstNotInGroups.DataTextField = "Name"; + lstNotInGroups.DataBind(); + } + + protected void btnAddToGroup_Click(object sender, EventArgs e) + { + MoveItems(lstNotInGroups, lstInGroups); + } + + protected void btnRemoveFromGroup_Click(object sender, EventArgs e) + { + MoveItems(lstInGroups, lstNotInGroups); + } void sectionValidator_ServerValidate(object source, ServerValidateEventArgs args) { @@ -276,7 +340,6 @@ namespace umbraco.cms.presentation.user } // Handle content and media pickers - PlaceHolder medias = new PlaceHolder(); cMediaPicker.AppAlias = Constants.Applications.Media; cMediaPicker.TreeAlias = "media"; @@ -336,7 +399,6 @@ namespace umbraco.cms.presentation.user ///
private void SetupForm() { - if (!IsPostBack) { MembershipUser user = BackOfficeProvider.GetUser(u.LoginName, false); @@ -487,6 +549,12 @@ namespace umbraco.cms.presentation.user if (li.Selected) u.AddApplication(li.Value); } + u.ClearGroups(); + foreach (ListItem li in lstInGroups.Items) + { + u.AddGroup(int.Parse(li.Value), li.Text); + } + u.Save(); // save data diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx new file mode 100644 index 0000000000..6626f33eb1 --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx @@ -0,0 +1,52 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EditUserGroup.aspx.cs" MasterPageFile="../masterpages/umbracoPage.Master" + Inherits="umbraco.cms.presentation.user.EditUserGroup" %> + +<%@ Register TagPrefix="cc2" Namespace="umbraco.uicontrols" Assembly="controls" %> + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Users not in group:
+ +
+
+ + +
+
+
Users in group:
+ +
+
+
+
+
+
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.cs new file mode 100644 index 0000000000..9b929bd931 --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Web.UI.WebControls; +using umbraco.BasePages; +using umbraco.BusinessLogic; +using umbraco.cms.presentation.Trees; +using umbraco.interfaces; +using Umbraco.Core; + +namespace umbraco.cms.presentation.user +{ + public partial class EditUserGroup : EditUserGroupsBase + { + public EditUserGroup() + { + CurrentApp = BusinessLogic.DefaultApps.users.ToString(); + } + + protected void Page_Load(object sender, EventArgs e) + { + pnlUmbraco.Text = umbraco.ui.Text("usergroup", base.getUser()); + + var save = pnlUmbraco.Menu.NewButton(); + save.Click += save_Click; + save.ID = "save"; + save.ToolTip = ui.Text("save"); + save.Text = ui.Text("save"); + + pp_alias.Text = umbraco.ui.Text("alias", base.getUser()); + pp_name.Text = umbraco.ui.Text("name", base.getUser()); + + pp_rights.Text = umbraco.ui.Text("default", base.getUser()) + " " + umbraco.ui.Text("rights", base.getUser()); + pp_sections.Text = umbraco.ui.Text("sections", base.getUser()); + + pp_users.Text = umbraco.ui.Text("users", base.getUser()); + + //ensure we have a query string + if (string.IsNullOrEmpty(Request.QueryString["id"])) + return; + //ensure it is an integer + if (!int.TryParse(Request.QueryString["id"], out m_userGroupID)) + return; + + if (!IsPostBack) + { + BindDetails(); + BindActions(); + BindSections(); + BindUsers(); + ClientTools + .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias) + .SyncTree(m_userGroupID.ToString(), false); + } + } + + void save_Click(object sender, EventArgs e) + { + var userGroup = CurrentUserGroup; + userGroup.Name = txtUserGroupName.Text; + string actions = ""; + + foreach (ListItem li in cbl_rights.Items) + { + if (li.Selected) + actions += li.Value; + } + + userGroup.DefaultPermissions = actions; + + var userIds = new List(); + foreach (ListItem li in lstUsersInGroup.Items) + { + userIds.Add(int.Parse(li.Value)); + } + + userGroup.ClearApplications(); + foreach (ListItem li in cbl_sections.Items) + { + if (li.Selected) userGroup.AddApplication(li.Value); + } + + userGroup.SaveWithUsers(userIds.ToArray()); + + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserGroupSaved", base.getUser()), ""); + } + + protected List CurrentUserGroupActions + { + get + { + if (m_userGroupActions == null) + m_userGroupActions = umbraco.BusinessLogic.Actions.Action.FromString(CurrentUserGroup.DefaultPermissions); + return m_userGroupActions; + } + } + + protected UserGroup CurrentUserGroup + { + get + { + if (m_userGroup == null) + m_userGroup = UserGroup.GetUserGroup(m_userGroupID); + return m_userGroup; + } + } + + private UserGroup m_userGroup; + private List m_userGroupActions; + private int m_userGroupID; + + private void BindDetails() + { + lblUserGroupAlias.Text = CurrentUserGroup.Alias; + txtUserGroupName.Text = CurrentUserGroup.Name; + hidUserGroupID.Value = CurrentUserGroup.Id.ToString(); + } + + private void BindActions() + { + foreach (IAction ai in global::umbraco.BusinessLogic.Actions.Action.GetPermissionAssignable()) + { + + ListItem li = new ListItem(umbraco.ui.Text(ai.Alias, base.getUser()), ai.Letter.ToString()); + + if (CurrentUserGroupActions.Contains(ai)) + li.Selected = true; + + cbl_rights.Items.Add(li); + } + } + + private void BindSections() + { + string currentUserApps = ";"; + foreach (Application a in CurrentUser.Applications) + currentUserApps += a.alias + ";"; + + Application[] gapps = CurrentUserGroup.Applications; + foreach (Application app in BusinessLogic.Application.getAll()) + { + if (CurrentUser.IsAdmin() || currentUserApps.Contains(";" + app.alias + ";")) + { + ListItem li = new ListItem(ui.Text("sections", app.alias), app.alias); + foreach (Application tmp in gapps) if (app.alias == tmp.alias) li.Selected = true; + cbl_sections.Items.Add(li); + } + } + } + + private void BindUsers() + { + var userService = ApplicationContext.Current.Services.UserService; + + lstUsersInGroup.DataSource = userService.GetAllInGroup(CurrentUserGroup.Id); + lstUsersInGroup.DataValueField = "Id"; + lstUsersInGroup.DataTextField = "Name"; + lstUsersInGroup.DataBind(); + + lstUsersNotInGroup.DataSource = userService.GetAllNotInGroup(CurrentUserGroup.Id); + lstUsersNotInGroup.DataValueField = "Id"; + lstUsersNotInGroup.DataTextField = "Name"; + lstUsersNotInGroup.DataBind(); + } + + protected void btnAddToGroup_Click(object sender, EventArgs e) + { + MoveItems(lstUsersNotInGroup, lstUsersInGroup); + } + + protected void btnRemoveFromGroup_Click(object sender, EventArgs e) + { + MoveItems(lstUsersInGroup, lstUsersNotInGroup); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.designer.cs new file mode 100644 index 0000000000..2de0f19ef0 --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroup.aspx.designer.cs @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace umbraco.cms.presentation.user { + + + public partial class EditUserGroup { + + /// + /// pnlUmbraco control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.UmbracoPanel pnlUmbraco; + + /// + /// pnl1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pnl1; + + /// + /// hidUserGroupID control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.HiddenField hidUserGroupID; + + /// + /// pp_name control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_name; + + /// + /// txtUserGroupName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtUserGroupName; + + /// + /// pp_alias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_alias; + + /// + /// lblUserGroupAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblUserGroupAlias; + + /// + /// pnl2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pnl2; + + /// + /// pp_rights control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_rights; + + /// + /// cbl_rights control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBoxList cbl_rights; + + /// + /// pnl3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pnl3; + + /// + /// pp_sections control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_sections; + + /// + /// cbl_sections control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBoxList cbl_sections; + + /// + /// pnl4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pnl4; + + /// + /// pp_users control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_users; + + /// + /// pnlUsers control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.UpdatePanel pnlUsers; + + /// + /// lstUsersNotInGroup control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.ListBox lstUsersNotInGroup; + + /// + /// btnAddToGroup control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnAddToGroup; + + /// + /// btnRemoveFromGroup control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnRemoveFromGroup; + + /// + /// lstUsersInGroup control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.ListBox lstUsersInGroup; + } +} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroupsBase.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroupsBase.cs new file mode 100644 index 0000000000..d5710423cf --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserGroupsBase.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Linq; +using System.Web.UI.WebControls; +using umbraco.BasePages; + +namespace umbraco.cms.presentation.user +{ + /// + /// Base class provided common functionality to the web forms that allow for assignment of users to groups + /// + public abstract class EditUserGroupsBase : UmbracoEnsuredPage + { + protected void MoveItems(ListBox from, ListBox to) + { + for (var i = from.Items.Count - 1; i >= 0; i--) + { + if (from.Items[i].Selected) + { + to.Items.Add(from.Items[i]); + var li = from.Items[i]; + from.Items.Remove(li); + } + } + + from.SelectedIndex = -1; + to.SelectedIndex = -1; + SortItems(to); + } + + protected void SortItems(ListBox listBox) + { + // Hat-tip: http://stackoverflow.com/a/3396527 + var list = new List(listBox.Items.Cast()); + list = list.OrderBy(li => li.Text).ToList(); + listBox.Items.Clear(); + listBox.Items.AddRange(list.ToArray()); + } + } +} \ No newline at end of file diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index 59b6c19a41..3a3b5f14cd 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -1,19 +1,13 @@ using System; using System.Collections; -using System.Web.Caching; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Models.Rdbms; - -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Repositories; -using umbraco.DataLayer; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; +using umbraco.DataLayer; +using Umbraco.Core.Persistence.Querying; namespace umbraco.BusinessLogic { @@ -596,32 +590,14 @@ namespace umbraco.BusinessLogic /// /// Gets the users permissions based on a nodes path /// - /// The path. + /// The path. /// - public string GetPermissions(string Path) + public string GetPermissions(string path) { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - var defaultPermissions = UserType.DefaultPermissions; - - var cachedPermissions = ApplicationContext.Current.Services.UserService.GetPermissions(UserEntity) - .ToArray(); - - // NH 4.7.1 changing default permission behavior to default to User Type permissions IF no specific permissions has been - // set for the current node - var nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal) + 1)) : int.Parse(Path); - if (cachedPermissions.Any(x => x.EntityId == nodeId)) - { - var found = cachedPermissions.First(x => x.EntityId == nodeId); - return string.Join("", found.AssignedPermissions); - } - - // exception to everything. If default cruds is empty and we're on root node; allow browse of root node - if (string.IsNullOrEmpty(defaultPermissions) && Path == "-1") - defaultPermissions = "F"; - - // else return default user type cruds - return defaultPermissions; + var userService = ApplicationContext.Current.Services.UserService; + return userService.GetPermissionsForPath(UserEntity, path); } /// @@ -746,6 +722,32 @@ namespace umbraco.BusinessLogic ApplicationContext.Current.Services.UserService.Save(UserEntity); } + /// + /// Clears the list of groups the user is in, ensure to call Save afterwords + /// + public void ClearGroups() + { + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + foreach (var group in UserEntity.Groups.ToArray()) + { + UserEntity.RemoveGroup(group); + } + } + + /// + /// Adds a group to the list groups for the user, ensure to call Save() afterwords + /// + public void AddGroup(int groupId, string groupName) + { + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + UserEntity.AddGroup(new Umbraco.Core.Models.Membership.UserGroup + { + Id = groupId, + Name = groupName, + }); + } + + /// /// Gets or sets a value indicating whether the user has access to the Umbraco back end. /// diff --git a/src/umbraco.businesslogic/UserGroup.cs b/src/umbraco.businesslogic/UserGroup.cs new file mode 100644 index 0000000000..c04dad96e3 --- /dev/null +++ b/src/umbraco.businesslogic/UserGroup.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Events; + +namespace umbraco.BusinessLogic +{ + /// + /// Represents a umbraco Usergroup + /// + [Obsolete("Use the UserService instead")] + public class UserGroup + { + internal Umbraco.Core.Models.Membership.IUserGroup UserGroupItem; + + /// + /// Creates a new empty instance of a UserGroup + /// + public UserGroup() + { + UserGroupItem = new Umbraco.Core.Models.Membership.UserGroup(); + } + + internal UserGroup(Umbraco.Core.Models.Membership.IUserGroup userGroup) + { + UserGroupItem = userGroup; + } + + /// + /// Creates a new instance of a UserGroup and attempts to + /// load it's values from the database cache. + /// + /// + /// If the UserGroup is not found in the existing ID list, then this object + /// will remain an empty object + /// + /// The UserGroup id to find + public UserGroup(int id) + { + this.LoadByPrimaryKey(id); + } + + /// + /// Initializes a new instance of the class. + /// + /// The user type id. + /// The name. + public UserGroup(int id, string name) + { + UserGroupItem = new Umbraco.Core.Models.Membership.UserGroup(); + UserGroupItem.Id = id; + UserGroupItem.Name = name; + } + + /// + /// Creates a new instance of UserGroup with all parameters + /// + /// + /// + /// + /// + public UserGroup(int id, string name, string defaultPermissions, string alias) + { + UserGroupItem = new Umbraco.Core.Models.Membership.UserGroup(); + UserGroupItem.Id = id; + UserGroupItem.Name = name; + UserGroupItem.Alias = alias; + UserGroupItem.Permissions = defaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// The cache storage for all user groups + /// + private static List UserGroups + { + get + { + return ApplicationContext.Current.Services.UserService.GetAllUserGroups() + .Select(x => new UserGroup(x)) + .ToList(); + } + } + + #region Public Properties + /// + /// Gets or sets the user type alias. + /// + public string Alias + { + get { return UserGroupItem.Alias; } + set { UserGroupItem.Alias = value; } + } + + /// + /// Gets the name of the user type. + /// + public string Name + { + get { return UserGroupItem.Name; } + set { UserGroupItem.Name = value; } + } + + /// + /// Gets the id the user type + /// + public int Id + { + get { return UserGroupItem.Id; } + } + + /// + /// Gets the default permissions of the user type + /// + public string DefaultPermissions + { + get { return UserGroupItem.Permissions == null ? string.Empty : string.Join("", UserGroupItem.Permissions); } + set { UserGroupItem.Permissions = value.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); } + } + + /// + /// Gets the applications which the group has access to. + /// + /// The users applications. + public Application[] Applications + { + get + { + return GetApplications().ToArray(); + } + } + + /// + /// Get the application which the group has access to as a List + /// + /// + public List GetApplications() + { + var allApps = Application.getAll(); + var apps = new List(); + + var sections = UserGroupItem.AllowedSections; + + foreach (var s in sections) + { + var app = allApps.SingleOrDefault(x => x.alias == s); + if (app != null) + apps.Add(app); + } + + return apps; + } + + /// + /// Clears the list of applications the user has access to, ensure to call Save afterwords + /// + public void ClearApplications() + { + foreach (var s in UserGroupItem.AllowedSections.ToArray()) + { + UserGroupItem.RemoveAllowedSection(s); + } + } + + /// + /// Adds a application to the list of allowed applications, ensure to call Save() afterwords + /// + /// + public void AddApplication(string appAlias) + { + UserGroupItem.AddAllowedSection(appAlias); + } + + #endregion + + /// + /// Saves this instance + /// + public void Save() + { + PerformSave(); + } + + /// + /// Saves this instance (along with the list of users in the group if provided) + /// + public void SaveWithUsers(int[] userIds) + { + PerformSave(true, userIds); + } + + private void PerformSave(bool updateUsers = false, int[] userIds = null) + { + //ensure that this object has an ID specified (it exists in the database) + if (UserGroupItem.HasIdentity == false) + throw new Exception("The current UserGroup object does not exist in the database. New UserGroups should be created with the MakeNew method"); + + ApplicationContext.Current.Services.UserService.SaveUserGroup(UserGroupItem, updateUsers, userIds); + + //raise event + OnUpdated(this, new EventArgs()); + } + + /// + /// Deletes this instance. + /// + public void Delete() + { + //ensure that this object has an ID specified (it exists in the database) + if (UserGroupItem.HasIdentity == false) + throw new Exception("The current UserGroup object does not exist in the database. New UserGroups should be created with the MakeNew method"); + + ApplicationContext.Current.Services.UserService.DeleteUserGroup(UserGroupItem); + + //raise event + OnDeleted(this, new EventArgs()); + } + + /// + /// Load the data for the current UserGroup by it's id + /// + /// + /// Returns true if the UserGroup id was found + /// and the data was loaded, false if it wasn't + public bool LoadByPrimaryKey(int id) + { + UserGroupItem = ApplicationContext.Current.Services.UserService.GetUserGroupById(id); + return UserGroupItem != null; + } + + /// + /// Creates a new user group + /// + /// + /// + /// + public static UserGroup MakeNew(string name, string defaultPermissions, string alias) + { + //ensure that the current alias does not exist + //get the id for the new user type + var existing = UserGroups.Find(ut => (ut.Alias == alias)); + + if (existing != null) + throw new Exception("The UserGroup alias specified already exists"); + + var userType = new Umbraco.Core.Models.Membership.UserGroup + { + Alias = alias, + Name = name, + Permissions = defaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)) + }; + ApplicationContext.Current.Services.UserService.SaveUserGroup(userType); + + var legacy = new UserGroup(userType); + + //raise event + OnNew(legacy, new EventArgs()); + + return legacy; + } + + /// + /// Gets the user type with the specied ID + /// + /// The id. + /// + public static UserGroup GetUserGroup(int id) + { + return UserGroups.Find(ut => (ut.Id == id)); + } + + /// + /// Returns all user groups + /// + /// + public static List GetAllUserGroups() + { + return UserGroups; + } + + internal static event TypedEventHandler New; + private static void OnNew(UserGroup userType, EventArgs args) + { + if (New != null) + { + New(userType, args); + } + } + + internal static event TypedEventHandler Deleted; + private static void OnDeleted(UserGroup userType, EventArgs args) + { + if (Deleted != null) + { + Deleted(userType, args); + } + } + + internal static event TypedEventHandler Updated; + private static void OnUpdated(UserGroup userType, EventArgs args) + { + if (Updated != null) + { + Updated(userType, args); + } + } + } +} \ No newline at end of file diff --git a/src/umbraco.businesslogic/umbraco.businesslogic.csproj b/src/umbraco.businesslogic/umbraco.businesslogic.csproj index 1483523caf..0d6aae1b71 100644 --- a/src/umbraco.businesslogic/umbraco.businesslogic.csproj +++ b/src/umbraco.businesslogic/umbraco.businesslogic.csproj @@ -241,6 +241,7 @@ Code + Code From 2d9aeb3aa1595c715dcd5cc8ed8282259e63bda3 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Thu, 27 Oct 2016 18:11:46 +0200 Subject: [PATCH 02/15] Removed relationship between users and applications and got back to a compiling state. Set up editing screens for group/node permissions. --- src/Umbraco.Core/Cache/CacheKeys.cs | 2 + .../Models/Identity/BackOfficeIdentityUser.cs | 1 + .../Models/Identity/IdentityModelMappings.cs | 2 + .../Models/Membership/EntityPermission.cs | 6 +- .../Models/Membership/EntityPermissionSet.cs | 25 +- src/Umbraco.Core/Models/Membership/IUser.cs | 4 - src/Umbraco.Core/Models/Membership/User.cs | 92 +--- .../Membership/UserGroupEntityPermission.cs | 16 + src/Umbraco.Core/Models/Rdbms/User2AppDto.cs | 20 - .../Models/Rdbms/User2NodePermissionDto.cs | 23 - src/Umbraco.Core/Models/Rdbms/UserDto.cs | 3 - .../Persistence/Factories/UserFactory.cs | 23 - .../Factories/UserSectionFactory.cs | 35 -- .../Migrations/Initial/BaseDataCreation.cs | 16 - .../Relators/UserSectionRelator.cs | 50 --- .../Repositories/ContentRepository.cs | 8 +- .../DataTypeDefinitionRepository.cs | 2 +- .../Interfaces/IContentRepository.cs | 2 +- .../Interfaces/IUserGroupRepository.cs | 2 - .../Interfaces/IUserRepository.cs | 31 -- .../Repositories/PermissionRepository.cs | 239 +++++----- .../Repositories/UserGroupRepository.cs | 16 +- .../Repositories/UserRepository.cs | 136 +----- .../Security/BackOfficeUserStore.cs | 40 +- src/Umbraco.Core/Security/UserData.cs | 3 + src/Umbraco.Core/Services/ContentService.cs | 2 +- .../Services/ContentServiceExtensions.cs | 2 +- src/Umbraco.Core/Services/IContentService.cs | 2 +- src/Umbraco.Core/Services/ISectionService.cs | 2 +- src/Umbraco.Core/Services/IUserService.cs | 30 +- src/Umbraco.Core/Services/UserService.cs | 92 ++-- .../Services/UserServiceExtensions.cs | 13 +- src/Umbraco.Core/Umbraco.Core.csproj | 5 +- src/Umbraco.Tests/Models/UserTests.cs | 13 - .../Repositories/UserGroupRepositoryTest.cs | 425 ++++++++++++++++++ .../Repositories/UserRepositoryTest.cs | 139 +----- .../Services/SectionServiceTests.cs | 2 - .../Services/UserServiceTests.cs | 138 +++--- .../TestHelpers/Entities/MockedUser.cs | 17 +- .../TestHelpers/Entities/MockedUserGroup.cs | 17 + .../TreesAndSections/SectionTests.cs | 58 --- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + .../ContentControllerUnitTests.cs | 12 +- ...terAllowedOutgoingContentAttributeTests.cs | 8 +- src/Umbraco.Web.UI/config/trees.config | 2 +- .../umbraco/users/PermissionEditor.aspx | 2 +- .../Cache/CacheRefresherEventHandler.cs | 34 +- src/Umbraco.Web/Cache/DistributedCache.cs | 5 +- .../Cache/DistributedCacheExtensions.cs | 14 +- .../Cache/UserGroupCacheRefresher.cs | 66 +++ .../UserGroupPermissionsCacheRefresher.cs | 58 +++ .../Cache/UserPermissionsCacheRefresher.cs | 58 --- src/Umbraco.Web/Security/WebSecurity.cs | 1 - src/Umbraco.Web/Umbraco.Web.csproj | 16 +- ...Permissions.cs => UserGroupPermissions.cs} | 165 ++++--- .../umbraco/dialogs/cruds.aspx.cs | 72 ++- .../umbraco/dialogs/moveOrCopy.aspx.cs | 7 +- .../umbraco/users/EditUser.aspx.cs | 54 +-- .../umbraco/users/NodePermissions.ascx.cs | 103 ++--- .../umbraco/users/PermissionEditor.aspx | 2 +- .../umbraco/users/PermissionEditor.aspx.cs | 79 ++-- .../umbraco/users/PermissionsHandler.asmx.cs | 85 ++-- ...Permissions.cs => UserGroupPermissions.cs} | 292 ++++++------ src/umbraco.businesslogic/User.cs | 54 --- src/umbraco.cms/businesslogic/CMSNode.cs | 5 +- src/umbraco.cms/businesslogic/Permission.cs | 105 +++-- 66 files changed, 1430 insertions(+), 1625 deletions(-) create mode 100644 src/Umbraco.Core/Models/Membership/UserGroupEntityPermission.cs delete mode 100644 src/Umbraco.Core/Models/Rdbms/User2AppDto.cs delete mode 100644 src/Umbraco.Core/Models/Rdbms/User2NodePermissionDto.cs delete mode 100644 src/Umbraco.Core/Persistence/Factories/UserSectionFactory.cs delete mode 100644 src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs create mode 100644 src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs create mode 100644 src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs create mode 100644 src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs create mode 100644 src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs delete mode 100644 src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs rename src/Umbraco.Web/umbraco.presentation/umbraco/Trees/{UserPermissions.cs => UserGroupPermissions.cs} (57%) rename src/Umbraco.Web/umbraco.presentation/umbraco/users/{UserPermissions.cs => UserGroupPermissions.cs} (77%) diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index 0c1a202b66..596bf2455b 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -58,6 +58,8 @@ namespace Umbraco.Core.Cache public const string UserPermissionsCacheKey = "UmbracoUserPermissions"; + public const string UserGroupPermissionsCacheKey = "UmbracoUserGroupPermissions"; + [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] public const string ContentTypeCacheKey = "UmbracoContentType"; diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs index 50dc7d06f8..1618150851 100644 --- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs @@ -34,6 +34,7 @@ namespace Umbraco.Core.Models.Identity public int StartContentId { get; set; } public int StartMediaId { get; set; } public string[] AllowedSections { get; set; } + public string[] Groups { get; set; } public string Culture { get; set; } public string UserTypeAlias { get; set; } diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs index 0dc95a8987..74fd602a7e 100644 --- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs @@ -24,12 +24,14 @@ namespace Umbraco.Core.Models.Identity .ForMember(user => user.StartContentId, expression => expression.MapFrom(user => user.StartContentId)) .ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias)) .ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts)) + .ForMember(user => user.Groups, expression => expression.MapFrom(user => user.Groups.Select(x => x.Name).ToArray())) .ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray())); config.CreateMap() .ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id' .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) + .ForMember(detail => detail.Groups, opt => opt.MapFrom(user => user.Groups)) .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias })) .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId)) diff --git a/src/Umbraco.Core/Models/Membership/EntityPermission.cs b/src/Umbraco.Core/Models/Membership/EntityPermission.cs index 520a9b8d37..90430e26a8 100644 --- a/src/Umbraco.Core/Models/Membership/EntityPermission.cs +++ b/src/Umbraco.Core/Models/Membership/EntityPermission.cs @@ -3,18 +3,16 @@ namespace Umbraco.Core.Models.Membership { /// - /// Represents a user -> entity permission + /// Represents an entity permission (defined on the user group and derived to retrieve permissions for a given user) /// public class EntityPermission { - public EntityPermission(int userId, int entityId, string[] assignedPermissions) + public EntityPermission(int entityId, string[] assignedPermissions) { - UserId = userId; EntityId = entityId; AssignedPermissions = assignedPermissions; } - public int UserId { get; private set; } public int EntityId { get; private set; } /// diff --git a/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs index c4669caf59..ea1e927a59 100644 --- a/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs +++ b/src/Umbraco.Core/Models/Membership/EntityPermissionSet.cs @@ -13,30 +13,31 @@ namespace Umbraco.Core.Models.Membership public int EntityId { get; private set; } /// - /// The key/value pairs of user id & single permission + /// The key/value pairs of user group id & single permission /// - public IEnumerable UserPermissionsSet { get; private set; } + public IEnumerable PermissionsSet { get; private set; } - public EntityPermissionSet(int entityId, IEnumerable userPermissionsSet) + public EntityPermissionSet(int entityId, IEnumerable permissionsSet) { EntityId = entityId; - UserPermissionsSet = userPermissionsSet; + PermissionsSet = permissionsSet; } - public class UserPermission + public class UserGroupPermission { - public UserPermission(int userId, string permission) + public UserGroupPermission(int groupId, string permission) { - UserId = userId; + UserGroupId = groupId; Permission = permission; } - public int UserId { get; private set; } + public int UserGroupId { get; private set; } + public string Permission { get; private set; } - protected bool Equals(UserPermission other) + protected bool Equals(UserGroupPermission other) { - return UserId == other.UserId && string.Equals(Permission, other.Permission); + return UserGroupId == other.UserGroupId && string.Equals(Permission, other.Permission); } public override bool Equals(object obj) @@ -44,14 +45,14 @@ namespace Umbraco.Core.Models.Membership if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((UserPermission) obj); + return Equals((UserGroupPermission) obj); } public override int GetHashCode() { unchecked { - return (UserId*397) ^ Permission.GetHashCode(); + return (UserGroupId * 397) ^ Permission.GetHashCode(); } } } diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index 095044c47e..a3cea0b232 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -47,10 +47,6 @@ namespace Umbraco.Core.Models.Membership IEnumerable AllowedSections { get; } - void RemoveAllowedSection(string sectionAlias); - - void AddAllowedSection(string sectionAlias); - /// /// Exposes the basic profile data /// diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 6fad1a6f78..41bcb5ee80 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -25,12 +25,8 @@ namespace Umbraco.Core.Models.Membership _defaultPermissions = _userType.Permissions == null ? Enumerable.Empty() : new List(_userType.Permissions); //Groups = new List { userType }; SessionTimeout = 60; - _sectionCollection = new ObservableCollection(); - _addedSections = new List(); - _removedSections = new List(); _groupCollection = new List(); _language = GlobalSettings.DefaultUILanguage; - _sectionCollection.CollectionChanged += SectionCollectionChanged; _isApproved = true; _isLockedOut = false; _startContentId = -1; @@ -55,9 +51,6 @@ namespace Umbraco.Core.Models.Membership private IUserType _userType; private string _name; private string _securityStamp; - private List _addedSections; - private List _removedSections; - private ObservableCollection _sectionCollection; private List _groupCollection; private bool _groupsLoaded; private int _sessionTimeout; @@ -92,7 +85,6 @@ namespace Umbraco.Core.Models.Membership public readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo(x => x.SessionTimeout); public readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartContentId); public readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartMediaId); - public readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedSections); public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username); @@ -200,22 +192,16 @@ namespace Umbraco.Core.Models.Membership public IEnumerable AllowedSections { - get { return _sectionCollection; } - } - - public void RemoveAllowedSection(string sectionAlias) - { - if (_sectionCollection.Contains(sectionAlias)) + get { - _sectionCollection.Remove(sectionAlias); - } - } + if (GroupsLoaded == false) + { + return Enumerable.Empty(); + } - public void AddAllowedSection(string sectionAlias) - { - if (_sectionCollection.Contains(sectionAlias) == false) - { - _sectionCollection.Add(sectionAlias); + return Groups + .SelectMany(x => x.AllowedSections) + .Distinct(); } } @@ -234,22 +220,6 @@ namespace Umbraco.Core.Models.Membership set { SetPropertyValueAndDetectChanges(value, ref _securityStamp, Ps.Value.SecurityStampSelector); } } - /// - /// Used internally to check if we need to add a section in the repository to the db - /// - internal IEnumerable AddedSections - { - get { return _addedSections; } - } - - /// - /// Used internally to check if we need to remove a section in the repository to the db - /// - internal IEnumerable RemovedSections - { - get { return _removedSections; } - } - /// /// Gets or sets the session timeout. /// @@ -363,61 +333,15 @@ namespace Umbraco.Core.Models.Membership #endregion - /// - /// Whenever resetting occurs, clear the remembered add/removed collections, even if - /// rememberPreviouslyChangedProperties is true, the AllowedSections property will still - /// be flagged as dirty. - /// - /// - public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) - { - _addedSections.Clear(); - _removedSections.Clear(); - base.ResetDirtyProperties(rememberPreviouslyChangedProperties); - } - - /// - /// Handles the collection changed event in order for us to flag the AllowedSections property as changed - /// - /// - /// - void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - OnPropertyChanged(Ps.Value.AllowedSectionsSelector); - - if (e.Action == NotifyCollectionChangedAction.Add) - { - var item = e.NewItems.Cast().First(); - - if (_addedSections.Contains(item) == false) - { - _addedSections.Add(item); - } - } - else if (e.Action == NotifyCollectionChangedAction.Remove) - { - var item = e.OldItems.Cast().First(); - - if (_removedSections.Contains(item) == false) - { - _removedSections.Add(item); - } - } - } - public override object DeepClone() { var clone = (User)base.DeepClone(); //turn off change tracking clone.DisableChangeTracking(); //need to create new collections otherwise they'll get copied by ref - clone._addedSections = new List(); - clone._removedSections = new List(); - clone._sectionCollection = new ObservableCollection(_sectionCollection.ToList()); clone._groupCollection = new List(_groupCollection.ToList()); clone._defaultPermissions = new List(_defaultPermissions.ToList()); //re-create the event handler - clone._sectionCollection.CollectionChanged += clone.SectionCollectionChanged; //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); //re-enable tracking diff --git a/src/Umbraco.Core/Models/Membership/UserGroupEntityPermission.cs b/src/Umbraco.Core/Models/Membership/UserGroupEntityPermission.cs new file mode 100644 index 0000000000..619223f01a --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/UserGroupEntityPermission.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Core.Models.Membership +{ + /// + /// Represents a user group -> entity permission + /// + public class UserGroupEntityPermission : EntityPermission + { + public UserGroupEntityPermission(int groupId, int entityId, string[] assignedPermissions) + : base(entityId, assignedPermissions) + { + UserGroupId = groupId; + } + + public int UserGroupId { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/User2AppDto.cs b/src/Umbraco.Core/Models/Rdbms/User2AppDto.cs deleted file mode 100644 index eafc6be230..0000000000 --- a/src/Umbraco.Core/Models/Rdbms/User2AppDto.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Models.Rdbms -{ - [TableName("umbracoUser2app")] - [PrimaryKey("user", autoIncrement = false)] - [ExplicitColumns] - internal class User2AppDto - { - [Column("user")] - [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2app", OnColumns = "user, app")] - [ForeignKey(typeof(UserDto))] - public int UserId { get; set; } - - [Column("app")] - [Length(50)] - public string AppAlias { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/User2NodePermissionDto.cs b/src/Umbraco.Core/Models/Rdbms/User2NodePermissionDto.cs deleted file mode 100644 index 1e6662735f..0000000000 --- a/src/Umbraco.Core/Models/Rdbms/User2NodePermissionDto.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Models.Rdbms -{ - [TableName("umbracoUser2NodePermission")] - [PrimaryKey("userId", autoIncrement = false)] - [ExplicitColumns] - internal class User2NodePermissionDto - { - [Column("userId")] - [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoUser2NodePermission", OnColumns = "userId, nodeId, permission")] - [ForeignKey(typeof(UserDto))] - public int UserId { get; set; } - - [Column("nodeId")] - [ForeignKey(typeof(NodeDto))] - public int NodeId { get; set; } - - [Column("permission")] - public string Permission { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/UserDto.cs b/src/Umbraco.Core/Models/Rdbms/UserDto.cs index 05a93c86bf..d05cf1086b 100644 --- a/src/Umbraco.Core/Models/Rdbms/UserDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/UserDto.cs @@ -73,8 +73,5 @@ namespace Umbraco.Core.Models.Rdbms [Column("lastLoginDate")] [NullSetting(NullSetting = NullSettings.Null)] public DateTime? LastLoginDate { get; set; } - - [ResultColumn] - public List User2AppDtos { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index 322e8b5b65..a404c43450 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -43,14 +43,6 @@ namespace Umbraco.Core.Persistence.Factories user.LastLoginDate = dto.LastLoginDate ?? DateTime.MinValue; user.LastPasswordChangeDate = dto.LastPasswordChangeDate ?? DateTime.MinValue; - if (dto.User2AppDtos != null) - { - foreach (var app in dto.User2AppDtos) - { - user.AddAllowedSection(app.AppAlias); - } - } - //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 user.ResetDirtyProperties(false); @@ -77,7 +69,6 @@ namespace Umbraco.Core.Persistence.Factories UserLanguage = entity.Language, UserName = entity.Name, Type = short.Parse(entity.UserType.Id.ToString(CultureInfo.InvariantCulture)), - User2AppDtos = new List(), SecurityStampToken = entity.SecurityStamp, FailedLoginAttempts = entity.FailedPasswordAttempts, LastLockoutDate = entity.LastLockoutDate == DateTime.MinValue ? (DateTime?)null : entity.LastLockoutDate, @@ -85,20 +76,6 @@ namespace Umbraco.Core.Persistence.Factories LastPasswordChangeDate = entity.LastPasswordChangeDate == DateTime.MinValue ? (DateTime?)null : entity.LastPasswordChangeDate, }; - foreach (var app in entity.AllowedSections) - { - var appDto = new User2AppDto - { - AppAlias = app - }; - if (entity.HasIdentity) - { - appDto.UserId = entity.Id; - } - - dto.User2AppDtos.Add(appDto); - } - if (entity.HasIdentity) { dto.Id = entity.Id.SafeCast(); diff --git a/src/Umbraco.Core/Persistence/Factories/UserSectionFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserSectionFactory.cs deleted file mode 100644 index 0e6a17594f..0000000000 --- a/src/Umbraco.Core/Persistence/Factories/UserSectionFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AutoMapper; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class UserSectionFactory - { - private readonly IUser _user; - - public UserSectionFactory(IUser user) - { - _user = user; - } - - public IEnumerable BuildEntity(IEnumerable dto) - { - return dto.Select(x => x.AppAlias); - } - - public IEnumerable BuildDto(IEnumerable entity) - { - return entity.Select(x => new User2AppDto - { - //NOTE: We're force casting to int here! this might not work in the future - UserId = (int)_user.Id, - AppAlias = x - }); - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 9570024b09..68d43a1acd 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -48,11 +48,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial CreateUmbracoUserTypeData(); } - if (tableName.Equals("umbracoUser2app")) - { - CreateUmbracoUser2AppData(); - } - if (tableName.Equals("cmsPropertyTypeGroup")) { CreateCmsPropertyTypeGroupData(); @@ -168,17 +163,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("umbracoUserType", "id", false, new UserTypeDto { Id = 4, Alias = "translator", Name = "Translator", DefaultPermissions = "AF" }); } - private void CreateUmbracoUser2AppData() - { - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Content }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Developer }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Media }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Members }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Settings }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Users }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Forms }); - } - private void CreateCmsPropertyTypeGroupData() { _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) }); diff --git a/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs b/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs deleted file mode 100644 index 923348e729..0000000000 --- a/src/Umbraco.Core/Persistence/Relators/UserSectionRelator.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Relators -{ - internal class UserSectionRelator - { - internal UserDto Current; - - internal UserDto Map(UserDto a, User2AppDto p) - { - // Terminating call. Since we can return null from this function - // we need to be ready for PetaPoco to callback later with null - // parameters - if (a == null) - return Current; - - // Is this the same DictionaryItem as the current one we're processing - if (Current != null && Current.Id == a.Id) - { - if (p.AppAlias.IsNullOrWhiteSpace() == false) - { - // Yes, just add this User2AppDto to the current item's collection - Current.User2AppDtos.Add(p); - } - - // Return null to indicate we're not done with this User yet - return null; - } - - // This is a different User to the current one, or this is the - // first time through and we don't have one yet - - // Save the current User - var prev = Current; - - // Setup the new current User - Current = a; - Current.User2AppDtos = new List(); - //this can be null since we are doing a left join - if (p.AppAlias.IsNullOrWhiteSpace() == false) - { - Current.User2AppDtos.Add(p); - } - - // Return the now populated previous User (or null if first time through) - return prev; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index cafbabd286..03b3b9e6e9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -394,12 +394,12 @@ namespace Umbraco.Core.Persistence.Repositories // user's default permissions. if (parentPermissions.Any()) { - var userPermissions = ( + var userGroupPermissions = ( from perm in parentPermissions from p in perm.AssignedPermissions - select new EntityPermissionSet.UserPermission(perm.UserId, p)).ToList(); + select new EntityPermissionSet.UserGroupPermission(perm.UserGroupId, p)).ToList(); - permissionsRepo.ReplaceEntityPermissions(new EntityPermissionSet(entity.Id, userPermissions)); + permissionsRepo.ReplaceEntityPermissions(new EntityPermissionSet(entity.Id, userGroupPermissions)); //flag the entity's permissions changed flag so we can track those changes. //Currently only used for the cache refreshers to detect if we should refresh all user permissions cache. ((Content)entity).PermissionsChanged = true; @@ -791,7 +791,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder"; repo.AssignEntityPermission(entity, permission, userIds); } - public IEnumerable GetPermissionsForEntity(int entityId) + public IEnumerable GetPermissionsForEntity(int entityId) { var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); return repo.GetPermissionsForEntity(entityId); diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index a4d71885f8..b95d3f0d62 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -248,7 +248,7 @@ AND umbracoNode.id <> @id", Database.Delete("WHERE nodeId = @Id", new { Id = entity.Id }); //Remove Permissions - Database.Delete("WHERE nodeId = @Id", new { Id = entity.Id }); + Database.Delete("WHERE nodeId = @Id", new { Id = entity.Id }); //Remove associated tags Database.Delete("WHERE nodeId = @Id", new { Id = entity.Id }); diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index e2555995d2..5f74a21d7a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -60,7 +60,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - IEnumerable GetPermissionsForEntity(int entityId); + IEnumerable GetPermissionsForEntity(int entityId); /// /// Used to add/update published xml for the content item diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs index e50d265228..45abdc70b4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs @@ -24,7 +24,6 @@ namespace Umbraco.Core.Persistence.Repositories /// Ids of users void AddUsersToGroup(int groupId, int[] userIds); - /* /// /// Gets the group permissions for the specified entities /// @@ -47,6 +46,5 @@ namespace Umbraco.Core.Persistence.Repositories /// Permissions as enumerable list of /// Specify the nodes to replace permissions for void AssignGroupPermission(int groupId, char permission, params int[] entityIds); - */ } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index 5645cafa2a..620350f563 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -22,12 +22,6 @@ namespace Umbraco.Core.Persistence.Repositories /// bool Exists(string username); - /// - /// This is useful when an entire section is removed from config - /// - /// - IEnumerable GetUsersAssignedToSection(string sectionAlias); - /// /// Gets all groups for a given user /// @@ -57,30 +51,5 @@ namespace Umbraco.Core.Persistence.Repositories /// /// IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy); - - - /// - /// Gets the user permissions for the specified entities - /// - /// - /// - /// - IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds); - - /// - /// Replaces the same permission set for a single user to any number of entities - /// - /// - /// - /// - void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); - - /// - /// Assigns the same permission set for a single user to any number of entities - /// - /// - /// - /// - void AssignUserPermission(int userId, char permission, params int[] entityIds); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index aa671dccec..83c31f6651 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -15,6 +15,15 @@ using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Services; using CacheKeys = Umbraco.Core.Cache.CacheKeys; using Umbraco.Core.Cache; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { @@ -38,196 +47,227 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// Returns permissions for a given user for any number of nodes + /// Returns permissions for a given group for any number of nodes /// - /// + /// /// /// - public IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds) + public IEnumerable GetPermissionsForEntities(int groupId, params int[] entityIds) { - var entityIdKey = string.Join(",", entityIds.Select(x => x.ToString(CultureInfo.InvariantCulture))); + var entityIdKey = GetEntityIdKey(entityIds); return _runtimeCache.GetCacheItem>( - string.Format("{0}{1}{2}", CacheKeys.UserPermissionsCacheKey, userId, entityIdKey), + string.Format("{0}{1}{2}", + CacheKeys.UserGroupPermissionsCacheKey, + groupId, + entityIdKey), () => - { - - var whereBuilder = new StringBuilder(); - - //where userId = @userId AND - whereBuilder.Append(_sqlSyntax.GetQuotedColumnName("userId")); - whereBuilder.Append("="); - whereBuilder.Append(userId); - - if (entityIds.Any()) - { - whereBuilder.Append(" AND "); - - //where nodeId = @nodeId1 OR nodeId = @nodeId2, etc... - whereBuilder.Append("("); - for (var index = 0; index < entityIds.Length; index++) - { - var entityId = entityIds[index]; - whereBuilder.Append(_sqlSyntax.GetQuotedColumnName("nodeId")); - whereBuilder.Append("="); - whereBuilder.Append(entityId); - if (index < entityIds.Length - 1) - { - whereBuilder.Append(" OR "); - } - } - whereBuilder.Append(")"); - } - + { + var whereCriteria = GetPermissionsForEntitiesCriteria(groupId, entityIds); var sql = new Sql(); sql.Select("*") - .From() - .Where(whereBuilder.ToString()); - - //ToArray() to ensure it's all fetched from the db once. - var result = _unitOfWork.Database.Fetch(sql).ToArray(); + .From() + .Where(whereCriteria); + var result = _unitOfWork.Database.Fetch(sql).ToArray(); + // ToArray() to ensure it's all fetched from the db once return ConvertToPermissionList(result); - }, - //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes, - // then it will refresh from the database. - new TimeSpan(0, 20, 0), - //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average - priority: CacheItemPriority.BelowNormal); + GetCacheTimeout(), + priority: GetCachePriority()); + } - } + private static string GetEntityIdKey(int[] entityIds) + { + return string.Join(",", entityIds.Select(x => x.ToString(CultureInfo.InvariantCulture))); + } + + private string GetPermissionsForEntitiesCriteria(int groupId, params int[] entityIds) + { + var whereBuilder = new StringBuilder(); + whereBuilder.Append(_sqlSyntax.GetQuotedColumnName("userGroupId")); + whereBuilder.Append("="); + whereBuilder.Append(groupId); + + if (entityIds.Any()) + { + whereBuilder.Append(" AND "); + + //where nodeId = @nodeId1 OR nodeId = @nodeId2, etc... + whereBuilder.Append("("); + for (var index = 0; index < entityIds.Length; index++) + { + var entityId = entityIds[index]; + whereBuilder.Append(_sqlSyntax.GetQuotedColumnName("nodeId")); + whereBuilder.Append("="); + whereBuilder.Append(entityId); + if (index < entityIds.Length - 1) + { + whereBuilder.Append(" OR "); + } + } + whereBuilder.Append(")"); + } + + return whereBuilder.ToString(); + } + + private static TimeSpan GetCacheTimeout() + { + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes, + // then it will refresh from the database. + return new TimeSpan(0, 20, 0); + } + + private static CacheItemPriority GetCachePriority() + { + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average + return CacheItemPriority.BelowNormal; + } + + private static IEnumerable ConvertToPermissionList(IEnumerable result) + { + var permissions = new List(); + var nodePermissions = result.GroupBy(x => x.NodeId); + foreach (var np in nodePermissions) + { + var userGroupPermissions = np.GroupBy(x => x.UserGroupId); + foreach (var permission in userGroupPermissions) + { + var perms = permission.Select(x => x.Permission).ToArray(); + permissions.Add(new UserGroupEntityPermission(permission.Key, permission.First().NodeId, perms)); + } + } + + return permissions; + } /// - /// Returns permissions for all users for a given entity + /// Returns permissions for all groups for a given entity /// /// /// - public IEnumerable GetPermissionsForEntity(int entityId) + public IEnumerable GetPermissionsForEntity(int entityId) { var sql = new Sql(); sql.Select("*") - .From() - .Where(dto => dto.NodeId == entityId) - .OrderBy(dto => dto.NodeId); + .From() + .Where(dto => dto.NodeId == entityId) + .OrderBy(dto => dto.NodeId); - //ToArray() to ensure it's all fetched from the db once. - var result = _unitOfWork.Database.Fetch(sql).ToArray(); + var result = _unitOfWork.Database.Fetch(sql).ToArray(); + // ToArray() to ensure it's all fetched from the db once return ConvertToPermissionList(result); } /// - /// Assigns the same permission set for a single user to any number of entities + /// Assigns the same permission set for a single group to any number of entities /// - /// + /// /// /// /// /// This will first clear the permissions for this user and entities and recreate them /// - public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) + public void ReplacePermissions(int groupId, IEnumerable permissions, params int[] entityIds) { var db = _unitOfWork.Database; using (var trans = db.GetTransaction()) { //we need to batch these in groups of 2000 so we don't exceed the max 2100 limit + var sql = "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @groupId AND nodeId in (@nodeIds)"; foreach (var idGroup in entityIds.InGroupsOf(2000)) { - db.Execute("DELETE FROM umbracoUser2NodePermission WHERE userId=@userId AND nodeId in (@nodeIds)", - new { userId = userId, nodeIds = idGroup }); + db.Execute(sql, new {groupId = groupId, nodeIds = idGroup}); } - var toInsert = new List(); + var toInsert = new List(); foreach (var p in permissions) { foreach (var e in entityIds) { - toInsert.Add(new User2NodePermissionDto + toInsert.Add(new UserGroup2NodePermissionDto { NodeId = e, Permission = p.ToString(CultureInfo.InvariantCulture), - UserId = userId + UserGroupId = groupId }); } } _unitOfWork.Database.BulkInsertRecords(toInsert, trans); - trans.Complete(); - //Raise the event AssignedPermissions.RaiseEvent( - new SaveEventArgs(ConvertToPermissionList(toInsert), false), this); + new SaveEventArgs(ConvertToPermissionList(toInsert), false), this); } } /// /// Assigns one permission for a user to many entities /// - /// + /// /// /// - public void AssignUserPermission(int userId, char permission, params int[] entityIds) + public void AssignPermission(int groupId, char permission, params int[] entityIds) { var db = _unitOfWork.Database; using (var trans = db.GetTransaction()) { - db.Execute("DELETE FROM umbracoUser2NodePermission WHERE userId=@userId AND permission=@permission AND nodeId in (@entityIds)", + var sql = "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @groupId AND permission=@permission AND nodeId in (@entityIds)"; + db.Execute(sql, new { - userId = userId, + groupId = groupId, permission = permission.ToString(CultureInfo.InvariantCulture), entityIds = entityIds }); - var actions = entityIds.Select(id => new User2NodePermissionDto + var actions = entityIds.Select(id => new UserGroup2NodePermissionDto { NodeId = id, Permission = permission.ToString(CultureInfo.InvariantCulture), - UserId = userId + UserGroupId = groupId }).ToArray(); _unitOfWork.Database.BulkInsertRecords(actions, trans); - trans.Complete(); - //Raise the event AssignedPermissions.RaiseEvent( - new SaveEventArgs(ConvertToPermissionList(actions), false), this); + new SaveEventArgs(ConvertToPermissionList(actions), false), this); } - } + } /// - /// Assigns one permission to an entity for multiple users + /// Assigns one permission to an entity for multiple groups /// /// /// - /// - public void AssignEntityPermission(TEntity entity, char permission, IEnumerable userIds) + /// + public void AssignEntityPermission(TEntity entity, char permission, IEnumerable groupIds) { var db = _unitOfWork.Database; using (var trans = db.GetTransaction()) { - db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId AND permission=@permission AND userId in (@userIds)", + var sql = "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @nodeId AND permission = @permission AND userGroupId in (@groupIds)"; + db.Execute(sql, new { - nodeId = entity.Id, + nodeId = entity.Id, permission = permission.ToString(CultureInfo.InvariantCulture), - userIds = userIds + groupIds = groupIds }); - var actions = userIds.Select(id => new User2NodePermissionDto + var actions = groupIds.Select(id => new UserGroup2NodePermissionDto { NodeId = entity.Id, Permission = permission.ToString(CultureInfo.InvariantCulture), - UserId = id + UserGroupId = id }).ToArray(); _unitOfWork.Database.BulkInsertRecords(actions, trans); - trans.Complete(); - //Raise the event AssignedPermissions.RaiseEvent( - new SaveEventArgs(ConvertToPermissionList(actions), false), this); + new SaveEventArgs(ConvertToPermissionList(actions), false), this); } } @@ -244,41 +284,24 @@ namespace Umbraco.Core.Persistence.Repositories var db = _unitOfWork.Database; using (var trans = db.GetTransaction()) { - db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId", new { nodeId = permissionSet.EntityId }); + var sql = "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @nodeId"; + db.Execute(sql, new {nodeId = permissionSet.EntityId}); - var actions = permissionSet.UserPermissionsSet.Select(p => new User2NodePermissionDto + var actions = permissionSet.PermissionsSet.Select(p => new UserGroup2NodePermissionDto { NodeId = permissionSet.EntityId, Permission = p.Permission, - UserId = p.UserId + UserGroupId = p.UserGroupId }).ToArray(); _unitOfWork.Database.BulkInsertRecords(actions, trans); - trans.Complete(); - //Raise the event AssignedPermissions.RaiseEvent( - new SaveEventArgs(ConvertToPermissionList(actions), false), this); - } - } - - private static IEnumerable ConvertToPermissionList(IEnumerable result) - { - var permissions = new List(); - var nodePermissions = result.GroupBy(x => x.NodeId); - foreach (var np in nodePermissions) - { - var userPermissions = np.GroupBy(x => x.UserId); - foreach (var up in userPermissions) - { - var perms = up.Select(x => x.Permission).ToArray(); - permissions.Add(new EntityPermission(up.Key, up.First().NodeId, perms)); - } + new SaveEventArgs(ConvertToPermissionList(actions), false), this); } - return permissions; } - public static event TypedEventHandler, SaveEventArgs> AssignedPermissions; + public static event TypedEventHandler, SaveEventArgs> AssignedPermissions; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs index ba47270379..b13033c7b3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs @@ -76,16 +76,15 @@ namespace Umbraco.Core.Persistence.Repositories } } - /* /// /// Gets the group permissions for the specified entities /// /// Id of group /// Array of entity Ids - public IEnumerable GetGroupPermissionsForEntities(int groupId, params int[] entityIds) + public IEnumerable GetPermissionsForEntities(int groupId, params int[] entityIds) { - var repo = new GroupPermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - return repo.GetGroupPermissionsForEntities(groupId, entityIds); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + return repo.GetPermissionsForEntities(groupId, entityIds); } /// @@ -96,8 +95,8 @@ namespace Umbraco.Core.Persistence.Repositories /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. public void ReplaceGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds) { - var repo = new GroupPermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - repo.ReplaceGroupPermissions(groupId, permissions, entityIds); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + repo.ReplacePermissions(groupId, permissions, entityIds); } /// @@ -108,10 +107,9 @@ namespace Umbraco.Core.Persistence.Repositories /// Specify the nodes to replace permissions for public void AssignGroupPermission(int groupId, char permission, params int[] entityIds) { - var repo = new GroupPermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - repo.AssignGroupPermission(groupId, permission, entityIds); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + repo.AssignPermission(groupId, permission, entityIds); } - */ #region Overrides of RepositoryBase diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 72d20ff660..f5e3fda7c5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { Id = id }); - var dto = Database.Fetch(new UserSectionRelator().Map, sql).FirstOrDefault(); + var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; @@ -74,7 +74,7 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where("umbracoUser.id in (@ids)", new {ids = ids}); } - return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)) + return ConvertFromDtos(Database.Fetch(sql)) .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. } @@ -84,7 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); - var dtos = Database.Fetch(new UserSectionRelator().Map, sql) + var dtos = Database.Fetch(sql) .DistinctBy(x => x.Id); var users = ConvertFromDtos(dtos) @@ -121,9 +121,7 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = new Sql(); sql.Select(columns) - .From() - .LeftJoin() - .On(left => left.Id, right => right.UserId); + .From(); return sql; } @@ -139,9 +137,8 @@ namespace Umbraco.Core.Persistence.Repositories { "DELETE FROM cmsTask WHERE userId = @Id", "DELETE FROM cmsTask WHERE parentUserId = @Id", - "DELETE FROM umbracoUser2NodePermission WHERE userId = @Id", + "DELETE FROM umbracoUser2UserGroup WHERE userId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id", - "DELETE FROM umbracoUser2app WHERE " + SqlSyntax.GetQuotedColumnName("user") + "=@Id", "DELETE FROM umbracoUser WHERE id = @Id", "DELETE FROM umbracoExternalLogin WHERE id = @Id" }; @@ -168,13 +165,6 @@ namespace Umbraco.Core.Persistence.Repositories var id = Convert.ToInt32(Database.Insert(userDto)); entity.Id = id; - foreach (var sectionDto in userDto.User2AppDtos) - { - //need to set the id explicitly here - sectionDto.UserId = id; - Database.Insert(sectionDto); - } - entity.ResetDirtyProperties(); } @@ -238,52 +228,23 @@ namespace Umbraco.Core.Persistence.Repositories { Database.Update(userDto, changedCols); } - - //update the sections if they've changed + + //update the groups var user = (User)entity; - if (user.IsPropertyDirty("AllowedSections")) + + //first delete all + Database.Delete("WHERE UserId = @UserId", + new { UserId = user.Id }); + + //then re-add any associated with the user + foreach (var group in user.Groups) { - //now we need to delete any applications that have been removed - foreach (var section in user.RemovedSections) + var dto = new User2UserGroupDto { - //we need to manually delete this record because it has a composite key - Database.Delete("WHERE app = @Section AND " + SqlSyntax.GetQuotedColumnName("user") + "= @UserId", - new { Section = section, UserId = user.Id }); - } - - //for any that exist on the object, we need to determine if we need to update or insert - //NOTE: the User2AppDtos collection wil always be equal to the User.AllowedSections - foreach (var sectionDto in userDto.User2AppDtos) - { - //if something has been added then insert it - if (user.AddedSections.Contains(sectionDto.AppAlias)) - { - //we need to insert since this was added - Database.Insert(sectionDto); - } - else - { - //we need to manually update this record because it has a composite key - Database.Update("SET app=@Section WHERE app=@Section AND " + SqlSyntax.GetQuotedColumnName("user") + "=@UserId", - new { Section = sectionDto.AppAlias, UserId = sectionDto.UserId }); - } - } - - //update the groups - //first delete all - Database.Delete("WHERE UserId = @UserId", - new { UserId = user.Id }); - - //then re-add any associated with the user - foreach (var group in user.Groups) - { - var dto = new User2UserGroupDto - { - UserGroupId = group.Id, - UserId = user.Id - }; - Database.Insert(dto); - } + UserGroupId = group.Id, + UserId = user.Id + }; + Database.Insert(dto); } entity.ResetDirtyProperties(); @@ -316,29 +277,6 @@ namespace Umbraco.Core.Persistence.Repositories return Database.ExecuteScalar(sql) > 0; } - public IEnumerable GetUsersAssignedToSection(string sectionAlias) - { - //Here we're building up a query that looks like this, a sub query is required because the resulting structure - // needs to still contain all of the section rows per user. - - //SELECT * - //FROM [umbracoUser] - //LEFT JOIN [umbracoUser2app] - //ON [umbracoUser].[id] = [umbracoUser2app].[user] - //WHERE umbracoUser.id IN (SELECT umbracoUser.id - // FROM [umbracoUser] - // LEFT JOIN [umbracoUser2app] - // ON [umbracoUser].[id] = [umbracoUser2app].[user] - // WHERE umbracoUser2app.app = 'content') - - var sql = GetBaseQuery(false); - var innerSql = GetBaseQuery("umbracoUser.id"); - innerSql.Where("umbracoUser2app.app = " + SqlSyntax.GetQuotedValue(sectionAlias)); - sql.Where(string.Format("umbracoUser.id IN ({0})", innerSql.SQL)); - - return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)); - } - /// /// Gets all groups for a given user /// @@ -462,42 +400,6 @@ namespace Umbraco.Core.Persistence.Repositories return ids.Length == 0 ? Enumerable.Empty() : GetAll(ids).OrderBy(x => x.Id); } - /// - /// Returns permissions for a given user for any number of nodes - /// - /// - /// - /// - public IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds) - { - var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - return repo.GetUserPermissionsForEntities(userId, entityIds); - } - - /// - /// Replaces the same permission set for a single user to any number of entities - /// - /// - /// - /// - public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) - { - var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - repo.ReplaceUserPermissions(userId, permissions, entityIds); - } - - /// - /// Assigns the same permission set for a single user to any number of entities - /// - /// - /// - /// - public void AssignUserPermission(int userId, char permission, params int[] entityIds) - { - var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - repo.AssignUserPermission(userId, permission, entityIds); - } - #endregion private IEnumerable ConvertFromDtos(IEnumerable dtos) diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index c6714e256a..fcdf623dba 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -389,7 +389,7 @@ namespace Umbraco.Core.Security /// - /// Adds a user to a role (section) + /// Adds a user to a role (user group) /// /// /// @@ -406,18 +406,19 @@ namespace Umbraco.Core.Security throw new InvalidOperationException("The user id must be an integer to work with the Umbraco"); } - var found = _userService.GetUserById(asInt.Result); + var foundUser = _userService.GetUserById(asInt.Result); + var foundGroup = _userService.GetUserGroupByName(roleName); - if (found != null) + if (foundUser != null && foundGroup != null) { - found.AddAllowedSection(roleName); + foundUser.AddGroup(foundGroup); } return Task.FromResult(0); } /// - /// Removes the role (allowed section) for the user + /// Removes the role (user group) for the user /// /// /// @@ -434,18 +435,19 @@ namespace Umbraco.Core.Security throw new InvalidOperationException("The user id must be an integer to work with the Umbraco"); } - var found = _userService.GetUserById(asInt.Result); + var foundUser = _userService.GetUserById(asInt.Result); + var foundGroup = _userService.GetUserGroupByName(roleName); - if (found != null) + if (foundUser != null && foundGroup != null) { - found.RemoveAllowedSection(roleName); + foundUser.RemoveGroup(foundGroup); } return Task.FromResult(0); } /// - /// Returns the roles for this user + /// Returns the roles (user groups) for this user /// /// /// @@ -453,7 +455,7 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException("user"); - return Task.FromResult((IList)user.AllowedSections.ToList()); + return Task.FromResult((IList)user.Groups.ToList()); } /// @@ -465,7 +467,7 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException("user"); - return Task.FromResult(user.AllowedSections.InvariantContains(roleName)); + return Task.FromResult(user.Groups.InvariantContains(roleName)); } /// @@ -677,17 +679,21 @@ namespace Umbraco.Core.Security user.SecurityStamp = identityUser.SecurityStamp; } - if (user.AllowedSections.ContainsAll(identityUser.AllowedSections) == false - || identityUser.AllowedSections.ContainsAll(user.AllowedSections) == false) + if (user.Groups.Select(x => x.Name).ContainsAll(identityUser.Groups) == false + || identityUser.Groups.ContainsAll(user.Groups.Select(x => x.Name)) == false) { anythingChanged = true; - foreach (var allowedSection in user.AllowedSections) + foreach (var group in user.Groups) { - user.RemoveAllowedSection(allowedSection); + user.RemoveGroup(group); } - foreach (var allowedApplication in identityUser.AllowedSections) + foreach (var group in identityUser.Groups) { - user.AddAllowedSection(allowedApplication); + var foundGroup = _userService.GetUserGroupByName(group); + if (foundGroup != null) + { + user.AddGroup(foundGroup); + } } } diff --git a/src/Umbraco.Core/Security/UserData.cs b/src/Umbraco.Core/Security/UserData.cs index 407d2782dd..b1a8b08507 100644 --- a/src/Umbraco.Core/Security/UserData.cs +++ b/src/Umbraco.Core/Security/UserData.cs @@ -56,6 +56,9 @@ namespace Umbraco.Core.Security [DataMember(Name = "allowedApps")] public string[] AllowedApplications { get; set; } + [DataMember(Name = "groups")] + public string[] Groups { get; set; } + [DataMember(Name = "culture")] public string Culture { get; set; } } diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 38b7aec9d7..8aa7030a8f 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -126,7 +126,7 @@ namespace Umbraco.Core.Services /// /// /// - public IEnumerable GetPermissionsForEntity(IContent content) + public IEnumerable GetPermissionsForEntity(IContent content) { var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateContentRepository(uow)) diff --git a/src/Umbraco.Core/Services/ContentServiceExtensions.cs b/src/Umbraco.Core/Services/ContentServiceExtensions.cs index 4919a81217..9c56cf28eb 100644 --- a/src/Umbraco.Core/Services/ContentServiceExtensions.cs +++ b/src/Umbraco.Core/Services/ContentServiceExtensions.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Services /// public static void RemoveContentPermissions(this IContentService contentService, int contentId) { - contentService.ReplaceContentPermissions(new EntityPermissionSet(contentId, Enumerable.Empty())); + contentService.ReplaceContentPermissions(new EntityPermissionSet(contentId, Enumerable.Empty())); } /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index ec5db8c4fb..6ee384c413 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -135,7 +135,7 @@ namespace Umbraco.Core.Services /// /// /// - IEnumerable GetPermissionsForEntity(IContent content); + IEnumerable GetPermissionsForEntity(IContent content); bool SendToPublication(IContent content, int userId = 0); diff --git a/src/Umbraco.Core/Services/ISectionService.cs b/src/Umbraco.Core/Services/ISectionService.cs index df5c89d4bf..d726179224 100644 --- a/src/Umbraco.Core/Services/ISectionService.cs +++ b/src/Umbraco.Core/Services/ISectionService.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Services IEnumerable
GetSections(); /// - /// Get the user's allowed sections + /// Get the user group's allowed sections /// /// /// diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 2a3ebf9828..fa69cb30a3 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -48,19 +48,19 @@ namespace Umbraco.Core.Services IUser GetUserById(int id); /// - /// Removes a specific section from all users + /// Removes a specific section from all user groups /// /// This is useful when an entire section is removed from config /// Alias of the section to remove - void DeleteSectionFromAllUsers(string sectionAlias); - + void DeleteSectionFromAllUserGroups(string sectionAlias); + /// - /// Add a specific section to all users or those specified as parameters + /// Add a specific section to all user groups or those specified as parameters /// - /// This is useful when a new section is created to allow specific users accessing it + /// This is useful when a new section is created to allow specific user groups to access it /// Alias of the section to add - /// Specifiying nothing will add the section to all user - void AddSectionToAllUsers(string sectionAlias, params int[] userIds); + /// Specifiying nothing will add the section to all user + void AddSectionToAllUserGroups(string sectionAlias, params int[] groupIds); /// /// Get permissions set for a user and optional node ids @@ -96,24 +96,24 @@ namespace Umbraco.Core.Services string GetPermissionsForPath(IUserGroup group, string path); /// - /// Replaces the same permission set for a single user to any number of entities + /// Replaces the same permission set for a single group to any number of entities /// - /// Id of the user + /// Id of the group /// /// Permissions as enumerable list of , - /// if no permissions are specified then all permissions for this node are removed for this user + /// if no permissions are specified then all permissions for this node are removed for this group /// /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. - /// If no 'entityIds' are specified all permissions will be removed for the specified user. - void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); + /// If no 'entityIds' are specified all permissions will be removed for the specified group. + void ReplaceUserGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds); /// - /// Assigns the same permission set for a single user to any number of entities + /// Assigns the same permission set for a single user group to any number of entities /// - /// Id of the user + /// Id of the group /// /// Specify the nodes to replace permissions for - void AssignUserPermission(int userId, char permission, params int[] entityIds); + void AssignUserGroupPermission(int groupId, char permission, params int[] entityIds); /// /// Gets a list of objects associated with a given group diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index fcd8c45536..1a5abb8ee7 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -147,9 +147,6 @@ namespace Umbraco.Core.Services IsLockedOut = false, IsApproved = true }; - //adding default sections content and media - user.AddAllowedSection("content"); - user.AddAllowedSection("media"); if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(user), this)) return user; @@ -584,33 +581,33 @@ namespace Umbraco.Core.Services } /// - /// Replaces the same permission set for a single user to any number of entities + /// Replaces the same permission set for a single group to any number of entities /// - /// If no 'entityIds' are specified all permissions will be removed for the specified user. - /// Id of the user + /// If no 'entityIds' are specified all permissions will be removed for the specified group. + /// Id of the group /// Permissions as enumerable list of /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. - public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) + public void ReplaceUserGroupPermissions(int groupId, IEnumerable permissions, params int[] entityIds) { var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserRepository(uow)) + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) { - repository.ReplaceUserPermissions(userId, permissions, entityIds); + repository.ReplaceGroupPermissions(groupId, permissions, entityIds); } } /// - /// Assigns the same permission set for a single user to any number of entities + /// Assigns the same permission set for a single user group to any number of entities /// - /// Id of the user + /// Id of the user group /// /// Specify the nodes to replace permissions for - public void AssignUserPermission(int userId, char permission, params int[] entityIds) + public void AssignUserGroupPermission(int groupId, char permission, params int[] entityIds) { var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserRepository(uow)) + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) { - repository.AssignUserPermission(userId, permission, entityIds); + repository.AssignGroupPermission(groupId, permission, entityIds); } } @@ -843,48 +840,50 @@ namespace Umbraco.Core.Services /// /// This is useful when an entire section is removed from config /// Alias of the section to remove - public void DeleteSectionFromAllUsers(string sectionAlias) + public void DeleteSectionFromAllUserGroups(string sectionAlias) { var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserRepository(uow)) + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) { - var assignedUsers = repository.GetUsersAssignedToSection(sectionAlias); - foreach (var user in assignedUsers) + var assignedGroups = repository.GetGroupsAssignedToSection(sectionAlias); + foreach (var group in assignedGroups) { //now remove the section for each user and commit - user.RemoveAllowedSection(sectionAlias); - repository.AddOrUpdate(user); + group.RemoveAllowedSection(sectionAlias); + repository.AddOrUpdate(group); } + uow.Commit(); } } - + /// - /// Add a specific section to all users or those specified as parameters + /// Add a specific section to all user groups or those specified as parameters /// - /// This is useful when a new section is created to allow specific users accessing it + /// This is useful when a new section is created to allow specific user groups to access it /// Alias of the section to add - /// Specifiying nothing will add the section to all user - public void AddSectionToAllUsers(string sectionAlias, params int[] userIds) + /// Specifiying nothing will add the section to all user + public void AddSectionToAllUserGroups(string sectionAlias, params int[] groupIds) { var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserRepository(uow)) + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) { - IEnumerable users; - if (userIds.Any()) + IEnumerable groups; + if (groupIds.Any()) { - users = repository.GetAll(userIds); + groups = repository.GetAll(groupIds); } else { - users = repository.GetAll(); + groups = repository.GetAll(); } - foreach (var user in users.Where(u => !u.AllowedSections.InvariantContains(sectionAlias))) + foreach (var group in groups.Where(g => g.AllowedSections.InvariantContains(sectionAlias) == false)) { - //now add the section for each user and commit - user.AddAllowedSection(sectionAlias); - repository.AddOrUpdate(user); + //now add the section for each group and commit + group.AddAllowedSection(sectionAlias); + repository.AddOrUpdate(group); } + uow.Commit(); } } @@ -901,16 +900,15 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateUserRepository(uow)) { - var explicitPermissions = repository.GetUserPermissionsForEntities(user.Id, nodeIds); + // TODO: rework (to use groups - currently defaulting to user type) //if no permissions are assigned to a particular node then we will fill in those permissions with the user's defaults - var result = new List(explicitPermissions); + var result = new List(); var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); foreach (var id in missingIds) { result.Add( new EntityPermission( - user.Id, id, user.DefaultPermissions.ToArray())); } @@ -939,16 +937,9 @@ namespace Umbraco.Core.Services /// An enumerable list of private IEnumerable GetPermissions(IUserRepository repository, IUser user, params int[] nodeIds) { - var explicitPermissions = repository.GetUserPermissionsForEntities(user.Id, nodeIds); - var result = new List(explicitPermissions); + var result = new List(); - // Save list of nodes that user has explicit permissions for - these take priority so we don't want - // to amend with any settings from groups (we'll take the special "null" action as not being defined - // as this is set if someone sets permissions on a user and then removes them) - var explicitlyDefinedEntityIds = result - .Where(IsNotNullActionPermission) - .Select(x => x.EntityId) - .ToArray(); + // TODO: rework to groups first, then user type // If no permissions are assigned to a particular node then, we will fill in those permissions with the user's defaults var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); @@ -956,7 +947,6 @@ namespace Umbraco.Core.Services { result.Add( new EntityPermission( - user.Id, id, user.DefaultPermissions.ToArray())); } @@ -967,12 +957,8 @@ namespace Umbraco.Core.Services var groupPermissions = GetPermissions(group, nodeIds).ToList(); foreach (var groupPermission in groupPermissions) { - // Check if we already have this from explicitly set user permissions, if so, ignore it - if (explicitlyDefinedEntityIds.Contains(groupPermission.EntityId) == false) - { - // Add group permission, ensuring we keep a unique value for the entity Id in the list - AddOrAmendPermission(result, groupPermission); - } + // Add group permission, ensuring we keep a unique value for the entity Id in the list + AddOrAmendPermission(result, groupPermission); } } diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index afb679afa1..a1f480a1f5 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -10,21 +10,21 @@ namespace Umbraco.Core.Services /// Remove all permissions for this user for all nodes specified /// /// - /// + /// /// - public static void RemoveUserPermissions(this IUserService userService, int userId, params int[] entityIds) + public static void RemoveUserGroupPermissions(this IUserService userService, int groupId, params int[] entityIds) { - userService.ReplaceUserPermissions(userId, new char[] {}, entityIds); + userService.ReplaceUserGroupPermissions(groupId, new char[] {}, entityIds); } /// /// Remove all permissions for this user for all nodes /// /// - /// - public static void RemoveUserPermissions(this IUserService userService, int userId) + /// + public static void RemoveUserGroupPermissions(this IUserService userService, int groupId) { - userService.ReplaceUserPermissions(userId, new char[] { }); + userService.ReplaceUserGroupPermissions(groupId, new char[] { }); } /// @@ -62,7 +62,6 @@ namespace Umbraco.Core.Services member.ProviderUserKey == null ? member.UserName : member.ProviderUserKey.ToString(), Guid.NewGuid().ToString("N"), //pass cannot be empty writer); - user.AddAllowedSection(Constants.Applications.Content); userService.Save(user); return user; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 555c76eb4c..07256fda05 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -385,6 +385,7 @@ + @@ -787,7 +788,6 @@ - @@ -1034,7 +1034,6 @@ - @@ -1205,9 +1204,7 @@ - - diff --git a/src/Umbraco.Tests/Models/UserTests.cs b/src/Umbraco.Tests/Models/UserTests.cs index 189a4a17a8..9f974ee9fa 100644 --- a/src/Umbraco.Tests/Models/UserTests.cs +++ b/src/Umbraco.Tests/Models/UserTests.cs @@ -41,8 +41,6 @@ namespace Umbraco.Tests.Models Username = "username" }; - item.AddAllowedSection("test"); - var clone = (User)item.DeepClone(); Assert.AreNotSame(clone, item); @@ -61,17 +59,6 @@ namespace Umbraco.Tests.Models { Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); } - - //ensure internal collections are differet - Assert.AreNotSame(item.AddedSections, clone.AddedSections); - Assert.AreNotSame(item.RemovedSections, clone.RemovedSections); - - //ensure event handlers are still wired on clone - clone.AddAllowedSection("blah"); - Assert.AreEqual(1, clone.AddedSections.Count()); - clone.RemoveAllowedSection("blah"); - Assert.AreEqual(1, clone.RemovedSections.Count()); - } [Test] diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs new file mode 100644 index 0000000000..a29aeb8c29 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs @@ -0,0 +1,425 @@ +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; + +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + [TestFixture] + public class UserGroupRepositoryTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + private UserGroupRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + { + return new UserGroupRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); + } + + [Test] + public void Can_Perform_Add_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + + var userType = MockedUserGroup.CreateUserGroup(); + + // Act + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Assert + Assert.That(userType.HasIdentity, Is.True); + } + } + + [Test] + public void Can_Perform_Multiple_Adds_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + + var userType1 = MockedUserGroup.CreateUserGroup("1"); + var userType2 = MockedUserGroup.CreateUserGroup("2"); + + // Act + repository.AddOrUpdate(userType1); + unitOfWork.Commit(); + repository.AddOrUpdate(userType2); + unitOfWork.Commit(); + + // Assert + Assert.That(userType1.HasIdentity, Is.True); + Assert.That(userType2.HasIdentity, Is.True); + } + } + + [Test] + public void Can_Verify_Fresh_Entity_Is_Not_Dirty() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var userType = MockedUserGroup.CreateUserGroup(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get(userType.Id); + bool dirty = ((UserGroup) resolved).IsDirty(); + + // Assert + Assert.That(dirty, Is.False); + } + } + + [Test] + public void Can_Perform_Update_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var userType = MockedUserGroup.CreateUserGroup(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get(userType.Id); + resolved.Name = "New Name"; + resolved.Permissions = new[]{"Z", "Y", "X"}; + repository.AddOrUpdate(resolved); + unitOfWork.Commit(); + var updatedItem = repository.Get(userType.Id); + + // Assert + Assert.That(updatedItem.Id, Is.EqualTo(resolved.Id)); + Assert.That(updatedItem.Name, Is.EqualTo(resolved.Name)); + Assert.That(updatedItem.Permissions, Is.EqualTo(resolved.Permissions)); + } + } + + [Test] + public void Can_Perform_Delete_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + + var userType = MockedUserGroup.CreateUserGroup(); + + // Act + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + var id = userType.Id; + + using (var repository2 = new UserGroupRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, SqlSyntax)) + { + repository2.Delete(userType); + unitOfWork.Commit(); + + var resolved = repository2.Get(id); + + // Assert + Assert.That(resolved, Is.Null); + } + + } + } + + [Test] + public void Can_Perform_Get_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var userType = MockedUserGroup.CreateUserGroup(); + repository.AddOrUpdate(userType); + unitOfWork.Commit(); + + // Act + var resolved = repository.Get(userType.Id); + + // Assert + Assert.That(resolved.Id, Is.EqualTo(userType.Id)); + //Assert.That(resolved.CreateDate, Is.GreaterThan(DateTime.MinValue)); + //Assert.That(resolved.UpdateDate, Is.GreaterThan(DateTime.MinValue)); + Assert.That(resolved.Name, Is.EqualTo(userType.Name)); + Assert.That(resolved.Alias, Is.EqualTo(userType.Alias)); + Assert.That(resolved.Permissions, Is.EqualTo(userType.Permissions)); + } + } + + [Test] + public void Can_Perform_GetByQuery_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + var query = Query.Builder.Where(x => x.Alias == "testUserGroup1"); + var result = repository.GetByQuery(query); + + // Assert + Assert.That(result.Count(), Is.GreaterThanOrEqualTo(1)); + } + } + + [Test] + public void Can_Perform_GetAll_By_Param_Ids_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var userTypes = CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + var result = repository.GetAll(userTypes[0].Id, userTypes[1].Id); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Any(), Is.True); + Assert.That(result.Count(), Is.EqualTo(2)); + } + } + + [Test] + public void Can_Perform_GetAll_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + var result = repository.GetAll(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Any(), Is.True); + Assert.That(result.Count(), Is.GreaterThanOrEqualTo(3)); + } + } + + [Test] + public void Can_Perform_Exists_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var userTypes = CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + var exists = repository.Exists(userTypes[0].Id); + + // Assert + Assert.That(exists, Is.True); + } + } + + [Test] + public void Can_Perform_Count_On_UserGroupRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var userTypes = CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + var query = Query.Builder.Where(x => x.Alias == "testUserGroup1" || x.Alias == "testUserGroup2"); + var result = repository.Count(query); + + // Assert + Assert.That(result, Is.GreaterThanOrEqualTo(2)); + } + } + + [Test] + public void Can_Remove_Section_For_Group() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var groups = CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + + //add and remove a few times, this tests the internal collection + groups[0].RemoveAllowedSection("content"); + groups[0].RemoveAllowedSection("content"); + groups[0].AddAllowedSection("content"); + groups[0].RemoveAllowedSection("content"); + + groups[1].RemoveAllowedSection("media"); + groups[1].RemoveAllowedSection("media"); + + repository.AddOrUpdate(groups[0]); + repository.AddOrUpdate(groups[1]); + unitOfWork.Commit(); + + // Assert + var result = repository.GetAll((int)groups[0].Id, (int)groups[1].Id).ToArray(); + Assert.AreEqual(1, result[0].AllowedSections.Count()); + Assert.AreEqual("media", result[0].AllowedSections.First()); + Assert.AreEqual(1, result[1].AllowedSections.Count()); + Assert.AreEqual("content", result[1].AllowedSections.First()); + } + } + + [Test] + public void Can_Add_Section_ForGroup() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var groups = CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + + //add and remove a few times, this tests the internal collection + groups[0].AddAllowedSection("settings"); + groups[0].AddAllowedSection("settings"); + groups[0].RemoveAllowedSection("settings"); + groups[0].AddAllowedSection("settings"); + + groups[1].AddAllowedSection("developer"); + + //add the same even though it's already there + groups[2].AddAllowedSection("content"); + + repository.AddOrUpdate(groups[0]); + repository.AddOrUpdate(groups[1]); + unitOfWork.Commit(); + + // Assert + var result = repository.GetAll((int)groups[0].Id, (int)groups[1].Id, (int)groups[2].Id).ToArray(); + Assert.AreEqual(3, result[0].AllowedSections.Count()); + Assert.IsTrue(result[0].AllowedSections.Contains("content")); + Assert.IsTrue(result[0].AllowedSections.Contains("media")); + Assert.IsTrue(result[0].AllowedSections.Contains("settings")); + Assert.AreEqual(3, result[1].AllowedSections.Count()); + Assert.IsTrue(result[1].AllowedSections.Contains("content")); + Assert.IsTrue(result[1].AllowedSections.Contains("media")); + Assert.IsTrue(result[1].AllowedSections.Contains("developer")); + Assert.AreEqual(2, result[2].AllowedSections.Count()); + Assert.IsTrue(result[1].AllowedSections.Contains("content")); + Assert.IsTrue(result[1].AllowedSections.Contains("media")); + } + } + + [Test] + public void Can_Update_Section_For_Group() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var groups = CreateAndCommitMultipleUserGroups(repository, unitOfWork); + + // Act + + groups[0].RemoveAllowedSection("content"); + groups[0].AddAllowedSection("settings"); + + repository.AddOrUpdate(groups[0]); + unitOfWork.Commit(); + + // Assert + var result = repository.Get((int)groups[0].Id); + Assert.AreEqual(2, result.AllowedSections.Count()); + Assert.IsTrue(result.AllowedSections.Contains("settings")); + Assert.IsTrue(result.AllowedSections.Contains("media")); + } + } + + + [Test] + public void Get_Groups_Assigned_To_Section() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var user1 = MockedUserGroup.CreateUserGroup("1", new[] { "media" }); + var user2 = MockedUserGroup.CreateUserGroup("2", new[] { "settings" }); + var user3 = MockedUserGroup.CreateUserGroup("3", new[] { "settings" }); + repository.AddOrUpdate(user1); + repository.AddOrUpdate(user2); + repository.AddOrUpdate(user3); + unitOfWork.Commit(); + + // Act + + var groups = repository.GetGroupsAssignedToSection("test"); + + // Assert + Assert.AreEqual(2, groups.Count()); + var names = groups.Select(x => x.Name).ToArray(); + Assert.IsTrue(names.Contains("TestGroup1")); + Assert.IsTrue(names.Contains("TestGroup3")); + } + } + + private IUserGroup[] CreateAndCommitMultipleUserGroups(IUserGroupRepository repository, IUnitOfWork unitOfWork) + { + var userGroup1 = MockedUserGroup.CreateUserGroup("1"); + var userGroup2 = MockedUserGroup.CreateUserGroup("2"); + var userGroup3 = MockedUserGroup.CreateUserGroup("3"); + repository.AddOrUpdate(userGroup1); + repository.AddOrUpdate(userGroup2); + repository.AddOrUpdate(userGroup3); + unitOfWork.Commit(); + return new IUserGroup[] { userGroup1, userGroup2, userGroup3 }; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 1d26e59113..2a92cb39c8 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -135,7 +135,6 @@ namespace Umbraco.Tests.Persistence.Repositories resolved.StartMediaId = 11; resolved.Email = "new@new.com"; resolved.Username = "newName"; - resolved.RemoveAllowedSection("content"); repository.AddOrUpdate(resolved); unitOfWork.Commit(); @@ -341,142 +340,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Test] - public void Can_Remove_Section_For_User() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(Logger); - var unitOfWork = provider.GetUnitOfWork(); - UserTypeRepository userTypeRepository; - using (var repository = CreateRepository(unitOfWork, out userTypeRepository)) - { - var users = CreateAndCommitMultipleUsers(repository, unitOfWork); - - // Act - - //add and remove a few times, this tests the internal collection - users[0].RemoveAllowedSection("content"); - users[0].RemoveAllowedSection("content"); - users[0].AddAllowedSection("content"); - users[0].RemoveAllowedSection("content"); - - users[1].RemoveAllowedSection("media"); - users[1].RemoveAllowedSection("media"); - - repository.AddOrUpdate(users[0]); - repository.AddOrUpdate(users[1]); - unitOfWork.Commit(); - - // Assert - var result = repository.GetAll((int) users[0].Id, (int) users[1].Id).ToArray(); - Assert.AreEqual(1, result[0].AllowedSections.Count()); - Assert.AreEqual("media", result[0].AllowedSections.First()); - Assert.AreEqual(1, result[1].AllowedSections.Count()); - Assert.AreEqual("content", result[1].AllowedSections.First()); - } - } - - [Test] - public void Can_Add_Section_For_User() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(Logger); - var unitOfWork = provider.GetUnitOfWork(); - UserTypeRepository userTypeRepository; - using (var repository = CreateRepository(unitOfWork, out userTypeRepository)) - { - var users = CreateAndCommitMultipleUsers(repository, unitOfWork); - - // Act - - //add and remove a few times, this tests the internal collection - users[0].AddAllowedSection("settings"); - users[0].AddAllowedSection("settings"); - users[0].RemoveAllowedSection("settings"); - users[0].AddAllowedSection("settings"); - - users[1].AddAllowedSection("developer"); - - //add the same even though it's already there - users[2].AddAllowedSection("content"); - - repository.AddOrUpdate(users[0]); - repository.AddOrUpdate(users[1]); - unitOfWork.Commit(); - - // Assert - var result = repository.GetAll((int) users[0].Id, (int) users[1].Id, (int) users[2].Id).ToArray(); - Assert.AreEqual(3, result[0].AllowedSections.Count()); - Assert.IsTrue(result[0].AllowedSections.Contains("content")); - Assert.IsTrue(result[0].AllowedSections.Contains("media")); - Assert.IsTrue(result[0].AllowedSections.Contains("settings")); - Assert.AreEqual(3, result[1].AllowedSections.Count()); - Assert.IsTrue(result[1].AllowedSections.Contains("content")); - Assert.IsTrue(result[1].AllowedSections.Contains("media")); - Assert.IsTrue(result[1].AllowedSections.Contains("developer")); - Assert.AreEqual(2, result[2].AllowedSections.Count()); - Assert.IsTrue(result[1].AllowedSections.Contains("content")); - Assert.IsTrue(result[1].AllowedSections.Contains("media")); - } - } - - [Test] - public void Can_Update_Section_For_User() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(Logger); - var unitOfWork = provider.GetUnitOfWork(); - UserTypeRepository userTypeRepository; - using (var repository = CreateRepository(unitOfWork, out userTypeRepository)) - { - var users = CreateAndCommitMultipleUsers(repository, unitOfWork); - - // Act - - users[0].RemoveAllowedSection("content"); - users[0].AddAllowedSection("settings"); - - repository.AddOrUpdate(users[0]); - unitOfWork.Commit(); - - // Assert - var result = repository.Get((int) users[0].Id); - Assert.AreEqual(2, result.AllowedSections.Count()); - Assert.IsTrue(result.AllowedSections.Contains("settings")); - Assert.IsTrue(result.AllowedSections.Contains("media")); - } - } - - - [Test] - public void Get_Users_Assigned_To_Section() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(Logger); - var unitOfWork = provider.GetUnitOfWork(); - UserTypeRepository userTypeRepository; - using (var repository = CreateRepository(unitOfWork, out userTypeRepository)) - { - var user1 = MockedUser.CreateUser(CreateAndCommitUserType(), "1", "test", "media"); - var user2 = MockedUser.CreateUser(CreateAndCommitUserType(), "2", "media", "settings"); - var user3 = MockedUser.CreateUser(CreateAndCommitUserType(), "3", "test", "settings"); - repository.AddOrUpdate(user1); - repository.AddOrUpdate(user2); - repository.AddOrUpdate(user3); - unitOfWork.Commit(); - - // Act - - var users = repository.GetUsersAssignedToSection("test"); - - // Assert - Assert.AreEqual(2, users.Count()); - var names = users.Select(x => x.Username).ToArray(); - Assert.IsTrue(names.Contains("TestUser1")); - Assert.IsTrue(names.Contains("TestUser3")); - } - } - [Test] public void Default_User_Permissions_Based_On_User_Type() { @@ -488,7 +351,7 @@ namespace Umbraco.Tests.Persistence.Repositories { // Act - var user1 = MockedUser.CreateUser(CreateAndCommitUserType(), "1", "test", "media"); + var user1 = MockedUser.CreateUser(CreateAndCommitUserType(), "1"); repository.AddOrUpdate(user1); unitOfWork.Commit(); diff --git a/src/Umbraco.Tests/Services/SectionServiceTests.cs b/src/Umbraco.Tests/Services/SectionServiceTests.cs index e574d749f4..3bfba08e76 100644 --- a/src/Umbraco.Tests/Services/SectionServiceTests.cs +++ b/src/Umbraco.Tests/Services/SectionServiceTests.cs @@ -71,8 +71,6 @@ namespace Umbraco.Tests.Services Username = "testUser", Email = "testuser@test.com", }; - user.AddAllowedSection("content"); - user.AddAllowedSection("media"); ServiceContext.UserService.Save(user, false); if (withGroups) diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index c7002ce79d..35013ca69b 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -185,7 +185,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10); + var users = MockedUser.CreateMulipleUsers(userType, 10); ServiceContext.UserService.Save(users); //don't find this var customUser = MockedUser.CreateUser(userType); @@ -203,7 +203,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10); + var users = MockedUser.CreateMulipleUsers(userType, 10); ServiceContext.UserService.Save(users); //include this var customUser = MockedUser.CreateUser(userType); @@ -221,7 +221,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10); + var users = MockedUser.CreateMulipleUsers(userType, 10); ServiceContext.UserService.Save(users); //include this var customUser = MockedUser.CreateUser(userType); @@ -239,7 +239,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10); + var users = MockedUser.CreateMulipleUsers(userType, 10); ServiceContext.UserService.Save(users); //include this var customUser = MockedUser.CreateUser(userType); @@ -257,7 +257,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10); + var users = MockedUser.CreateMulipleUsers(userType, 10); ServiceContext.UserService.Save(users); int totalRecs; @@ -275,7 +275,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10); + var users = MockedUser.CreateMulipleUsers(userType, 10); ServiceContext.UserService.Save(users); var customUser = MockedUser.CreateUser(userType); ServiceContext.UserService.Save(customUser); @@ -292,7 +292,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10, (i, member) => member.LastLoginDate = DateTime.Now.AddMinutes(i * -2)); + var users = MockedUser.CreateMulipleUsers(userType, 10, (i, member) => member.LastLoginDate = DateTime.Now.AddMinutes(i * -2)); ServiceContext.UserService.Save(users); var customUser = MockedUser.CreateUser(userType); @@ -304,7 +304,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10, (i, member) => member.IsLockedOut = i % 2 == 0); + var users = MockedUser.CreateMulipleUsers(userType, 10, (i, member) => member.IsLockedOut = i % 2 == 0); ServiceContext.UserService.Save(users); var customUser = MockedUser.CreateUser(userType); @@ -321,7 +321,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var users = MockedUser.CreateUser(userType, 10, (i, member) => member.IsApproved = i % 2 == 0); + var users = MockedUser.CreateMulipleUsers(userType, 10, (i, member) => member.IsApproved = i % 2 == 0); ServiceContext.UserService.Save(users); var customUser = MockedUser.CreateUser(userType); @@ -392,102 +392,124 @@ namespace Umbraco.Tests.Services } [Test] - public void Can_Add_And_Remove_Sections_From_User() + public void Can_Add_And_Remove_Sections_From_UserGroup() { - var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); + var userGroup = new UserGroup + { + Id = 1, + Alias = "Group1", + }; + userGroup.AddAllowedSection("content"); + userGroup.AddAllowedSection("mediat"); + ServiceContext.UserService.SaveUserGroup(userGroup); - var user1 = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com", userType); + var result1 = ServiceContext.UserService.GetUserGroupById(userGroup.Id); - var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); - //expect 2 sections by default Assert.AreEqual(2, result1.AllowedSections.Count()); //adds some allowed sections - user1.AddAllowedSection("test1"); - user1.AddAllowedSection("test2"); - user1.AddAllowedSection("test3"); - user1.AddAllowedSection("test4"); - ServiceContext.UserService.Save(user1); + userGroup.AddAllowedSection("test1"); + userGroup.AddAllowedSection("test2"); + userGroup.AddAllowedSection("test3"); + userGroup.AddAllowedSection("test4"); + ServiceContext.UserService.SaveUserGroup(userGroup); + + result1 = ServiceContext.UserService.GetUserGroupById(userGroup.Id); - result1 = ServiceContext.UserService.GetUserById((int)user1.Id); - //expect 6 sections including the two default sections Assert.AreEqual(6, result1.AllowedSections.Count()); //simulate clearing the sections - foreach (var s in user1.AllowedSections) + foreach (var s in userGroup.AllowedSections) { result1.RemoveAllowedSection(s); } + //now just re-add a couple result1.AddAllowedSection("test3"); result1.AddAllowedSection("test4"); - ServiceContext.UserService.Save(result1); + ServiceContext.UserService.SaveUserGroup(result1); //assert //re-get - result1 = ServiceContext.UserService.GetUserById((int)user1.Id); + result1 = ServiceContext.UserService.GetUserGroupById(userGroup.Id); Assert.AreEqual(2, result1.AllowedSections.Count()); } [Test] - public void Can_Remove_Section_From_All_Assigned_Users() - { - var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); + public void Can_Remove_Section_From_All_Assigned_UserGroups() + { + var userGroup1 = new UserGroup + { + Id = 1, + Alias = "Group1", + }; + var userGroup2 = new UserGroup + { + Id = 2, + Alias = "Group2", + }; + ServiceContext.UserService.SaveUserGroup(userGroup1); + ServiceContext.UserService.SaveUserGroup(userGroup2); - var user1 = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com", userType); - var user2 = ServiceContext.UserService.CreateUserWithIdentity("test2", "test2@test.com", userType); - //adds some allowed sections - user1.AddAllowedSection("test"); - user2.AddAllowedSection("test"); - ServiceContext.UserService.Save(user1); - ServiceContext.UserService.Save(user2); + userGroup1.AddAllowedSection("test"); + userGroup2.AddAllowedSection("test"); + ServiceContext.UserService.SaveUserGroup(userGroup1); + ServiceContext.UserService.SaveUserGroup(userGroup2); //now clear the section from all users - ServiceContext.UserService.DeleteSectionFromAllUsers("test"); + ServiceContext.UserService.DeleteSectionFromAllUserGroups("test"); //assert - var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); - var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); + var result1 = ServiceContext.UserService.GetUserGroupById(userGroup1.Id); + var result2 = ServiceContext.UserService.GetUserGroupById(userGroup2.Id); Assert.IsFalse(result1.AllowedSections.Contains("test")); Assert.IsFalse(result2.AllowedSections.Contains("test")); } [Test] - public void Can_Add_Section_To_All_Users() + public void Can_Add_Section_To_All_UserGroups() { - var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); + var userGroup1 = new UserGroup + { + Id = 1, + Alias = "Group1", + }; + var userGroup2 = new UserGroup + { + Id = 2, + Alias = "Group2", + }; + var userGroup3 = new UserGroup + { + Id = 2, + Alias = "Group3", + }; + ServiceContext.UserService.SaveUserGroup(userGroup1); + ServiceContext.UserService.SaveUserGroup(userGroup2); + ServiceContext.UserService.SaveUserGroup(userGroup3); - var user1 = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com", userType); - var user2 = ServiceContext.UserService.CreateUserWithIdentity("test2", "test2@test.com", userType); - var user3 = ServiceContext.UserService.CreateUserWithIdentity("test3", "test3@test.com", userType); - var user4 = ServiceContext.UserService.CreateUserWithIdentity("test4", "test4@test.com", userType); - - //now add the section to specific users - ServiceContext.UserService.AddSectionToAllUsers("test", (int)user1.Id, (int)user2.Id); + //now add the section to specific groups + ServiceContext.UserService.AddSectionToAllUserGroups("test", userGroup1.Id, userGroup2.Id); //assert - var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); - var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); - var result3 = ServiceContext.UserService.GetUserById((int)user3.Id); - var result4 = ServiceContext.UserService.GetUserById((int)user4.Id); + var result1 = ServiceContext.UserService.GetUserGroupById(userGroup1.Id); + var result2 = ServiceContext.UserService.GetUserGroupById(userGroup2.Id); + var result3 = ServiceContext.UserService.GetUserGroupById(userGroup3.Id); Assert.IsTrue(result1.AllowedSections.Contains("test")); Assert.IsTrue(result2.AllowedSections.Contains("test")); Assert.IsFalse(result3.AllowedSections.Contains("test")); - Assert.IsFalse(result4.AllowedSections.Contains("test")); - //now add the section to all users - ServiceContext.UserService.AddSectionToAllUsers("test"); + //now add the section to all groups + ServiceContext.UserService.AddSectionToAllUserGroups("test"); //assert - result1 = ServiceContext.UserService.GetUserById((int)user1.Id); - result2 = ServiceContext.UserService.GetUserById((int)user2.Id); - result3 = ServiceContext.UserService.GetUserById((int)user3.Id); - result4 = ServiceContext.UserService.GetUserById((int)user4.Id); + result1 = ServiceContext.UserService.GetUserGroupById(userGroup1.Id); + result2 = ServiceContext.UserService.GetUserGroupById(userGroup2.Id); + result3 = ServiceContext.UserService.GetUserGroupById(userGroup3.Id); Assert.IsTrue(result1.AllowedSections.Contains("test")); Assert.IsTrue(result2.AllowedSections.Contains("test")); Assert.IsTrue(result3.AllowedSections.Contains("test")); - Assert.IsTrue(result4.AllowedSections.Contains("test")); } [Test] diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs index 15d1ab76fd..3ccdd70966 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs @@ -8,7 +8,7 @@ namespace Umbraco.Tests.TestHelpers.Entities { public class MockedUser { - internal static User CreateUser(IUserType userType = null, string suffix = "", params string[] allowedSections) + internal static User CreateUser(IUserType userType = null, string suffix = "") { if (userType == null) { @@ -29,23 +29,10 @@ namespace Umbraco.Tests.TestHelpers.Entities Username = "TestUser" + suffix }; - if (allowedSections.Any()) - { - foreach (var s in allowedSections) - { - user.AddAllowedSection(s); - } - } - else - { - user.AddAllowedSection("content"); - user.AddAllowedSection("media"); - } - return user; } - internal static IEnumerable CreateUser(IUserType userType, int amount, Action onCreating = null) + internal static IEnumerable CreateMulipleUsers(IUserType userType, int amount, Action onCreating = null) { var list = new List(); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs new file mode 100644 index 0000000000..f266f55332 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs @@ -0,0 +1,17 @@ +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedUserGroup + { + internal static UserGroup CreateUserGroup(string suffix = "", string[] permissions = null) + { + return new UserGroup() + { + Alias = "testUserGroup" + suffix, + Name = "TestUserGroup" + suffix, + Permissions = permissions ?? new[]{"A", "B", "C"} + }; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TreesAndSections/SectionTests.cs b/src/Umbraco.Tests/TreesAndSections/SectionTests.cs index 67ef50af38..1edd10aec7 100644 --- a/src/Umbraco.Tests/TreesAndSections/SectionTests.cs +++ b/src/Umbraco.Tests/TreesAndSections/SectionTests.cs @@ -70,64 +70,6 @@ namespace Umbraco.Tests.TreesAndSections Assert.IsNull(ApplicationContext.Current.Services.SectionService.GetByAlias(name)); } - /// - /// Creates a new user, assigns the user to existing application, - /// then deletes the user - /// - [Test()] - public void Application_Create_New_User_Assign_Application_And_Delete_User() - { - var name = Guid.NewGuid().ToString("N"); - - //new user - var ut = UserType.GetAllUserTypes().First(); - var user = User.MakeNew(name, name, name, ut); - - //get application - //var app = Application.getAll().First(); - - //assign the app - user.addApplication(Constants.Applications.Content); - //ensure it's added - Assert.AreEqual(1, user.Applications.Count(x => x.alias == Constants.Applications.Content)); - - //delete the user - user.delete(); - - //make sure the assigned applications are gone - Assert.AreEqual(0, user.Applications.Count(x => x.alias == name)); - } - - /// - /// create a new application and assigne an new user and deletes the application making sure the assignments are removed - /// - [Test()] - public void Application_Make_New_Assign_User_And_Delete() - { - var name = Guid.NewGuid().ToString("N"); - - //new user - var ut = UserType.GetAllUserTypes().First(); - var user = User.MakeNew(name, name, name, ut); - - ApplicationContext.Current.Services.SectionService.MakeNew(name, name, "icon.jpg"); - - //check if it exists - var app = ApplicationContext.Current.Services.SectionService.GetByAlias(name); - Assert.IsNotNull(app); - - //assign the app - user.addApplication(app.Alias); - //ensure it's added - Assert.AreEqual(1, user.Applications.Count(x => x.alias == app.Alias)); - - //delete the app - ApplicationContext.Current.Services.SectionService.DeleteSection(app); - - //make sure the assigned applications are gone - Assert.AreEqual(0, user.Applications.Count(x => x.alias == name)); - } - #region Tests to write diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 89053a2d5a..c0f43220d9 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -179,8 +179,10 @@ + + diff --git a/src/Umbraco.Tests/Web/Controllers/WebApiEditors/ContentControllerUnitTests.cs b/src/Umbraco.Tests/Web/Controllers/WebApiEditors/ContentControllerUnitTests.cs index 4b78df671d..9634bb164a 100644 --- a/src/Umbraco.Tests/Web/Controllers/WebApiEditors/ContentControllerUnitTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/WebApiEditors/ContentControllerUnitTests.cs @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors var userServiceMock = new Mock(); var permissions = new List { - new EntityPermission(9, 1234, new string[]{ "A", "B", "C" }) + new EntityPermission(1234, new string[]{ "A", "B", "C" }) }; userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); var userService = userServiceMock.Object; @@ -129,7 +129,7 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors var userServiceMock = new Mock(); var permissions = new List { - new EntityPermission(9, 1234, new string[]{ "A", "F", "C" }) + new EntityPermission(1234, new string[]{ "A", "F", "C" }) }; userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); var userService = userServiceMock.Object; @@ -217,7 +217,7 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors var userServiceMock = new Mock(); var permissions = new List { - new EntityPermission(9, 1234, new string[]{ "A" }) + new EntityPermission(1234, new string[]{ "A" }) }; userServiceMock.Setup(x => x.GetPermissions(user, -1)).Returns(permissions); var userService = userServiceMock.Object; @@ -241,7 +241,7 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors var userServiceMock = new Mock(); var permissions = new List { - new EntityPermission(9, 1234, new string[]{ "A" }) + new EntityPermission(1234, new string[]{ "A" }) }; userServiceMock.Setup(x => x.GetPermissions(user, -1)).Returns(permissions); var userService = userServiceMock.Object; @@ -265,7 +265,7 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors var userServiceMock = new Mock(); var permissions = new List { - new EntityPermission(9, 1234, new string[]{ "A" }) + new EntityPermission(1234, new string[]{ "A" }) }; userServiceMock.Setup(x => x.GetPermissions(user, -20)).Returns(permissions); var userService = userServiceMock.Object; @@ -289,7 +289,7 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors var userServiceMock = new Mock(); var permissions = new List { - new EntityPermission(9, 1234, new string[]{ "A" }) + new EntityPermission(1234, new string[]{ "A" }) }; userServiceMock.Setup(x => x.GetPermissions(user, -20)).Returns(permissions); var userService = userServiceMock.Object; diff --git a/src/Umbraco.Tests/Web/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs b/src/Umbraco.Tests/Web/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs index 1ebe2fc748..c776924af7 100644 --- a/src/Umbraco.Tests/Web/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs @@ -112,10 +112,10 @@ namespace Umbraco.Tests.Web.Controllers.WebApiEditors //we're only assigning 3 nodes browse permissions so that is what we expect as a result var permissions = new List { - new EntityPermission(9, 1, new string[]{ "F" }), - new EntityPermission(9, 2, new string[]{ "F" }), - new EntityPermission(9, 3, new string[]{ "F" }), - new EntityPermission(9, 4, new string[]{ "A" }) + new EntityPermission(1, new string[]{ "F" }), + new EntityPermission(2, new string[]{ "F" }), + new EntityPermission(3, new string[]{ "F" }), + new EntityPermission(4, new string[]{ "A" }) }; userServiceMock.Setup(x => x.GetPermissions(user, ids)).Returns(permissions); var userService = userServiceMock.Object; diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 30563647f8..cc63766afc 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -27,7 +27,7 @@ - + diff --git a/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx b/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx index 815a420f88..fc9270e370 100644 --- a/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx +++ b/src/Umbraco.Web.UI/umbraco/users/PermissionEditor.aspx @@ -27,7 +27,7 @@ - diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index 638d124658..b5743df158 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -36,9 +36,9 @@ namespace Umbraco.Web.Cache SectionService.Deleted += ApplicationDeleted; SectionService.New += ApplicationNew; - //bind to user / user type events - UserService.SavedUserType += UserServiceSavedUserType; - UserService.DeletedUserType += UserServiceDeletedUserType; + //bind to user / user group events + UserService.SavedUserGroup += UserServiceSavedUserGroup; + UserService.DeletedUserGroup += UserServiceDeletedUserGroup; UserService.SavedUser += UserServiceSavedUser; UserService.DeletedUser += UserServiceDeletedUser; @@ -349,19 +349,6 @@ namespace Umbraco.Web.Cache } #endregion - #region UserType event handlers - static void UserServiceDeletedUserType(IUserService sender, DeleteEventArgs e) - { - e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserTypeCache(x.Id)); - } - - static void UserServiceSavedUserType(IUserService sender, SaveEventArgs e) - { - e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserTypeCache(x.Id)); - } - - #endregion - #region Dictionary event handlers static void LocalizationServiceSavedDictionaryItem(ILocalizationService sender, SaveEventArgs e) @@ -537,7 +524,17 @@ namespace Umbraco.Web.Cache { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserCache(x.Id)); } - + + static void UserServiceSavedUserGroup(IUserService sender, SaveEventArgs e) + { + e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserGroupCache(x.Id)); + } + + static void UserServiceDeletedUserGroup(IUserService sender, DeleteEventArgs e) + { + e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserGroupCache(x.Id)); + } + private static void InvalidateCacheForPermissionsChange(UserGroupPermission sender) { if (sender.UserGroup != null) diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs index 75715565ac..6956df2374 100644 --- a/src/Umbraco.Web/Cache/DistributedCache.cs +++ b/src/Umbraco.Web/Cache/DistributedCache.cs @@ -35,7 +35,6 @@ namespace Umbraco.Web.Cache public const string UserCacheRefresherId = "E057AF6D-2EE6-41F4-8045-3694010F0AA6"; public const string UserGroupCacheRefresherId = "45178038-B232-4FE8-AA1A-F2B949C44762"; public const string UserGroupPermissionsCacheRefresherId = "840AB9C5-5C0B-48DB-A77E-29FE4B80CD3A"; - public const string UserTypeCacheRefresherId = "7E707E21-0195-4522-9A3C-658CC1761BD4"; public const string ContentTypeCacheRefresherId = "6902E22C-9C10-483C-91F3-66B7CAE9E2F5"; public const string LanguageCacheRefresherId = "3E0F95D8-0BE5-44B8-8394-2B8750B62654"; public const string DomainCacheRefresherId = "11290A79-4B57-4C99-AD72-7748A3CF38AF"; @@ -58,8 +57,8 @@ namespace Umbraco.Web.Cache public static readonly Guid MediaCacheRefresherGuid = new Guid(MediaCacheRefresherId); public static readonly Guid MacroCacheRefresherGuid = new Guid(MacroCacheRefresherId); public static readonly Guid UserCacheRefresherGuid = new Guid(UserCacheRefresherId); + public static readonly Guid UserGroupCacheRefresherGuid = new Guid(UserGroupCacheRefresherId); public static readonly Guid UserGroupPermissionsCacheRefresherGuid = new Guid(UserGroupPermissionsCacheRefresherId); - public static readonly Guid UserTypeCacheRefresherGuid = new Guid(UserTypeCacheRefresherId); public static readonly Guid ContentTypeCacheRefresherGuid = new Guid(ContentTypeCacheRefresherId); public static readonly Guid LanguageCacheRefresherGuid = new Guid(LanguageCacheRefresherId); public static readonly Guid DomainCacheRefresherGuid = new Guid(DomainCacheRefresherId); diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index f7abb7494d..1050517c5a 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -41,25 +41,6 @@ namespace Umbraco.Web.Cache #endregion - #region User type cache - - public static void RemoveUserTypeCache(this DistributedCache dc, int userTypeId) - { - dc.Remove(DistributedCache.UserTypeCacheRefresherGuid, userTypeId); - } - - public static void RefreshUserTypeCache(this DistributedCache dc, int userTypeId) - { - dc.Refresh(DistributedCache.UserTypeCacheRefresherGuid, userTypeId); - } - - public static void RefreshAllUserTypeCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.UserTypeCacheRefresherGuid); - } - - #endregion - #region User cache public static void RemoveUserCache(this DistributedCache dc, int userId) @@ -75,7 +56,26 @@ namespace Umbraco.Web.Cache public static void RefreshAllUserCache(this DistributedCache dc) { dc.RefreshAll(DistributedCache.UserCacheRefresherGuid); - } + } + + #endregion + + #region User group cache + + public static void RemoveUserGroupCache(this DistributedCache dc, int userId) + { + dc.Remove(DistributedCache.UserGroupCacheRefresherGuid, userId); + } + + public static void RefreshUserGroupCache(this DistributedCache dc, int userId) + { + dc.Refresh(DistributedCache.UserGroupCacheRefresherGuid, userId); + } + + public static void RefreshAllUserGroupCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.UserGroupCacheRefresherGuid); + } #endregion diff --git a/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs deleted file mode 100644 index ca3f93c068..0000000000 --- a/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Models.Membership; - -using Umbraco.Core.Persistence.Repositories; - -namespace Umbraco.Web.Cache -{ - /// - /// Handles User type cache invalidation/refreshing - /// - public sealed class UserTypeCacheRefresher : CacheRefresherBase - { - protected override UserTypeCacheRefresher Instance - { - get { return this; } - } - - public override Guid UniqueIdentifier - { - get { return Guid.Parse(DistributedCache.UserTypeCacheRefresherId); } - } - - public override string Name - { - get { return "User type cache refresher"; } - } - - public override void RefreshAll() - { - ClearAllIsolatedCacheByEntityType(); - base.RefreshAll(); - } - - public override void Refresh(int id) - { - var userTypeCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); - if (userTypeCache) - userTypeCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); - base.Refresh(id); - } - - public override void Remove(int id) - { - var userTypeCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); - if (userTypeCache) - userTypeCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); - base.Remove(id); - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 199ffd9bed..bc596a8a0a 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -604,60 +604,49 @@ namespace Umbraco.Web.Editors } else { - var defaultUserType = autoLinkOptions.GetDefaultUserType(UmbracoContext, loginInfo); - var userType = Services.UserService.GetUserTypeByAlias(defaultUserType); - if (userType == null) + if (loginInfo.Email.IsNullOrWhiteSpace()) throw new InvalidOperationException("The Email value cannot be null"); + if (loginInfo.ExternalIdentity.Name.IsNullOrWhiteSpace()) throw new InvalidOperationException("The Name value cannot be null"); + + var autoLinkUser = new BackOfficeIdentityUser { - ViewData[TokenExternalSignInError] = new[] { "Could not auto-link this account, the specified User Type does not exist: " + defaultUserType }; + Email = loginInfo.Email, + Name = loginInfo.ExternalIdentity.Name, + AllowedSections = autoLinkOptions.GetDefaultAllowedSections(UmbracoContext, loginInfo), + Culture = autoLinkOptions.GetDefaultCulture(UmbracoContext, loginInfo), + UserName = loginInfo.Email + }; + + //call the callback if one is assigned + if (autoLinkOptions.OnAutoLinking != null) + { + autoLinkOptions.OnAutoLinking(autoLinkUser, loginInfo); + } + + var userCreationResult = await UserManager.CreateAsync(autoLinkUser); + + if (userCreationResult.Succeeded == false) + { + ViewData[TokenExternalSignInError] = userCreationResult.Errors; } else { - - if (loginInfo.Email.IsNullOrWhiteSpace()) throw new InvalidOperationException("The Email value cannot be null"); - if (loginInfo.ExternalIdentity.Name.IsNullOrWhiteSpace()) throw new InvalidOperationException("The Name value cannot be null"); - - var autoLinkUser = new BackOfficeIdentityUser() + var linkResult = await UserManager.AddLoginAsync(autoLinkUser.Id, loginInfo.Login); + if (linkResult.Succeeded == false) { - Email = loginInfo.Email, - Name = loginInfo.ExternalIdentity.Name, - UserTypeAlias = userType.Alias, - AllowedSections = autoLinkOptions.GetDefaultAllowedSections(UmbracoContext, loginInfo), - Culture = autoLinkOptions.GetDefaultCulture(UmbracoContext, loginInfo), - UserName = loginInfo.Email - }; + ViewData[TokenExternalSignInError] = linkResult.Errors; - //call the callback if one is assigned - if (autoLinkOptions.OnAutoLinking != null) - { - autoLinkOptions.OnAutoLinking(autoLinkUser, loginInfo); - } - - var userCreationResult = await UserManager.CreateAsync(autoLinkUser); - - if (userCreationResult.Succeeded == false) - { - ViewData[TokenExternalSignInError] = userCreationResult.Errors; + //If this fails, we should really delete the user since it will be in an inconsistent state! + var deleteResult = await UserManager.DeleteAsync(autoLinkUser); + if (deleteResult.Succeeded == false) + { + //DOH! ... this isn't good, combine all errors to be shown + ViewData[TokenExternalSignInError] = linkResult.Errors.Concat(deleteResult.Errors); + } } else { - var linkResult = await UserManager.AddLoginAsync(autoLinkUser.Id, loginInfo.Login); - if (linkResult.Succeeded == false) - { - ViewData[TokenExternalSignInError] = linkResult.Errors; - - //If this fails, we should really delete the user since it will be in an inconsistent state! - var deleteResult = await UserManager.DeleteAsync(autoLinkUser); - if (deleteResult.Succeeded == false) - { - //DOH! ... this isn't good, combine all errors to be shown - ViewData[TokenExternalSignInError] = linkResult.Errors.Concat(deleteResult.Errors); - } - } - else - { - //sign in - await SignInManager.SignInAsync(autoLinkUser, isPersistent: false, rememberBrowser: false); - } + //sign in + await SignInManager.SignInAsync(autoLinkUser, isPersistent: false, rememberBrowser: false); } } } diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index d668399e83..e4c94965b7 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -37,13 +37,12 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.InternalServerError); var user = Security.CurrentUser; - var userType = user.UserType.Alias; var allowedSections = string.Join(",", user.AllowedSections); var language = user.Language; var version = UmbracoVersion.GetSemanticVersion().ToSemanticString(); - var url = string.Format(baseUrl + "{0}?section={0}&type={1}&allowed={2}&lang={3}&version={4}", section, userType, allowedSections, language, version); - var key = "umbraco-dynamic-dashboard-" + userType + language + allowedSections.Replace(",", "-") + section; + var url = string.Format(baseUrl + "{0}?section={0}&allowed={1}&lang={2}&version={3}", section, allowedSections, language, version); + var key = "umbraco-dynamic-dashboard-" + language + allowedSections.Replace(",", "-") + section; var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem(key); var result = new JObject(); diff --git a/src/Umbraco.Web/Editors/DashboardSecurity.cs b/src/Umbraco.Web/Editors/DashboardSecurity.cs index a8f5e44ef2..879fd468e0 100644 --- a/src/Umbraco.Web/Editors/DashboardSecurity.cs +++ b/src/Umbraco.Web/Editors/DashboardSecurity.cs @@ -76,33 +76,6 @@ namespace Umbraco.Web.Editors } } - //Check if this item as any grant arguments, if so check if the user is one of the user types approved, if so they will - // be allowed to see it (so far) - if (grantedTypes.Any()) - { - var allApprovedUserTypes = grantedTypes.SelectMany(g => g.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)).ToArray(); - if (allApprovedUserTypes.InvariantContains(user.UserType.Alias)) - { - allowedSoFar = true; - } - } - else - { - //if there are not explicit grant types then everyone is allowed so far and we'll only disallow on a deny basis - allowedSoFar = true; - } - - //Check if this item as any deny arguments, if so check if the user is one of the user types approved, if so they will - // be denied to see it no matter what - if (denyTypes.Any()) - { - var allDeniedUserTypes = denyTypes.SelectMany(g => g.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)).ToArray(); - if (allDeniedUserTypes.InvariantContains(user.UserType.Alias)) - { - allowedSoFar = false; - } - } - return allowedSoFar; } } diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index 8ccfd6dde7..9fb7a63c9f 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -72,8 +72,7 @@ namespace Umbraco.Web.Editors var userIsAdmin = Umbraco.UmbracoContext.Security.CurrentUser.IsAdmin(); if (userIsAdmin == false) { - var errorMessage = string.Format("User of type {0} is not allowed to toggle the URL tracker", - Umbraco.UmbracoContext.Security.CurrentUser.UserType.Alias); + var errorMessage = "User is not a member of the administrators group and so is not allowed to toggle the URL tracker"; LogHelper.Debug(errorMessage); throw new UserAuthorizationException(errorMessage); } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs index d27736576f..ada24ee38c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs @@ -21,10 +21,6 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "emailHash")] public string EmailHash { get; set; } - [DataMember(Name = "userType", IsRequired = true)] - [Required] - public string UserType { get; set; } - /// /// Gets/sets the number of seconds for the user's auth ticket to expire /// diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs index 72093b19a8..213abc5afc 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models.Mapping; @@ -17,7 +18,6 @@ namespace Umbraco.Web.Models.Mapping { config.CreateMap() .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id))) - .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserType.Alias)) .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId)) .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId)) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService))) @@ -28,7 +28,6 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap() .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => user.Id)) - .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserTypeAlias)) .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId)) .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId)) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture)) @@ -46,7 +45,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) - .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] {user.UserType.Alias})) + .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups.Select(x => x.Name))) .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId)) .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId)) .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index f3b0eaeb5f..3cff7344ad 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -229,9 +229,6 @@ namespace Umbraco.Web.Security //if we already have a user object in Umbraco we don't need to do anything, otherwise we need to create a mapped Umbraco account. if (user != null) return user; - //we need to create an Umbraco IUser of a 'writer' type with access to only content - this was how v6 operates. - var writer = _applicationContext.Services.UserService.GetUserTypeByAlias("writer"); - var email = membershipUser.Email; if (email.IsNullOrWhiteSpace()) { @@ -239,7 +236,7 @@ namespace Umbraco.Web.Security email = Guid.NewGuid().ToString("N") + "@example.com"; } - user = new Core.Models.Membership.User(writer) + user = new Core.Models.Membership.User { Email = email, Language = GlobalSettings.DefaultUILanguage, diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a324e39af4..c2b5665f4c 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -476,7 +476,6 @@ - @@ -1856,18 +1855,10 @@ - XmlTree.xsd - - EditUserType.aspx - ASPXCodeBehind - - - EditUserType.aspx - NodePermissions.ascx ASPXCodeBehind @@ -1886,7 +1877,6 @@ PermissionsHandler.asmx - CacheRefresher.asmx @@ -2151,9 +2141,6 @@ ASPXCodeBehind - - ASPXCodeBehind - Form diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs deleted file mode 100644 index 54dd21738b..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/UserTypes.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using umbraco.BusinessLogic; -using System.Collections.Generic; -using umbraco.businesslogic; -using Umbraco.Core; - -namespace umbraco.cms.presentation.Trees -{ - [Tree(Constants.Applications.Users, "userTypes", "User Types", sortOrder: 1)] - public class UserTypes : BaseTree - { - - public UserTypes(string application) : base(application) { } - - public override void RenderJS(ref System.Text.StringBuilder Javascript) - { - Javascript.Append( - @" -function openUserTypes(id) { - UmbClientMgr.contentFrame('users/EditUserType.aspx?id=' + id); -} -"); - } - - public override void Render(ref XmlTree tree) - { - List userTypes = UserType.GetAllUserTypes(); - foreach (UserType userType in userTypes) - { - if (userType.Id > 1) //don't show the admin user type, they should always have full permissions - { - XmlTreeNode node = XmlTreeNode.Create(this); - node.NodeID = userType.Id.ToString(); - node.Action = string.Format("javascript:openUserTypes({0})", userType.Id.ToString()); - node.Icon = "icon-users"; - node.Text = userType.Name; - - OnBeforeNodeRender(ref tree, ref node, EventArgs.Empty); - if (node != null) - { - tree.Add(node); - OnAfterNodeRender(ref tree, ref node, EventArgs.Empty); - } - } - } - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - rootNode.Text = ui.Text("user", "userTypes"); - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs index 2b775ed797..2b5965d2f3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs @@ -1,10 +1,11 @@ using System.Web.Services; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Web; namespace umbraco.cms.presentation.developer.RelationTypes { - /// + /// /// Webservice to delete relation types, this allows deletion via a javacscript call hooked into the tree UI /// [WebService(Namespace = "http://tempuri.org/")] @@ -22,7 +23,7 @@ namespace umbraco.cms.presentation.developer.RelationTypes { var user = UmbracoContext.Current.Security.CurrentUser; - if (user.UserType.Name == "Administrators") + if (user.IsAdmin()) { var relationService = ApplicationContext.Current.Services.RelationService; var relationType = relationService.GetRelationTypeById(relationTypeId); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs index c02baaaa7b..a310764498 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Data; using System.Configuration; using System.Collections; @@ -70,7 +71,7 @@ namespace umbraco.presentation.dialogs // Translators foreach (var u in BusinessLogic.User.getAll()) - if (u.UserType.Alias.ToLower() == "translator" || UserHasTranslatePermission(u, _currentPage)) + if (u.GetGroups().Select(x => x.ToLower()).Contains("translators") || UserHasTranslatePermission(u, _currentPage)) translator.Items.Add(new ListItem(u.Name, u.Id.ToString())); if (translator.Items.Count == 0) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs index 510a526a0e..83c30ddc25 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -93,19 +93,6 @@ namespace umbraco.cms.presentation.user throw new Exception("Admin users can only be edited by admins"); } - // Populate usertype list - foreach (UserType ut in UserType.getAll) - { - if (CurrentUser.IsAdmin() || ut.Alias != "admin") - { - ListItem li = new ListItem(ui.Text("user", ut.Name.ToLower(), UmbracoUser), ut.Id.ToString()); - if (ut.Id == u.UserType.Id) - li.Selected = true; - - userType.Items.Add(li); - } - } - var userCulture = UserExtensions.GetUserCulture(u.Language, Services.TextService); // Populate ui language lsit @@ -467,7 +454,6 @@ namespace umbraco.cms.presentation.user // ok since the membership provider might be storing these details someplace totally different! But we want to keep our UI in sync. u.Name = uname.Text.Trim(); u.Language = userLanguage.SelectedValue; - u.UserType = UserType.GetUserType(int.Parse(userType.SelectedValue)); u.Email = email.Text.Trim(); u.LoginName = lname.Text; u.Disabled = Disabled.Checked; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx deleted file mode 100644 index d8671f0596..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx +++ /dev/null @@ -1,27 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EditUserType.aspx.cs" MasterPageFile="../masterpages/umbracoPage.Master" - Inherits="umbraco.cms.presentation.user.EditUserType" %> - -<%@ Register TagPrefix="cc2" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs deleted file mode 100644 index b6796e45fa..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Data; -using System.Configuration; -using System.Collections; -using System.Web; -using System.Web.Security; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.WebControls.WebParts; -using System.Web.UI.HtmlControls; -using umbraco.BasePages; -using System.Collections.Generic; -using umbraco.interfaces; -using umbraco.BusinessLogic.Actions; -using umbraco.BusinessLogic; -using umbraco.uicontrols; -using umbraco.cms.presentation.Trees; -using Umbraco.Core.IO; - -namespace umbraco.cms.presentation.user -{ - public partial class EditUserType : UmbracoEnsuredPage - { - public EditUserType() - { - CurrentApp = BusinessLogic.DefaultApps.users.ToString(); - } - protected void Page_Load(object sender, EventArgs e) - { - pnlUmbraco.Text = umbraco.ui.Text("usertype", base.getUser()); - - var save = pnlUmbraco.Menu.NewButton(); - save.Click += save_Click; - save.ID = "save"; - save.ToolTip = ui.Text("save"); - save.Text = ui.Text("save"); - - pp_alias.Text = umbraco.ui.Text("usertype", base.getUser()) + " " + umbraco.ui.Text("alias", base.getUser()); - pp_name.Text = umbraco.ui.Text("usertype", base.getUser()) + " " + umbraco.ui.Text("name", base.getUser()); - - pp_rights.Text = umbraco.ui.Text("default", base.getUser()) + " " + umbraco.ui.Text("rights", base.getUser()); - - //ensure we have a query string - if (string.IsNullOrEmpty(Request.QueryString["id"])) - return; - //ensuer it is an integer - if (!int.TryParse(Request.QueryString["id"], out m_userTypeID)) - return; - - if (!IsPostBack) - { - BindActions(); - - ClientTools - .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias) - .SyncTree(m_userTypeID.ToString(), false); - } - - } - - void save_Click(object sender, EventArgs e) - { - UserType userType = CurrentUserType; - userType.Name = txtUserTypeName.Text; - string actions = ""; - - foreach (ListItem li in cbl_rights.Items) { - if (li.Selected) - actions += li.Value; - } - - userType.DefaultPermissions = actions; - userType.Save(); - - ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserTypeSaved", base.getUser()), ""); - } - - protected List CurrentUserTypeActions - { - get - { - if (m_userTypeActions == null) - m_userTypeActions = umbraco.BusinessLogic.Actions.Action.FromString(CurrentUserType.DefaultPermissions); - return m_userTypeActions; - } - } - - protected UserType CurrentUserType - { - get - { - if (m_userType == null) - m_userType = UserType.GetUserType(m_userTypeID); - return m_userType; - } - } - private UserType m_userType; - private List m_userTypeActions; - private int m_userTypeID; - - private void BindActions() - { - lblUserTypeAlias.Text = CurrentUserType.Alias; - txtUserTypeName.Text = CurrentUserType.Name; - hidUserTypeID.Value = CurrentUserType.Id.ToString(); - - foreach (IAction ai in global::umbraco.BusinessLogic.Actions.Action.GetPermissionAssignable()) { - - ListItem li = new ListItem(umbraco.ui.Text(ai.Alias, base.getUser()), ai.Letter.ToString()); - - if(CurrentUserTypeActions.Contains(ai)) - li.Selected = true; - - cbl_rights.Items.Add(li); - } - } - - - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.designer.cs deleted file mode 100644 index ac1fddb9ec..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUserType.aspx.designer.cs +++ /dev/null @@ -1,105 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation.user { - - - public partial class EditUserType { - - /// - /// pnlUmbraco control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.UmbracoPanel pnlUmbraco; - - /// - /// pnl1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pnl1; - - /// - /// hidUserTypeID control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.HiddenField hidUserTypeID; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_name; - - /// - /// txtUserTypeName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtUserTypeName; - - /// - /// pp_alias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_alias; - - /// - /// lblUserTypeAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblUserTypeAlias; - - /// - /// pnl2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pnl2; - - /// - /// pp_rights control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_rights; - - /// - /// cbl_rights control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList cbl_rights; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserTypeTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserTypeTasks.cs deleted file mode 100644 index 0a8e02341a..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserTypeTasks.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Data; -using System.Configuration; -using System.Web; -using System.Web.Security; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.WebControls.WebParts; -using System.Web.UI.HtmlControls; -using Umbraco.Web.UI; -using umbraco.interfaces; -using umbraco.BusinessLogic; - -namespace umbraco.cms.presentation.user -{ - public class UserTypeTasks : LegacyDialogTask - { - public override bool PerformSave() - { - try - { - var u = UserType.MakeNew(Alias, "", Alias); - _returnUrl = string.Format("users/EditUserType.aspx?id={0}", u.Id); - return true; - } - catch - { - return false; - } - } - - public override bool PerformDelete() - { - var userType = UserType.GetUserType(ParentID); - if (userType == null) - return false; - userType.Delete(); - return true; - } - - private string _returnUrl = ""; - public override string ReturnUrl - { - get { return _returnUrl; } - } - - public override string AssignedApp - { - get { return DefaultApps.users.ToString(); } - } - } -} diff --git a/src/umbraco.businesslogic/BasePages/BasePage.cs b/src/umbraco.businesslogic/BasePages/BasePage.cs index 8e7f1f47e1..bf026a5c62 100644 --- a/src/umbraco.businesslogic/BasePages/BasePage.cs +++ b/src/umbraco.businesslogic/BasePages/BasePage.cs @@ -293,7 +293,7 @@ namespace umbraco.BasePages AllowedApplications = u.GetApplications().Select(x => x.alias).ToArray(), RealName = u.Name, //currently we only have one user type! - Roles = new[] { u.UserType.Alias }, + Roles = u.GetGroups(), StartContentNode = u.StartNodeId, StartMediaNode = u.StartMediaId, Username = u.LoginName, diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index e8ed675998..f6096306c7 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -313,24 +313,6 @@ namespace umbraco.BusinessLogic return (tmp != null); } - /// - /// Gets or sets the type of the user. - /// - /// The type of the user. - public UserType UserType - { - get - { - if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return new UserType(UserEntity.UserType); - } - set - { - UserEntity.UserType = value.UserTypeItem; - } - } - - /// /// Gets all users /// @@ -447,10 +429,9 @@ namespace umbraco.BusinessLogic /// The full name. /// The login name. /// The password. - /// The user type. - public static User MakeNew(string name, string lname, string passw, UserType ut) + public static User MakeNew(string name, string lname, string passw) { - var user = new Umbraco.Core.Models.Membership.User(name, "", lname, passw, ut.UserTypeItem); + var user = new Umbraco.Core.Models.Membership.User(name, "", lname, passw); ApplicationContext.Current.Services.UserService.Save(user); var u = new User(user); @@ -467,10 +448,9 @@ namespace umbraco.BusinessLogic /// The lname. /// The passw. /// The email. - /// The ut. - public static User MakeNew(string name, string lname, string passw, string email, UserType ut) + public static User MakeNew(string name, string lname, string passw, string email) { - var user = new Umbraco.Core.Models.Membership.User(name, email, lname, passw, ut.UserTypeItem); + var user = new Umbraco.Core.Models.Membership.User(name, email, lname, passw); ApplicationContext.Current.Services.UserService.Save(user); var u = new User(user); @@ -487,8 +467,7 @@ namespace umbraco.BusinessLogic /// The name. /// The lname. /// The email. - /// The ut. - public static void Update(int id, string name, string lname, string email, UserType ut) + public static void Update(int id, string name, string lname, string email) { if (EnsureUniqueLoginName(lname, GetUser(id)) == false) throw new Exception(String.Format("A user with the login '{0}' already exists", lname)); @@ -498,11 +477,10 @@ namespace umbraco.BusinessLogic found.Name = name; found.Username = lname; found.Email = email; - found.UserType = ut.UserTypeItem; ApplicationContext.Current.Services.UserService.Save(found); } - public static void Update(int id, string name, string lname, string email, bool disabled, bool noConsole, UserType ut) + public static void Update(int id, string name, string lname, string email, bool disabled, bool noConsole) { if (EnsureUniqueLoginName(lname, GetUser(id)) == false) throw new Exception(String.Format("A user with the login '{0}' already exists", lname)); @@ -512,7 +490,6 @@ namespace umbraco.BusinessLogic found.Name = name; found.Username = lname; found.Email = email; - found.UserType = ut.UserTypeItem; found.IsApproved = disabled == false; found.IsLockedOut = noConsole; ApplicationContext.Current.Services.UserService.Save(found); @@ -681,7 +658,7 @@ namespace umbraco.BusinessLogic } /// - /// Adds a group to the list groups for the user, ensure to call Save() afterwords + /// Adds a group to the list of groups for the user, ensure to call Save() afterwords /// public void AddGroup(int groupId, string groupName) { @@ -693,6 +670,12 @@ namespace umbraco.BusinessLogic }); } + public string[] GetGroups() + { + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return UserEntity.Groups.Select(x => x.Name).ToArray(); + } + /// /// Gets or sets a value indicating whether the user has access to the Umbraco back end. diff --git a/src/umbraco.businesslogic/UserType.cs b/src/umbraco.businesslogic/UserType.cs deleted file mode 100644 index b7bc2b9675..0000000000 --- a/src/umbraco.businesslogic/UserType.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Globalization; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Web; -using System.Web.Caching; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Events; -using umbraco.DataLayer; - -namespace umbraco.BusinessLogic -{ - /// - /// Represents a umbraco Usertype - /// - [Obsolete("Use the UserService instead")] - public class UserType - { - - internal Umbraco.Core.Models.Membership.IUserType UserTypeItem; - - /// - /// Creates a new empty instance of a UserType - /// - public UserType() - { - UserTypeItem = new Umbraco.Core.Models.Membership.UserType(); - } - - internal UserType(Umbraco.Core.Models.Membership.IUserType userType) - { - UserTypeItem = userType; - } - - /// - /// Creates a new instance of a UserType and attempts to - /// load it's values from the database cache. - /// - /// - /// If the UserType is not found in the existing ID list, then this object - /// will remain an empty object - /// - /// The UserType id to find - public UserType(int id) - { - this.LoadByPrimaryKey(id); - } - - /// - /// Initializes a new instance of the class. - /// - /// The user type id. - /// The name. - public UserType(int id, string name) - { - UserTypeItem = new Umbraco.Core.Models.Membership.UserType(); - UserTypeItem.Id = id; - UserTypeItem.Name = name; - } - - /// - /// Creates a new instance of UserType with all parameters - /// - /// - /// - /// - /// - public UserType(int id, string name, string defaultPermissions, string alias) - { - UserTypeItem = new Umbraco.Core.Models.Membership.UserType(); - UserTypeItem.Id = id; - UserTypeItem.Name = name; - UserTypeItem.Alias = alias; - UserTypeItem.Permissions = defaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); - } - - /// - /// The cache storage for all user types - /// - private static List UserTypes - { - get - { - return ApplicationContext.Current.Services.UserService.GetAllUserTypes() - .Select(x => new UserType(x)) - .ToList(); - } - } - - #region Public Properties - /// - /// Gets or sets the user type alias. - /// - public string Alias - { - get { return UserTypeItem.Alias; } - set { UserTypeItem.Alias = value; } - } - - /// - /// Gets the name of the user type. - /// - public string Name - { - get { return UserTypeItem.Name; } - set { UserTypeItem.Name = value; } - } - - /// - /// Gets the id the user type - /// - public int Id - { - get { return UserTypeItem.Id; } - } - - /// - /// Gets the default permissions of the user type - /// - public string DefaultPermissions - { - get { return UserTypeItem.Permissions == null ? string.Empty : string.Join("", UserTypeItem.Permissions); } - set { UserTypeItem.Permissions = value.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); } - } - - /// - /// Returns an array of UserTypes - /// - [Obsolete("Use the GetAll method instead")] - public static UserType[] getAll - { - get { return GetAllUserTypes().ToArray(); } - } - #endregion - - /// - /// Saves this instance. - /// - public void Save() - { - //ensure that this object has an ID specified (it exists in the database) - if (UserTypeItem.HasIdentity == false) - throw new Exception("The current UserType object does not exist in the database. New UserTypes should be created with the MakeNew method"); - - ApplicationContext.Current.Services.UserService.SaveUserType(UserTypeItem); - - //raise event - OnUpdated(this, new EventArgs()); - } - - /// - /// Deletes this instance. - /// - public void Delete() - { - //ensure that this object has an ID specified (it exists in the database) - if (UserTypeItem.HasIdentity == false) - throw new Exception("The current UserType object does not exist in the database. New UserTypes should be created with the MakeNew method"); - - ApplicationContext.Current.Services.UserService.DeleteUserType(UserTypeItem); - - //raise event - OnDeleted(this, new EventArgs()); - } - - /// - /// Load the data for the current UserType by it's id - /// - /// - /// Returns true if the UserType id was found - /// and the data was loaded, false if it wasn't - public bool LoadByPrimaryKey(int id) - { - UserTypeItem = ApplicationContext.Current.Services.UserService.GetUserTypeById(id); - return UserTypeItem != null; - } - - /// - /// Creates a new user type - /// - /// - /// - /// - public static UserType MakeNew(string name, string defaultPermissions, string alias) - { - //ensure that the current alias does not exist - //get the id for the new user type - var existing = UserTypes.Find(ut => (ut.Alias == alias)); - - if (existing != null) - throw new Exception("The UserType alias specified already exists"); - - var userType = new Umbraco.Core.Models.Membership.UserType - { - Alias = alias, - Name = name, - Permissions = defaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)) - }; - ApplicationContext.Current.Services.UserService.SaveUserType(userType); - - var legacy = new UserType(userType); - - //raise event - OnNew(legacy, new EventArgs()); - - return legacy; - } - - /// - /// Gets the user type with the specied ID - /// - /// The id. - /// - public static UserType GetUserType(int id) - { - return UserTypes.Find(ut => (ut.Id == id)); - } - - /// - /// Returns all UserType's - /// - /// - public static List GetAllUserTypes() - { - return UserTypes; - } - - internal static event TypedEventHandler New; - private static void OnNew(UserType userType, EventArgs args) - { - if (New != null) - { - New(userType, args); - } - } - - internal static event TypedEventHandler Deleted; - private static void OnDeleted(UserType userType, EventArgs args) - { - if (Deleted != null) - { - Deleted(userType, args); - } - } - - internal static event TypedEventHandler Updated; - private static void OnUpdated(UserType userType, EventArgs args) - { - if (Updated != null) - { - Updated(userType, args); - } - } - - } -} \ No newline at end of file diff --git a/src/umbraco.businesslogic/umbraco.businesslogic.csproj b/src/umbraco.businesslogic/umbraco.businesslogic.csproj index 0d6aae1b71..87de399469 100644 --- a/src/umbraco.businesslogic/umbraco.businesslogic.csproj +++ b/src/umbraco.businesslogic/umbraco.businesslogic.csproj @@ -242,9 +242,6 @@ Code - - Code - Code diff --git a/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs b/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs index fcae44b113..cf6fba9e43 100644 --- a/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs +++ b/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Linq; using System.Text.RegularExpressions; using System.Web; using System.Web.UI; @@ -136,8 +137,8 @@ namespace umbraco.editorControls.tinyMCE3 config.Add("plugins", _plugins); // Check advanced settings - if (UmbracoEnsuredPage.CurrentUser != null && ("," + _advancedUsers + ",").IndexOf("," + UmbracoEnsuredPage.CurrentUser.UserType.Id + ",") > - -1) + var advancedUserGroupNames = _advancedUsers.Split(','); + if (UmbracoEnsuredPage.CurrentUser != null && advancedUserGroupNames.Intersect(UmbracoEnsuredPage.CurrentUser.GetGroups()).Any()) config.Add("umbraco_advancedMode", "true"); else config.Add("umbraco_advancedMode", "false"); diff --git a/src/umbraco.editorControls/tinymce/tinyMCEPreValueConfigurator.cs b/src/umbraco.editorControls/tinymce/tinyMCEPreValueConfigurator.cs index 533a961416..fe6505be54 100644 --- a/src/umbraco.editorControls/tinymce/tinyMCEPreValueConfigurator.cs +++ b/src/umbraco.editorControls/tinymce/tinyMCEPreValueConfigurator.cs @@ -1,15 +1,15 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Collections; using System.Web.UI; using System.Web.UI.WebControls; - using umbraco.BusinessLogic; using umbraco.DataLayer; +using Umbraco.Core; namespace umbraco.editorControls.tinymce { + + [Obsolete("IDataType and all other references to the legacy property editors are no longer used this will be removed from the codebase in future versions")] public class tinyMCEPreValueConfigurator : System.Web.UI.WebControls.PlaceHolder, interfaces.IDataPrevalue { @@ -178,10 +178,11 @@ namespace umbraco.editorControls.tinymce } // add users - foreach (BusinessLogic.UserType ut in BusinessLogic.UserType.getAll) + var userService = ApplicationContext.Current.Services.UserService; + foreach (var ug in userService.GetAllUserGroups()) { - ListItem li = new ListItem(ut.Name, ut.Id.ToString()); - if (("," + _advancedUsers + ",").IndexOf("," + ut.Id.ToString() + ",") > -1) + ListItem li = new ListItem(ug.Name, ug.Id.ToString()); + if (("," + _advancedUsers + ",").IndexOf("," + ug.Id + ",") > -1) li.Selected = true; _advancedUsersList.Items.Add(li); diff --git a/src/umbraco.providers/UsersMembershipProvider.cs b/src/umbraco.providers/UsersMembershipProvider.cs index 93f5327cbb..39285449a9 100644 --- a/src/umbraco.providers/UsersMembershipProvider.cs +++ b/src/umbraco.providers/UsersMembershipProvider.cs @@ -137,17 +137,11 @@ namespace umbraco.providers try { // Get the usertype of the current user - var ut = UserType.GetUserType(1); - if (BasePages.UmbracoEnsuredPage.CurrentUser != null) - { - ut = BasePages.UmbracoEnsuredPage.CurrentUser.UserType; - } - //ensure the password is encrypted/hashed string salt; var encodedPass = EncryptOrHashNewPassword(password, out salt); - User.MakeNew(username, username, FormatPasswordForStorage(encodedPass, salt), email, ut); + User.MakeNew(username, username, FormatPasswordForStorage(encodedPass, salt), email); status = MembershipCreateStatus.Success; } @@ -468,8 +462,8 @@ namespace umbraco.providers } else { - //This keeps compatibility - even though this logic to update name and user type should not exist here - User.Update(m.Id, typedUser.FullName.Trim(), typedUser.UserName, typedUser.Email, user.IsApproved == false, user.IsLockedOut, typedUser.UserType); + //This keeps compatibility - even though this logic to update name should not exist here + User.Update(m.Id, typedUser.FullName.Trim(), typedUser.UserName, typedUser.Email, user.IsApproved == false, user.IsLockedOut); } m.Save(); @@ -562,7 +556,7 @@ namespace umbraco.providers return new UsersMembershipUser(base.Name, user.LoginName, user.Id, user.Email, string.Empty, string.Empty, true, user.Disabled, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, - DateTime.Now, user.Name, user.Language, user.UserType); + DateTime.Now, user.Name, user.Language); } #endregion diff --git a/src/umbraco.providers/UsersMembershipUser.cs b/src/umbraco.providers/UsersMembershipUser.cs index dd406662dc..ffb2d75ae9 100644 --- a/src/umbraco.providers/UsersMembershipUser.cs +++ b/src/umbraco.providers/UsersMembershipUser.cs @@ -38,17 +38,7 @@ namespace umbraco.providers get { return _Language; } set { _Language = value; } } - private UserType _UserType; - /// - /// Gets or sets the type of the user. - /// - /// The type of the user. - public UserType UserType - { - get { return _UserType; } - set { _UserType = value; } - } #endregion #region Constructors @@ -78,16 +68,14 @@ namespace umbraco.providers /// The last lockout date. /// The full name. /// The language. - /// Type of the user. public UsersMembershipUser(string providerName, string name, object providerUserKey, string email, string passwordQuestion, string comment, bool isApproved, bool isLockedOut, DateTime creationDate, DateTime lastLoginDate, DateTime lastActivityDate, DateTime lastPasswordChangedDate, - DateTime lastLockoutDate, string fullName, string language, UserType userType ) + DateTime lastLockoutDate, string fullName, string language) : base( providerName, name, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOut, creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate) { _FullName = fullName; - _UserType = userType; _Language = language; } #endregion From 92010adce6bdfc7c1555d528437766a2498d81e1 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 29 Oct 2016 08:28:57 +0200 Subject: [PATCH 06/15] Permission checks on nodes for users and groups --- src/Umbraco.Core/Models/Membership/IUser.cs | 9 - src/Umbraco.Core/Models/Membership/User.cs | 11 -- .../AddUserGroupTables.cs | 10 + .../Repositories/PermissionRepository.cs | 1 + .../Repositories/UserRepository.cs | 3 - src/Umbraco.Core/Services/IUserService.cs | 8 - src/Umbraco.Core/Services/SectionService.cs | 1 - src/Umbraco.Core/Services/UserService.cs | 183 +++++++----------- src/Umbraco.Tests/Models/UserTests.cs | 5 - .../Repositories/UserRepositoryTest.cs | 23 --- .../Services/UserServiceTests.cs | 1 - .../TestHelpers/Entities/MockedUser.cs | 1 - src/umbraco.businesslogic/UserGroup.cs | 8 +- 13 files changed, 89 insertions(+), 175 deletions(-) diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index 1fb82116f9..171a9f0c99 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -31,15 +31,6 @@ namespace Umbraco.Core.Models.Membership void SetGroupsLoaded(); - //TODO: This should be a private set - /// - /// The default permission set for the user - /// - /// - /// Currently in umbraco each permission is a single char but with an Enumerable{string} collection this allows for flexible changes to this in the future - /// - IEnumerable DefaultPermissions { get; set; } - IEnumerable AllowedSections { get; } /// diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index d6d02df8f3..8dd1f31654 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -62,8 +62,6 @@ namespace Umbraco.Core.Models.Membership private DateTime _lastLoginDate; private DateTime _lastLockoutDate; - private IEnumerable _defaultPermissions; - private bool _defaultToLiveEditing; private static readonly Lazy Ps = new Lazy(); @@ -259,14 +257,6 @@ namespace Umbraco.Core.Models.Membership set { SetPropertyValueAndDetectChanges(value, ref _language, Ps.Value.LanguageSelector); } } - //TODO: This should be a private set - [DataMember] - public IEnumerable DefaultPermissions - { - get { return _defaultPermissions;} - set { _defaultPermissions = value; } - } - [IgnoreDataMember] internal bool DefaultToLiveEditing { @@ -318,7 +308,6 @@ namespace Umbraco.Core.Models.Membership clone.DisableChangeTracking(); //need to create new collections otherwise they'll get copied by ref clone._groupCollection = new List(_groupCollection.ToList()); - clone._defaultPermissions = new List(_defaultPermissions.ToList()); //re-create the event handler //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs index dae7505462..603517bbe3 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs @@ -100,6 +100,16 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero ) FROM umbracoUser u"); + // Add the built-in administrator account to all apps + Execute.Sql(@"INSERT INTO umbracoUserGroup2app (userGroupId,app) + SELECT ug.id, app + FROM umbracoUserGroup ug + INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id + INNER JOIN umbracoUser u ON u.id = u2ug.userId + INNER JOIN umbracoUser2app u2a ON u2a.[user] = u.id + WHERE u.id = 0"); + + // Rename some groups for consistency (plural form) Execute.Sql("UPDATE umbracoUserGroup SET userGroupName = 'Writers' WHERE userGroupName = 'Writer'"); Execute.Sql("UPDATE umbracoUserGroup SET userGroupName = 'Translators' WHERE userGroupName = 'Translator'"); } diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index 83c31f6651..b15e11c21a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -104,6 +104,7 @@ namespace Umbraco.Core.Persistence.Repositories whereBuilder.Append(" OR "); } } + whereBuilder.Append(")"); } diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 12bdc12976..dbfb1a5bfe 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -2,13 +2,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using Umbraco.Core; using Umbraco.Core.Logging; -using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; - using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index d6df61a7c5..08454c3b86 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -71,14 +71,6 @@ namespace Umbraco.Core.Services /// An enumerable list of IEnumerable GetPermissions(IUser user, params int[] nodeIds); - /// - /// Get permissions set for a user group and optional node ids - /// - /// Group to retrieve permissions for - /// Specifiying nothing will return all group permissions for all nodes - /// An enumerable list of - IEnumerable GetPermissions(IUserGroup group, params int[] nodeIds); - /// /// Gets the permissions for the provided user and path /// diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index b810840873..02cc3f874b 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -184,7 +184,6 @@ namespace Umbraco.Core.Services /// public IEnumerable
GetAllowedSections(int userId) { - var user = _userService.GetUserById(userId); if (user == null) { diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 11a476a10b..66aad86dbd 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -749,86 +749,41 @@ namespace Umbraco.Core.Services uow.Commit(); } - } + } /// - /// Get permissions set for a user and optional node ids + /// Get permissions set for a user and node Id /// - /// If no permissions are found for a particular entity then the user's default permissions will be applied /// User to retrieve permissions for - /// Specifiying nothing will return all user permissions for all nodes + /// Specifiying nothing will return all permissions for all nodes /// An enumerable list of public IEnumerable GetPermissions(IUser user, params int[] nodeIds) { - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserRepository(uow)) + if (user.GroupsLoaded == false) { - // TODO: rework (to use groups - currently defaulting to user type) - - //if no permissions are assigned to a particular node then we will fill in those permissions with the user's defaults - var result = new List(); - var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); - foreach (var id in missingIds) - { - result.Add( - new EntityPermission( - id, - user.DefaultPermissions.ToArray())); - } - - return result; + throw new InvalidOperationException("Cannot determine permissions for user as their associated groups are not loaded"); } - } - /// - /// Get permissions set for a group and optional node ids - /// - /// Group to retrieve permissions for - /// Specifiying nothing will return all permissions for all nodes - /// An enumerable list of - public IEnumerable GetPermissions(IUserGroup group, params int[] nodeIds) - { - throw new NotImplementedException(); - } - - /// - /// Get permissions set for a user and optional node ids - /// - /// User repository - /// User to retrieve permissions for - /// Specifiying nothing will return all user permissions for all nodes - /// An enumerable list of - private IEnumerable GetPermissions(IUserRepository repository, IUser user, params int[] nodeIds) - { var result = new List(); - - // TODO: rework to groups first, then user type - - // If no permissions are assigned to a particular node then, we will fill in those permissions with the user's defaults - var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); - foreach (var id in missingIds) - { - result.Add( - new EntityPermission( - id, - user.DefaultPermissions.ToArray())); - } - - // Add or amend the existing permissions based on these foreach (var group in user.Groups) { - var groupPermissions = GetPermissions(group, nodeIds).ToList(); - foreach (var groupPermission in groupPermissions) + foreach (var permission in GetPermissions(group, nodeIds)) { - // Add group permission, ensuring we keep a unique value for the entity Id in the list - AddOrAmendPermission(result, groupPermission); + AddOrAmendPermissionList(result, permission); } } return result; } - private void AddOrAmendPermission(IList permissions, EntityPermission groupPermission) + /// + /// For an existing list of , takes a new and aggregates it. + /// If a permission for the entity associated with the new permission already exists, it's updated with those permissions to create a distinct, most permissive set. + /// If it doesn't, it's added to the list. + /// + /// List of already found permissions + /// New permission to aggregate + private void AddOrAmendPermissionList(IList permissions, EntityPermission groupPermission) { var existingPermission = permissions .SingleOrDefault(x => x.EntityId == groupPermission.EntityId); @@ -850,51 +805,38 @@ namespace Umbraco.Core.Services /// String indicating permissions for provided user and path public string GetPermissionsForPath(IUser user, string path) { - var nodeId = GetNodeIdFromPath(path); - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserRepository(uow)) + if (user.GroupsLoaded == false) { - // Check for permissions directly assigned to the node for the user (if we have any, they take priority - // over anything defined on groups; we'll take the special "null" action as not being defined - // as this is set if someone sets permissions on a user and then removes them) - var permissions = GetPermissions(repository, user, nodeId) - .Where(IsNotNullActionPermission) - .ToList(); - var gotDirectlyAssignedUserPermissions = permissions.Any(); - - // If none found, and checking groups, get permissions from the groups - if (gotDirectlyAssignedUserPermissions == false) - { - foreach (var group in user.Groups) - { - var groupPermissions = GetPermissions(group, nodeId).ToList(); - permissions.AddRange(groupPermissions); - } - } - - // If none found directly on the user, get those defined on the user type - if (gotDirectlyAssignedUserPermissions == false) - { - var typePermissions = GetPermissions(repository, user, nodeId).ToList(); - permissions.AddRange(typePermissions); - } - - // Extract the net permissions from the path from the set of permissions found - string assignedPermissions; - if (TryGetAssignedPermissionsForNode(permissions, nodeId, out assignedPermissions)) - { - return assignedPermissions; - } - - // Exception to everything. If default cruds is empty and we're on root node; allow browse of root node - if (path == "-1") - { - return "F"; - } - - return string.Empty; + throw new InvalidOperationException("Cannot determine permissions for user as their associated groups are not loaded"); } + + var assignedPermissions = GetPermissionsForGroupsAndPath(user.Groups, path); + return GetAggregatePermissions(assignedPermissions); + } + + /// + /// Retrieves the permissions assigned to each group for a given path + /// + /// List of groups associated with the user + /// Path to check permissions for + /// List of strings indicating permissions for each groups + private IEnumerable GetPermissionsForGroupsAndPath(IEnumerable groups, string path) + { + return groups + .Select(g => GetPermissionsForPath(g, path)) + .ToList(); + } + + /// + /// Aggregates a set of permissions strings to return a unique permissions string containing the most permissive set + /// + /// List of permission strings + /// Single permission string + private static string GetAggregatePermissions(IEnumerable assignedPermissions) + { + return string.Join(string.Empty, assignedPermissions + .SelectMany(s => s.ToCharArray()) + .Distinct()); } /// @@ -906,14 +848,14 @@ namespace Umbraco.Core.Services public string GetPermissionsForPath(IUserGroup group, string path) { var nodeId = GetNodeIdFromPath(path); - - var permissions = GetPermissions(group).ToList(); - - string assignedPermissions; - TryGetAssignedPermissionsForNode(permissions, nodeId, out assignedPermissions); - return assignedPermissions; + return string.Join(string.Empty, GetPermissions(group, nodeId).Single().AssignedPermissions); } + /// + /// Parses a path to find the lowermost node id + /// + /// Path as string + /// Node id private static int GetNodeIdFromPath(string path) { return path.Contains(",") @@ -921,6 +863,29 @@ namespace Umbraco.Core.Services : int.Parse(path); } + /// + /// Get permissions set for a group and node Id + /// + /// Group to retrieve permissions for + /// Specifiying nothing will return all permissions for all nodes + /// An enumerable list of + private IEnumerable GetPermissions(IUserGroup group, params int[] nodeIds) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) + { + var explicitPermissions = repository.GetPermissionsForEntities(group.Id, nodeIds); + var result = new List(explicitPermissions); + + // If no permissions are assigned to a particular node then we will fill in those permissions with the group's defaults + var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); + result.AddRange(missingIds + .Select(i => new EntityPermission(i, group.Permissions.ToArray()))); + + return result; + } + } + private static bool IsNotNullActionPermission(EntityPermission x) { const string NullActionChar = "-"; diff --git a/src/Umbraco.Tests/Models/UserTests.cs b/src/Umbraco.Tests/Models/UserTests.cs index 8699a97698..5bdc842cc1 100644 --- a/src/Umbraco.Tests/Models/UserTests.cs +++ b/src/Umbraco.Tests/Models/UserTests.cs @@ -21,7 +21,6 @@ namespace Umbraco.Tests.Models CreateDate = DateTime.Now, Name = "Test", Comments = "comments", - DefaultPermissions = new[]{"a","b","c"}, DefaultToLiveEditing = false, Email = "test@test.com", Language = "en", @@ -48,9 +47,6 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.AllowedSections.Count(), item.AllowedSections.Count()); - Assert.AreNotSame(clone.DefaultPermissions, item.DefaultPermissions); - Assert.AreEqual(clone.DefaultPermissions.Count(), item.DefaultPermissions.Count()); - //Verify normal properties with reflection var allProps = clone.GetType().GetProperties(); foreach (var propertyInfo in allProps) @@ -72,7 +68,6 @@ namespace Umbraco.Tests.Models CreateDate = DateTime.Now, Name = "Test", Comments = "comments", - DefaultPermissions = new[] { "a", "b", "c" }, DefaultToLiveEditing = false, Email = "test@test.com", Language = "en", diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 5c7a87c240..d3ba021294 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -322,33 +322,10 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Test] - public void Default_User_Permissions_Based_On_User_Type() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(Logger); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new UserRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, SqlSyntax)) - { - - // Act - var user1 = MockedUser.CreateUser("1"); - repository.AddOrUpdate(user1); - unitOfWork.Commit(); - - // Assert - Assert.AreEqual(3, user1.DefaultPermissions.Count()); - Assert.AreEqual("A", user1.DefaultPermissions.ElementAt(0)); - Assert.AreEqual("B", user1.DefaultPermissions.ElementAt(1)); - Assert.AreEqual("C", user1.DefaultPermissions.ElementAt(2)); - } - } - private void AssertPropertyValues(IUser updatedItem, IUser originalUser) { Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id)); Assert.That(updatedItem.Name, Is.EqualTo(originalUser.Name)); - Assert.That(updatedItem.DefaultPermissions, Is.EqualTo(originalUser.DefaultPermissions)); Assert.That(updatedItem.Language, Is.EqualTo(originalUser.Language)); Assert.That(updatedItem.IsApproved, Is.EqualTo(originalUser.IsApproved)); Assert.That(updatedItem.RawPasswordValue, Is.EqualTo(originalUser.RawPasswordValue)); diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index 4acdff7d2d..b8f23cee6c 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -506,7 +506,6 @@ namespace Umbraco.Tests.Services Assert.IsNotNull(updatedItem); Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id)); Assert.That(updatedItem.Name, Is.EqualTo(originalUser.Name)); - Assert.That(updatedItem.DefaultPermissions, Is.EqualTo(originalUser.DefaultPermissions)); Assert.That(updatedItem.Language, Is.EqualTo(originalUser.Language)); Assert.That(updatedItem.IsApproved, Is.EqualTo(originalUser.IsApproved)); Assert.That(updatedItem.RawPasswordValue, Is.EqualTo(originalUser.RawPasswordValue)); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs index 9237574c51..53b53e634f 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs @@ -15,7 +15,6 @@ namespace Umbraco.Tests.TestHelpers.Entities Name = "TestUser" + suffix, RawPasswordValue = "testing", IsLockedOut = false, - DefaultPermissions = new[]{"A", "B", "C"}, StartContentId = -1, StartMediaId = -1, Email = "test" + suffix + "@test.com", diff --git a/src/umbraco.businesslogic/UserGroup.cs b/src/umbraco.businesslogic/UserGroup.cs index c04dad96e3..4a18a95c33 100644 --- a/src/umbraco.businesslogic/UserGroup.cs +++ b/src/umbraco.businesslogic/UserGroup.cs @@ -103,7 +103,7 @@ namespace umbraco.BusinessLogic } /// - /// Gets the id the user type + /// Gets the id the user group /// public int Id { @@ -111,7 +111,7 @@ namespace umbraco.BusinessLogic } /// - /// Gets the default permissions of the user type + /// Gets the default permissions of the user group /// public string DefaultPermissions { @@ -238,7 +238,7 @@ namespace umbraco.BusinessLogic public static UserGroup MakeNew(string name, string defaultPermissions, string alias) { //ensure that the current alias does not exist - //get the id for the new user type + //get the id for the new user group var existing = UserGroups.Find(ut => (ut.Alias == alias)); if (existing != null) @@ -261,7 +261,7 @@ namespace umbraco.BusinessLogic } /// - /// Gets the user type with the specied ID + /// Gets the user group with the specfied ID /// /// The id. /// From 93c400a005e36831c04b9047922ce5e1290ae124 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 29 Oct 2016 23:10:00 +0200 Subject: [PATCH 07/15] Fixed and added unit tests for permission checks --- .../Models/Membership/IUserGroup.cs | 2 + .../Models/Membership/UserGroup.cs | 5 ++ .../Repositories/ContentRepository.cs | 8 +-- .../Interfaces/IContentRepository.cs | 6 +-- src/Umbraco.Core/Services/ContentService.cs | 6 +-- src/Umbraco.Core/Services/IContentService.cs | 6 +-- .../Services/IMembershipUserService.cs | 1 + src/Umbraco.Core/Services/SectionService.cs | 2 +- src/Umbraco.Core/Services/UserService.cs | 9 ++-- .../Repositories/ContentRepositoryTest.cs | 24 +++++---- .../Repositories/UserGroupRepositoryTest.cs | 40 +++++++------- .../Repositories/UserRepositoryTest.cs | 42 +++++++++------ .../Services/SectionServiceTests.cs | 53 +++++++------------ .../Services/UserServiceTests.cs | 50 ++++++++++++----- .../TestHelpers/Entities/MockedUserGroup.cs | 27 +++++++--- 15 files changed, 167 insertions(+), 114 deletions(-) diff --git a/src/Umbraco.Core/Models/Membership/IUserGroup.cs b/src/Umbraco.Core/Models/Membership/IUserGroup.cs index 8e9f862910..336caa4c47 100644 --- a/src/Umbraco.Core/Models/Membership/IUserGroup.cs +++ b/src/Umbraco.Core/Models/Membership/IUserGroup.cs @@ -28,5 +28,7 @@ namespace Umbraco.Core.Models.Membership void RemoveAllowedSection(string sectionAlias); void AddAllowedSection(string sectionAlias); + + void ClearAllowedSections(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserGroup.cs b/src/Umbraco.Core/Models/Membership/UserGroup.cs index ea5f891037..885a7cbe55 100644 --- a/src/Umbraco.Core/Models/Membership/UserGroup.cs +++ b/src/Umbraco.Core/Models/Membership/UserGroup.cs @@ -93,5 +93,10 @@ namespace Umbraco.Core.Models.Membership _sectionCollection.Add(sectionAlias); } } + + public void ClearAllowedSections() + { + _sectionCollection.Clear(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 1a544cc32e..0a244d30ef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -780,15 +780,15 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder"; } /// - /// Assigns a single permission to the current content item for the specified user ids + /// Assigns a single permission to the current content item for the specified user group ids /// /// /// - /// - public void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds) + /// + public void AssignEntityPermission(IContent entity, char permission, IEnumerable groupIds) { var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); - repo.AssignEntityPermission(entity, permission, userIds); + repo.AssignEntityPermission(entity, permission, groupIds); } public IEnumerable GetPermissionsForEntity(int entityId) diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 5f74a21d7a..a0c379666b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -48,12 +48,12 @@ namespace Umbraco.Core.Persistence.Repositories IEnumerable GetByPublishedVersion(IQuery query); /// - /// Assigns a single permission to the current content item for the specified user ids + /// Assigns a single permission to the current content item for the specified user group ids /// /// /// - /// - void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds); + /// + void AssignEntityPermission(IContent entity, char permission, IEnumerable groupIds); /// /// Gets the list of permissions for the content item diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 8aa7030a8f..b88678ac73 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -111,13 +111,13 @@ namespace Umbraco.Core.Services /// /// /// - /// - public void AssignContentPermission(IContent entity, char permission, IEnumerable userIds) + /// + public void AssignContentPermission(IContent entity, char permission, IEnumerable groupIds) { var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateContentRepository(uow)) { - repository.AssignEntityPermission(entity, permission, userIds); + repository.AssignEntityPermission(entity, permission, groupIds); } } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 6ee384c413..d2f277dfbb 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -123,12 +123,12 @@ namespace Umbraco.Core.Services void ReplaceContentPermissions(EntityPermissionSet permissionSet); /// - /// Assigns a single permission to the current content item for the specified user ids + /// Assigns a single permission to the current content item for the specified user group ids /// /// /// - /// - void AssignContentPermission(IContent entity, char permission, IEnumerable userIds); + /// + void AssignContentPermission(IContent entity, char permission, IEnumerable groupIds); /// /// Gets the list of permissions for the content item diff --git a/src/Umbraco.Core/Services/IMembershipUserService.cs b/src/Umbraco.Core/Services/IMembershipUserService.cs index 63dc4db37c..4143a871bf 100644 --- a/src/Umbraco.Core/Services/IMembershipUserService.cs +++ b/src/Umbraco.Core/Services/IMembershipUserService.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Services diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index 02cc3f874b..b12412b5f2 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -249,7 +249,7 @@ namespace Umbraco.Core.Services { //delete the assigned applications _uowProvider.GetUnitOfWork().Database.Execute( - "delete from umbracoUser2App where app = @appAlias", + "delete from umbracoUserGroup2App where app = @appAlias", new { appAlias = section.Alias }); //delete the assigned trees diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 66aad86dbd..45dc0c921d 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -878,9 +878,12 @@ namespace Umbraco.Core.Services var result = new List(explicitPermissions); // If no permissions are assigned to a particular node then we will fill in those permissions with the group's defaults - var missingIds = nodeIds.Except(result.Select(x => x.EntityId)); - result.AddRange(missingIds - .Select(i => new EntityPermission(i, group.Permissions.ToArray()))); + var missingIds = nodeIds.Except(result.Select(x => x.EntityId)).ToList(); + if (missingIds.Any()) + { + result.AddRange(missingIds + .Select(i => new EntityPermission(i, group.Permissions.ToArray()))); + } return result; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index abcb5b3d6a..e9b86ee2d3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -1,27 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using System.Xml.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; -using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence; - -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; -using umbraco.editorControls.tinyMCE3; -using umbraco.interfaces; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Tests.Persistence.Repositories @@ -67,6 +59,11 @@ namespace Umbraco.Tests.Persistence.Repositories return repository; } + private UserGroupRepository CreateUserGroupRepository(IDatabaseUnitOfWork unitOfWork) + { + return new UserGroupRepository(unitOfWork, CacheHelper, Logger, SqlSyntax); + } + [Test] public void Rebuild_Xml_Structures_With_Non_Latest_Version() { @@ -299,7 +296,14 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; + using (var repository = CreateUserGroupRepository(unitOfWork)) + { + var userGroup = MockedUserGroup.CreateUserGroup("1"); + repository.AddOrUpdate(userGroup); + unitOfWork.Commit(); + } + + ContentTypeRepository contentTypeRepository; using (var repository = CreateRepository(unitOfWork, out contentTypeRepository)) { var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); @@ -313,7 +317,7 @@ namespace Umbraco.Tests.Persistence.Repositories unitOfWork.Commit(); // Act - repository.AssignEntityPermission(parentPage, 'A', new int[] { 0 }); + repository.AssignEntityPermission(parentPage, 'A', new [] { 1 }); var childPage = MockedContent.CreateSimpleContent(contentType, "child", parentPage); repository.AddOrUpdate(childPage); unitOfWork.Commit(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs index a29aeb8c29..6a2412bc63 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs @@ -325,18 +325,24 @@ namespace Umbraco.Tests.Persistence.Repositories // Act //add and remove a few times, this tests the internal collection + groups[0].ClearAllowedSections(); + groups[0].AddAllowedSection("content"); + groups[0].AddAllowedSection("media"); + groups[0].RemoveAllowedSection("content"); + groups[0].AddAllowedSection("content"); groups[0].AddAllowedSection("settings"); - groups[0].AddAllowedSection("settings"); - groups[0].RemoveAllowedSection("settings"); - groups[0].AddAllowedSection("settings"); - - groups[1].AddAllowedSection("developer"); //add the same even though it's already there - groups[2].AddAllowedSection("content"); + groups[0].AddAllowedSection("content"); + + groups[1].ClearAllowedSections(); + groups[1].AddAllowedSection("developer"); + + groups[2].ClearAllowedSections(); repository.AddOrUpdate(groups[0]); repository.AddOrUpdate(groups[1]); + repository.AddOrUpdate(groups[2]); unitOfWork.Commit(); // Assert @@ -345,13 +351,9 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.IsTrue(result[0].AllowedSections.Contains("content")); Assert.IsTrue(result[0].AllowedSections.Contains("media")); Assert.IsTrue(result[0].AllowedSections.Contains("settings")); - Assert.AreEqual(3, result[1].AllowedSections.Count()); - Assert.IsTrue(result[1].AllowedSections.Contains("content")); - Assert.IsTrue(result[1].AllowedSections.Contains("media")); + Assert.AreEqual(1, result[1].AllowedSections.Count()); Assert.IsTrue(result[1].AllowedSections.Contains("developer")); - Assert.AreEqual(2, result[2].AllowedSections.Count()); - Assert.IsTrue(result[1].AllowedSections.Contains("content")); - Assert.IsTrue(result[1].AllowedSections.Contains("media")); + Assert.AreEqual(0, result[2].AllowedSections.Count()); } } @@ -390,23 +392,23 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); using (var repository = CreateRepository(unitOfWork)) { - var user1 = MockedUserGroup.CreateUserGroup("1", new[] { "media" }); - var user2 = MockedUserGroup.CreateUserGroup("2", new[] { "settings" }); - var user3 = MockedUserGroup.CreateUserGroup("3", new[] { "settings" }); + var user1 = MockedUserGroup.CreateUserGroup("1", allowedSections: new[] { "test1" }); + var user2 = MockedUserGroup.CreateUserGroup("2", allowedSections: new[] { "test2" }); + var user3 = MockedUserGroup.CreateUserGroup("3", allowedSections: new[] { "test1" }); repository.AddOrUpdate(user1); repository.AddOrUpdate(user2); repository.AddOrUpdate(user3); unitOfWork.Commit(); // Act - - var groups = repository.GetGroupsAssignedToSection("test"); + var groups = repository.GetGroupsAssignedToSection("test1"); // Assert Assert.AreEqual(2, groups.Count()); var names = groups.Select(x => x.Name).ToArray(); - Assert.IsTrue(names.Contains("TestGroup1")); - Assert.IsTrue(names.Contains("TestGroup3")); + Assert.IsTrue(names.Contains("TestUserGroup1")); + Assert.IsFalse(names.Contains("TestUserGroup2")); + Assert.IsTrue(names.Contains("TestUserGroup3")); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index d3ba021294..45c21e82c3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -30,10 +30,13 @@ namespace Umbraco.Tests.Persistence.Repositories private UserRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) { - var repository = new UserRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); - return repository; + return new UserRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } + private UserGroupRepository CreateUserGroupRepository(IDatabaseUnitOfWork unitOfWork) + { + return new UserGroupRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); + } [Test] public void Can_Perform_Add_On_UserRepository() @@ -43,7 +46,6 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); using (var repository = CreateRepository(unitOfWork)) { - var user = MockedUser.CreateUser(); // Act @@ -63,7 +65,6 @@ namespace Umbraco.Tests.Persistence.Repositories var unitOfWork = provider.GetUnitOfWork(); using (var repository = CreateRepository(unitOfWork)) { - var user1 = MockedUser.CreateUser("1"); var use2 = MockedUser.CreateUser("2"); @@ -107,17 +108,14 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); using (var repository = CreateRepository(unitOfWork)) + using (var userGroupRepository = CreateUserGroupRepository(unitOfWork)) { - var user = MockedUser.CreateUser(); - repository.AddOrUpdate(user); - unitOfWork.Commit(); + var user = CreateAndCommitUserWithGroup(repository, userGroupRepository, unitOfWork); // Act var resolved = (User)repository.Get((int)user.Id); resolved.Name = "New Name"; - //the db column is not used, default permissions are taken from the user type's permissions, this is a getter only - //resolved.DefaultPermissions = "ZYX"; resolved.Language = "fr"; resolved.IsApproved = false; resolved.RawPasswordValue = "new"; @@ -134,7 +132,6 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(updatedItem.Id, Is.EqualTo(resolved.Id)); Assert.That(updatedItem.Name, Is.EqualTo(resolved.Name)); - //Assert.That(updatedItem.DefaultPermissions, Is.EqualTo(resolved.DefaultPermissions)); Assert.That(updatedItem.Language, Is.EqualTo(resolved.Language)); Assert.That(updatedItem.IsApproved, Is.EqualTo(resolved.IsApproved)); Assert.That(updatedItem.RawPasswordValue, Is.EqualTo(resolved.RawPasswordValue)); @@ -143,7 +140,8 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(updatedItem.StartMediaId, Is.EqualTo(resolved.StartMediaId)); Assert.That(updatedItem.Email, Is.EqualTo(resolved.Email)); Assert.That(updatedItem.Username, Is.EqualTo(resolved.Username)); - Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(1)); + Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(2)); + Assert.IsTrue(updatedItem.AllowedSections.Contains("content")); Assert.IsTrue(updatedItem.AllowedSections.Contains("media")); } } @@ -213,13 +211,12 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); using (var repository = CreateRepository(unitOfWork)) + using (var userGroupRepository = CreateUserGroupRepository(unitOfWork)) { - var user = MockedUser.CreateUser(); - repository.AddOrUpdate(user); - unitOfWork.Commit(); + var user = CreateAndCommitUserWithGroup(repository, userGroupRepository, unitOfWork); // Act - var updatedItem = repository.Get((int) user.Id); + var updatedItem = repository.Get((int)user.Id); // Assert AssertPropertyValues(updatedItem, user); @@ -339,6 +336,21 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.IsTrue(updatedItem.AllowedSections.Contains("content")); } + private static User CreateAndCommitUserWithGroup(IUserRepository repository, IUserGroupRepository userGroupRepository, IDatabaseUnitOfWork unitOfWork) + { + var group = MockedUserGroup.CreateUserGroup(); + userGroupRepository.AddOrUpdate(@group); + unitOfWork.Commit(); + + var user = MockedUser.CreateUser(); + repository.AddOrUpdate(user); + unitOfWork.Commit(); + + userGroupRepository.AddUsersToGroup(@group.Id, new[] { user.Id }); + unitOfWork.Commit(); + return user; + } + private IUser[] CreateAndCommitMultipleUsers(IUserRepository repository, IUnitOfWork unitOfWork) { var user1 = MockedUser.CreateUser("1"); diff --git a/src/Umbraco.Tests/Services/SectionServiceTests.cs b/src/Umbraco.Tests/Services/SectionServiceTests.cs index a300d76e60..5c51233892 100644 --- a/src/Umbraco.Tests/Services/SectionServiceTests.cs +++ b/src/Umbraco.Tests/Services/SectionServiceTests.cs @@ -29,34 +29,20 @@ namespace Umbraco.Tests.Services ServiceContext.SectionService.MakeNew("Settings", "settings", "icon-settings"); ServiceContext.SectionService.MakeNew("Developer", "developer", "icon-developer"); } - [Test] public void SectionService_Can_Get_Allowed_Sections_For_User() { // Arrange - var user = CreateUser(); + var user = CreateTestUser(); // Act var result = ServiceContext.SectionService.GetAllowedSections(user.Id).ToList(); // Assert - Assert.AreEqual(2, result.Count); + Assert.AreEqual(3, result.Count); } - [Test] - public void SectionService_Can_Get_Allowed_Sections_For_User_With_Groups() - { - // Arrange - var user = CreateUser(true); - - // Act - var result = ServiceContext.SectionService.GetAllowedSections(user.Id).ToList(); - - // Assert - Assert.AreEqual(4, result.Count); - } - - private IUser CreateUser(bool withGroups = false) + private IUser CreateTestUser() { var user = new User { @@ -66,26 +52,23 @@ namespace Umbraco.Tests.Services }; ServiceContext.UserService.Save(user, false); - if (withGroups) + var userGroupA = new UserGroup { - var userGroupA = new UserGroup - { - Alias = "GroupA", - Name = "Group A" - }; - userGroupA.AddAllowedSection("media"); - userGroupA.AddAllowedSection("settings"); - ServiceContext.UserService.SaveUserGroup(userGroupA, true, new[] { user.Id }, false); + Alias = "GroupA", + Name = "Group A" + }; + userGroupA.AddAllowedSection("media"); + userGroupA.AddAllowedSection("settings"); + ServiceContext.UserService.SaveUserGroup(userGroupA, true, new[] { user.Id }, false); - var userGroupB = new UserGroup - { - Alias = "GroupB", - Name = "Group B" - }; - userGroupB.AddAllowedSection("settings"); - userGroupB.AddAllowedSection("developer"); - ServiceContext.UserService.SaveUserGroup(userGroupB, true, new[] { user.Id }, false); - } + var userGroupB = new UserGroup + { + Alias = "GroupB", + Name = "Group B" + }; + userGroupB.AddAllowedSection("settings"); + userGroupB.AddAllowedSection("developer"); + ServiceContext.UserService.SaveUserGroup(userGroupB, true, new[] { user.Id }, false); return ServiceContext.UserService.GetUserById(user.Id); } diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index b8f23cee6c..795088f48f 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -39,7 +39,8 @@ namespace Umbraco.Tests.Services { // Arrange var userService = ServiceContext.UserService; - var user = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com"); + var user = CreateTestUser(); + var contentType = MockedContentTypes.CreateSimpleContentType(); ServiceContext.ContentTypeService.Save(contentType); var content = new[] @@ -65,7 +66,8 @@ namespace Umbraco.Tests.Services { // Arrange var userService = ServiceContext.UserService; - var user = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com"); + var user = CreateTestUser(); + var contentType = MockedContentTypes.CreateSimpleContentType(); ServiceContext.ContentTypeService.Save(contentType); var content = new[] @@ -75,14 +77,12 @@ namespace Umbraco.Tests.Services MockedContent.CreateSimpleContent(contentType) }; ServiceContext.ContentService.Save(content); - ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionBrowse.Instance.Letter, new int[] { user.Id }); - ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionDelete.Instance.Letter, new int[] { user.Id }); - ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionMove.Instance.Letter, new int[] { user.Id }); - - ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionBrowse.Instance.Letter, new int[] { user.Id }); - ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionDelete.Instance.Letter, new int[] { user.Id }); - - ServiceContext.ContentService.AssignContentPermission(content.ElementAt(2), ActionBrowse.Instance.Letter, new int[] { user.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionBrowse.Instance.Letter, new int[] { 1 }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionDelete.Instance.Letter, new int[] { 1 }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionMove.Instance.Letter, new int[] { 1 }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionBrowse.Instance.Letter, new int[] { 1 }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionDelete.Instance.Letter, new int[] { 1 }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(2), ActionBrowse.Instance.Letter, new int[] { 1 }); // Act var permissions = userService.GetPermissions(user, content.ElementAt(0).Id, content.ElementAt(1).Id, content.ElementAt(2).Id); @@ -346,6 +346,7 @@ namespace Umbraco.Tests.Services { Id = 1, Alias = "Group1", + Name = "Group 1" }; userGroup.AddAllowedSection("content"); userGroup.AddAllowedSection("mediat"); @@ -390,11 +391,13 @@ namespace Umbraco.Tests.Services { Id = 1, Alias = "Group1", + Name = "Group 2" }; var userGroup2 = new UserGroup { Id = 2, Alias = "Group2", + Name = "Group 2" }; ServiceContext.UserService.SaveUserGroup(userGroup1); ServiceContext.UserService.SaveUserGroup(userGroup2); @@ -422,16 +425,19 @@ namespace Umbraco.Tests.Services { Id = 1, Alias = "Group1", + Name = "Group 1" }; var userGroup2 = new UserGroup { Id = 2, Alias = "Group2", + Name = "Group 2" }; var userGroup3 = new UserGroup { - Id = 2, + Id = 3, Alias = "Group3", + Name = "Group 3" }; ServiceContext.UserService.SaveUserGroup(userGroup1); ServiceContext.UserService.SaveUserGroup(userGroup2); @@ -496,7 +502,7 @@ namespace Umbraco.Tests.Services public void Get_User_By_Username() { // Arrange - var originalUser = (User)ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com"); + var originalUser = CreateTestUser(); // Act @@ -516,5 +522,25 @@ namespace Umbraco.Tests.Services Assert.That(updatedItem.Username, Is.EqualTo(originalUser.Username)); Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(2)); } + + private IUser CreateTestUser() + { + var userGroup = new UserGroup + { + Id = 1, + Alias = "testGroup", + Name = "Test Group", + Permissions = "ABCDEFGHIJ1234567".ToCharArray().Select(x => x.ToString()) + }; + ServiceContext.UserService.SaveUserGroup(userGroup); + ServiceContext.UserService.AddSectionToAllUserGroups("content", 1); + ServiceContext.UserService.AddSectionToAllUserGroups("media", 1); + + var user = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com"); + user.AddGroup(userGroup); + user.SetGroupsLoaded(); + ServiceContext.UserService.Save(user); + return user; + } } } diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs index f266f55332..920c84f3c3 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs @@ -4,14 +4,29 @@ namespace Umbraco.Tests.TestHelpers.Entities { public class MockedUserGroup { - internal static UserGroup CreateUserGroup(string suffix = "", string[] permissions = null) + internal static UserGroup CreateUserGroup(string suffix = "", string[] permissions = null, string[] allowedSections = null) { - return new UserGroup() + var group = new UserGroup + { + Alias = "testUserGroup" + suffix, + Name = "TestUserGroup" + suffix, + Permissions = permissions ?? new[] { "A", "B", "C" } + }; + + if (allowedSections == null) + { + group.AddAllowedSection("content"); + group.AddAllowedSection("media"); + } + else + { + foreach (var allowedSection in allowedSections) { - Alias = "testUserGroup" + suffix, - Name = "TestUserGroup" + suffix, - Permissions = permissions ?? new[]{"A", "B", "C"} - }; + group.AddAllowedSection(allowedSection); + } + } + + return group; } } } \ No newline at end of file From 311401d1198d68f7c6fbf2608274356a68ed1391 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 29 Oct 2016 23:57:04 +0200 Subject: [PATCH 08/15] Ensured groups always loaded along with users --- .../Persistence/Repositories/UserRepository.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index dbfb1a5bfe..cad213d1a0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -68,8 +68,15 @@ namespace Umbraco.Core.Persistence.Repositories sql.Where("umbracoUser.id in (@ids)", new {ids = ids}); } - return ConvertFromDtos(Database.Fetch(sql)) + var users = ConvertFromDtos(Database.Fetch(sql)) .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + + foreach (var user in users) + { + AssociateGroupsWithUser(user); + } + + return users; } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -84,10 +91,9 @@ namespace Umbraco.Core.Persistence.Repositories var users = ConvertFromDtos(dtos) .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. - // If a single user found (most likely from a look-up by an alternate key like email or username) then populate the groups - if (users.Length == 1) + foreach (var user in users) { - AssociateGroupsWithUser(users[0]); + AssociateGroupsWithUser(user); } return users; From f316b3f6942d6c2b38854d14e7e3b42424a24404 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 29 Oct 2016 23:57:31 +0200 Subject: [PATCH 09/15] Fixed display of node permissions when editing for a group --- .../umbraco/users/PermissionsEditor.js | 14 +++++++------- .../umbraco/users/PermissionsHandler.asmx.cs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/users/PermissionsEditor.js b/src/Umbraco.Web.UI/umbraco/users/PermissionsEditor.js index c193b24394..61a2c716ac 100644 --- a/src/Umbraco.Web.UI/umbraco/users/PermissionsEditor.js +++ b/src/Umbraco.Web.UI/umbraco/users/PermissionsEditor.js @@ -9,11 +9,11 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); $.fn.PermissionsEditor = function(opts) { return this.each(function() { var conf = $.extend({ - userId: -1, + groupId: -1, pPanelSelector: "", replacePChkBoxSelector: "" }, opts); - new Umbraco.Controls.PermissionsEditor().init($(this), conf.userId, $(conf.pPanelSelector), conf.replacePChkBoxSelector); + new Umbraco.Controls.PermissionsEditor().init($(this), conf.groupId, $(conf.pPanelSelector), conf.replacePChkBoxSelector); }); }; $.fn.PermissionsEditorAPI = function() { @@ -26,16 +26,16 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); /// return { //private members - _userID: -1, + _groupId: -1, _loadingContent: '







', _pPanel: null, _tree: null, _selectedNodes: new Array(), _chkReplaceSelector: null, - init: function(tree, uId, pPanel, chkReplaceSelector) { + init: function(tree, gId, pPanel, chkReplaceSelector) { ///constructor function - this._userID = parseInt(uId); + this._groupId = parseInt(gId); this._pPanel = pPanel; this._tree = tree; this._chkReplaceSelector = chkReplaceSelector; @@ -122,14 +122,14 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); }, _showNodePermissions: function(selectedIDs) { var _this = this; - umbraco.cms.presentation.user.PermissionsHandler.GetNodePermissions(this._userID, selectedIDs, function(r) { _this._showNodePermissionsCallback(r); }); + umbraco.cms.presentation.user.PermissionsHandler.GetNodePermissions(this._groupId, selectedIDs, function (r) { _this._showNodePermissionsCallback(r); }); }, _showNodePermissionsCallback: function(result) { this._pPanel.html(result); }, _savePermissions: function(nodeIDs, selectedPermissions, replaceChildren) { var _this = this; - umbraco.cms.presentation.user.PermissionsHandler.SaveNodePermissions(this._userID, nodeIDs, selectedPermissions, replaceChildren, function(r) { _this._savePermissionsHandler(r) }); + umbraco.cms.presentation.user.PermissionsHandler.SaveNodePermissions(this._groupId, nodeIDs, selectedPermissions, replaceChildren, function (r) { _this._savePermissionsHandler(r) }); }, _savePermissionsHandler: function(result) { if (UmbClientMgr.mainWindow().UmbSpeechBubble != null) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionsHandler.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionsHandler.asmx.cs index b7c5e173cf..a570a5c7bc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionsHandler.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionsHandler.asmx.cs @@ -18,10 +18,10 @@ namespace umbraco.cms.presentation.user [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ToolboxItem(false)] [ScriptService] - public class GroupPermissionsHandler + public class PermissionsHandler { /// - /// Loads the GroupNodePermissions UserControl with the appropriate properties, renders the contents and returns the output html. + /// Loads the NodePermissions UserControl with the appropriate properties, renders the contents and returns the output html. /// /// [WebMethod] From 6560f38cb09fcb1c0d60e25f8a7237ab804e00a7 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 30 Oct 2016 11:58:36 +0100 Subject: [PATCH 10/15] Fixed caching issues on editing node permissions --- src/Umbraco.Core/Cache/CacheKeys.cs | 2 - src/Umbraco.Core/Services/IUserService.cs | 18 +++- src/Umbraco.Core/Services/UserService.cs | 75 +++++++++------- .../Services/UserServiceTests.cs | 85 +++++++++++++++++-- src/Umbraco.Web/Cache/UserCacheRefresher.cs | 15 +--- .../Cache/UserGroupCacheRefresher.cs | 2 +- .../UserGroupPermissionsCacheRefresher.cs | 2 +- 7 files changed, 143 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index 596bf2455b..02b01f1e78 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -56,8 +56,6 @@ namespace Umbraco.Core.Cache [EditorBrowsable(EditorBrowsableState.Never)] public const string UserCacheKey = "UmbracoUser"; - public const string UserPermissionsCacheKey = "UmbracoUserPermissions"; - public const string UserGroupPermissionsCacheKey = "UmbracoUserGroupPermissions"; [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 08454c3b86..dafe746fc8 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -71,6 +71,18 @@ namespace Umbraco.Core.Services /// An enumerable list of IEnumerable GetPermissions(IUser user, params int[] nodeIds); + /// + /// Get permissions set for a group and optional node Ids + /// + /// Group to retrieve permissions for + /// + /// Flag indicating if we want to get just the permissions directly assigned for the group and path, + /// or fall back to the group's default permissions when nothing is directly assigned + /// + /// Specifiying nothing will return all permissions for all nodes + /// An enumerable list of + IEnumerable GetPermissions(IUserGroup group, bool directlyAssignedOnly, params int[] nodeIds); + /// /// Gets the permissions for the provided user and path /// @@ -84,8 +96,12 @@ namespace Umbraco.Core.Services ///
/// User to check permissions for /// Path to check permissions for + /// + /// Flag indicating if we want to get just the permissions directly assigned for the group and path, + /// or fall back to the group's default permissions when nothing is directly assigned + /// /// String indicating permissions for provided user and path - string GetPermissionsForPath(IUserGroup group, string path); + string GetPermissionsForPath(IUserGroup group, string path, bool directlyAssignedOnly = true); /// /// Replaces the same permission set for a single group to any number of entities diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 45dc0c921d..81a71e0f05 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -767,7 +767,7 @@ namespace Umbraco.Core.Services var result = new List(); foreach (var group in user.Groups) { - foreach (var permission in GetPermissions(group, nodeIds)) + foreach (var permission in GetPermissions(group, false, nodeIds)) { AddOrAmendPermissionList(result, permission); } @@ -776,6 +776,39 @@ namespace Umbraco.Core.Services return result; } + /// + /// Get permissions set for a group and node Id + /// + /// Group to retrieve permissions for + /// + /// Flag indicating if we want to get just the permissions directly assigned for the group and path, + /// or fall back to the group's default permissions when nothing is directly assigned + /// + /// Specifiying nothing will return all permissions for all nodes + /// An enumerable list of + public IEnumerable GetPermissions(IUserGroup group, bool directlyAssignedOnly, params int[] nodeIds) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) + { + var explicitPermissions = repository.GetPermissionsForEntities(group.Id, nodeIds); + var result = new List(explicitPermissions); + + // If requested, and no permissions are assigned to a particular node, then we will fill in those permissions with the group's defaults + if (directlyAssignedOnly == false) + { + var missingIds = nodeIds.Except(result.Select(x => x.EntityId)).ToList(); + if (missingIds.Any()) + { + result.AddRange(missingIds + .Select(i => new EntityPermission(i, group.Permissions.ToArray()))); + } + } + + return result; + } + } + /// /// For an existing list of , takes a new and aggregates it. /// If a permission for the entity associated with the new permission already exists, it's updated with those permissions to create a distinct, most permissive set. @@ -823,7 +856,7 @@ namespace Umbraco.Core.Services private IEnumerable GetPermissionsForGroupsAndPath(IEnumerable groups, string path) { return groups - .Select(g => GetPermissionsForPath(g, path)) + .Select(g => GetPermissionsForPath(g, path, directlyAssignedOnly: false)) .ToList(); } @@ -844,11 +877,19 @@ namespace Umbraco.Core.Services /// /// User to check permissions for /// Path to check permissions for + /// + /// Flag indicating if we want to get just the permissions directly assigned for the group and path, + /// or fall back to the group's default permissions when nothing is directly assigned + /// /// String indicating permissions for provided user and path - public string GetPermissionsForPath(IUserGroup group, string path) + public string GetPermissionsForPath(IUserGroup group, string path, bool directlyAssignedOnly = true) { var nodeId = GetNodeIdFromPath(path); - return string.Join(string.Empty, GetPermissions(group, nodeId).Single().AssignedPermissions); + var permission = GetPermissions(group, directlyAssignedOnly, nodeId) + .SingleOrDefault(); + return permission != null + ? string.Join(string.Empty, permission.AssignedPermissions) + : string.Empty; } /// @@ -863,32 +904,6 @@ namespace Umbraco.Core.Services : int.Parse(path); } - /// - /// Get permissions set for a group and node Id - /// - /// Group to retrieve permissions for - /// Specifiying nothing will return all permissions for all nodes - /// An enumerable list of - private IEnumerable GetPermissions(IUserGroup group, params int[] nodeIds) - { - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateUserGroupRepository(uow)) - { - var explicitPermissions = repository.GetPermissionsForEntities(group.Id, nodeIds); - var result = new List(explicitPermissions); - - // If no permissions are assigned to a particular node then we will fill in those permissions with the group's defaults - var missingIds = nodeIds.Except(result.Select(x => x.EntityId)).ToList(); - if (missingIds.Any()) - { - result.AddRange(missingIds - .Select(i => new EntityPermission(i, group.Permissions.ToArray()))); - } - - return result; - } - } - private static bool IsNotNullActionPermission(EntityPermission x) { const string NullActionChar = "-"; diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index 795088f48f..8992eebd95 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -93,7 +93,72 @@ namespace Umbraco.Tests.Services Assert.AreEqual(2, permissions.ElementAt(1).AssignedPermissions.Count()); Assert.AreEqual(1, permissions.ElementAt(2).AssignedPermissions.Count()); } - + + [Test] + public void UserService_Get_UserGroup_Assigned_Permissions() + { + // Arrange + var userService = ServiceContext.UserService; + var userGroup = CreateTestUserGroup(); + + var contentType = MockedContentTypes.CreateSimpleContentType(); + ServiceContext.ContentTypeService.Save(contentType); + var content = new[] + { + MockedContent.CreateSimpleContent(contentType), + MockedContent.CreateSimpleContent(contentType), + MockedContent.CreateSimpleContent(contentType) + }; + ServiceContext.ContentService.Save(content); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionBrowse.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionDelete.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionMove.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionBrowse.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionDelete.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(2), ActionBrowse.Instance.Letter, new int[] { userGroup.Id }); + + // Act + var permissions = userService.GetPermissions(userGroup, true, content.ElementAt(0).Id, content.ElementAt(1).Id, content.ElementAt(2).Id); + + //assert + Assert.AreEqual(3, permissions.Count()); + Assert.AreEqual(3, permissions.ElementAt(0).AssignedPermissions.Count()); + Assert.AreEqual(2, permissions.ElementAt(1).AssignedPermissions.Count()); + Assert.AreEqual(1, permissions.ElementAt(2).AssignedPermissions.Count()); + } + + [Test] + public void UserService_Get_UserGroup_Assigned_And_Default_Permissions() + { + // Arrange + var userService = ServiceContext.UserService; + var userGroup = CreateTestUserGroup(); + + var contentType = MockedContentTypes.CreateSimpleContentType(); + ServiceContext.ContentTypeService.Save(contentType); + var content = new[] + { + MockedContent.CreateSimpleContent(contentType), + MockedContent.CreateSimpleContent(contentType), + MockedContent.CreateSimpleContent(contentType) + }; + ServiceContext.ContentService.Save(content); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionBrowse.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionDelete.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(0), ActionMove.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionBrowse.Instance.Letter, new int[] { userGroup.Id }); + ServiceContext.ContentService.AssignContentPermission(content.ElementAt(1), ActionDelete.Instance.Letter, new int[] { userGroup.Id }); + + // Act + var permissions = userService.GetPermissions(userGroup, false, content.ElementAt(0).Id, content.ElementAt(1).Id, content.ElementAt(2).Id); + + //assert + Assert.AreEqual(3, permissions.Count()); + Assert.AreEqual(3, permissions.ElementAt(0).AssignedPermissions.Count()); + Assert.AreEqual(2, permissions.ElementAt(1).AssignedPermissions.Count()); + Assert.AreEqual(17, permissions.ElementAt(2).AssignedPermissions.Count()); + } + [Test] public void Can_Delete_User() { @@ -524,6 +589,17 @@ namespace Umbraco.Tests.Services } private IUser CreateTestUser() + { + var userGroup = CreateTestUserGroup(); + + var user = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com"); + user.AddGroup(userGroup); + user.SetGroupsLoaded(); + ServiceContext.UserService.Save(user); + return user; + } + + private UserGroup CreateTestUserGroup() { var userGroup = new UserGroup { @@ -535,12 +611,7 @@ namespace Umbraco.Tests.Services ServiceContext.UserService.SaveUserGroup(userGroup); ServiceContext.UserService.AddSectionToAllUserGroups("content", 1); ServiceContext.UserService.AddSectionToAllUserGroups("media", 1); - - var user = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com"); - user.AddGroup(userGroup); - user.SetGroupsLoaded(); - ServiceContext.UserService.Save(user); - return user; + return userGroup; } } } diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs index 6f9cc7db80..861d5e6abf 100644 --- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs @@ -2,9 +2,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models.Membership; - using Umbraco.Core.Persistence.Repositories; -using umbraco.interfaces; namespace Umbraco.Web.Cache { @@ -31,8 +29,6 @@ namespace Umbraco.Web.Cache public override void RefreshAll() { ClearAllIsolatedCacheByEntityType(); - if (UserPermissionsCache) - UserPermissionsCache.Result.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey); base.RefreshAll(); } @@ -47,17 +43,8 @@ namespace Umbraco.Web.Cache var userCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); if (userCache) userCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); - - if (UserPermissionsCache) - UserPermissionsCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); - + base.Remove(id); } - - private Attempt UserPermissionsCache - { - get { return ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); } - } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs index 60b5c9a710..5f36f60bab 100644 --- a/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserGroupCacheRefresher.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.Cache private Attempt UserGroupPermissionsCache { - get { return ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); } + get { return ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); } } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs index cf091b6618..8ea1b26f47 100644 --- a/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserGroupPermissionsCacheRefresher.cs @@ -52,7 +52,7 @@ namespace Umbraco.Web.Cache private Attempt UserGroupPermissionsCache { - get { return ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); } + get { return ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); } } } } \ No newline at end of file From 8bb3685e5deb3db23736fb82d8a2f4328c7f6532 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 30 Oct 2016 13:53:43 +0100 Subject: [PATCH 11/15] Fixed test failure reported on CI --- src/Umbraco.Core/Services/SectionService.cs | 4 +++- src/Umbraco.Tests/Services/SectionServiceTests.cs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index b12412b5f2..02c0b8c14a 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -211,7 +211,9 @@ namespace Umbraco.Core.Services /// The application icon, which has to be located in umbraco/images/tray folder. public void MakeNew(string name, string alias, string icon) { - MakeNew(name, alias, icon, GetSections().Max(x => x.SortOrder) + 1); + var sections = GetSections(); + var nextSortOrder = sections != null ? GetSections().Max(x => x.SortOrder) + 1 : 1; + MakeNew(name, alias, icon, nextSortOrder); } /// diff --git a/src/Umbraco.Tests/Services/SectionServiceTests.cs b/src/Umbraco.Tests/Services/SectionServiceTests.cs index 5c51233892..556203bb61 100644 --- a/src/Umbraco.Tests/Services/SectionServiceTests.cs +++ b/src/Umbraco.Tests/Services/SectionServiceTests.cs @@ -29,6 +29,7 @@ namespace Umbraco.Tests.Services ServiceContext.SectionService.MakeNew("Settings", "settings", "icon-settings"); ServiceContext.SectionService.MakeNew("Developer", "developer", "icon-developer"); } + [Test] public void SectionService_Can_Get_Allowed_Sections_For_User() { From ece61a45d8fb35546a0be4be03fb2c287eecc378 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 30 Oct 2016 14:16:01 +0100 Subject: [PATCH 12/15] Fixed test failure reported on CI (2) --- src/Umbraco.Core/Services/SectionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index 02c0b8c14a..87447c06f0 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -212,7 +212,7 @@ namespace Umbraco.Core.Services public void MakeNew(string name, string alias, string icon) { var sections = GetSections(); - var nextSortOrder = sections != null ? GetSections().Max(x => x.SortOrder) + 1 : 1; + var nextSortOrder = sections != null ? sections.Max(x => x.SortOrder) + 1 : 1; MakeNew(name, alias, icon, nextSortOrder); } From d7aba9ce4a80c87c67d6cbfd79b8c1f7a330a624 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 30 Oct 2016 16:10:00 +0100 Subject: [PATCH 13/15] Fixed test failure reported on CI (3) --- src/Umbraco.Core/Services/SectionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index 87447c06f0..9de793e37d 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -212,7 +212,7 @@ namespace Umbraco.Core.Services public void MakeNew(string name, string alias, string icon) { var sections = GetSections(); - var nextSortOrder = sections != null ? sections.Max(x => x.SortOrder) + 1 : 1; + var nextSortOrder = sections != null && sections.Any() ? sections.Max(x => x.SortOrder) + 1 : 1; MakeNew(name, alias, icon, nextSortOrder); } From f266c7891563e93b30f6442bd69eb65465055ce4 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Mon, 5 Dec 2016 22:26:09 +0100 Subject: [PATCH 14/15] Fixed issues with user creation and deletion --- .../AddUserGroupTables.cs | 13 ++++-- .../Repositories/UserGroupRepository.cs | 1 + .../Repositories/UserRepository.cs | 4 ++ src/Umbraco.Core/Services/UserService.cs | 2 +- .../umbraco/config/create/UI.xml | 8 ---- src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../umbraco/users/EditUser.aspx.cs | 2 - .../umbraco/users/UserGroupTasks.cs | 42 +++++++++++++++++++ 8 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/users/UserGroupTasks.cs diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs index 603517bbe3..ea566c0238 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddUserGroupTables.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.SqlSyntax; @@ -16,11 +17,12 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero public override void Up() { var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray(); + var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); AddNewTables(tables); MigrateUserPermissions(); MigrateUserTypesToGroups(); - DeleteOldTables(tables); + DeleteOldTables(tables, constraints); } private void AddNewTables(string[] tables) @@ -162,7 +164,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero )"); } - private void DeleteOldTables(string[] tables) + private void DeleteOldTables(string[] tables, Tuple[] constraints) { if (tables.InvariantContains("umbracoUser2App")) { @@ -176,6 +178,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero if (tables.InvariantContains("umbracoUserType") && tables.InvariantContains("umbracoUser")) { + if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id"))) + { + Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser"); + } + Delete.Column("userType").FromTable("umbracoUser"); Delete.Table("umbracoUserType"); } diff --git a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs index b13033c7b3..d214c0d555 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs @@ -194,6 +194,7 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { + "DELETE FROM umbracoUser2UserGroup WHERE userGroupId = @Id", "DELETE FROM umbracoUserGroup2App WHERE userGroupId = @Id", "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @Id", "DELETE FROM umbracoUserGroup WHERE id = @Id" diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index cad213d1a0..1bc86c1683 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -284,6 +284,10 @@ namespace Umbraco.Core.Persistence.Repositories /// An enumerable list of public IEnumerable GetGroupsForUser(int userId) { + var tables = SqlSyntax.GetTablesInSchema(ApplicationContext.Current.DatabaseContext.Database).ToArray(); + if (tables.InvariantContains("umbracoUserGroup") == false) + return new List(); + var sql = new Sql(); sql.Select("*") .From() diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 81a71e0f05..9499a142d0 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -321,7 +321,7 @@ namespace Umbraco.Core.Services public string GetDefaultMemberType() { // User types now being removed, there is no default user type to return - throw new NotImplementedException(); + return "None"; } /// diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml index ba3df007e7..3edb5fcedc 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml @@ -213,14 +213,6 @@ - -
User Types
- /create/simple.ascx - - - - -
User Groups
/create/simple.ascx diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c2b5665f4c..411657f5fc 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -429,6 +429,7 @@ ASPXCodeBehind + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs index 83c30ddc25..6d7c4056fd 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -39,7 +39,6 @@ namespace umbraco.cms.presentation.user protected TextBox lname = new TextBox(); protected PlaceHolder passw = new PlaceHolder(); protected TextBox email = new TextBox(); - protected DropDownList userType = new DropDownList(); protected DropDownList userLanguage = new DropDownList(); protected CheckBox NoConsole = new CheckBox(); protected CheckBox Disabled = new CheckBox(); @@ -162,7 +161,6 @@ namespace umbraco.cms.presentation.user pp.addProperty(ui.Text("user", "loginname", UmbracoUser), lname); pp.addProperty(ui.Text("user", "password", UmbracoUser), passw); pp.addProperty(ui.Text("email", UmbracoUser), email); - pp.addProperty(ui.Text("user", "usertype", UmbracoUser), userType); pp.addProperty(ui.Text("user", "language", UmbracoUser), userLanguage); // Media / content root nodes diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserGroupTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserGroupTasks.cs new file mode 100644 index 0000000000..0aae75d278 --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserGroupTasks.cs @@ -0,0 +1,42 @@ +using Umbraco.Web.UI; +using umbraco.BusinessLogic; + +namespace umbraco.cms.presentation.user +{ + public class UserGroupTasks : LegacyDialogTask + { + public override bool PerformSave() + { + try + { + var u = UserGroup.MakeNew(Alias, "", Alias); + _returnUrl = string.Format("users/EditUserGroup.aspx?id={0}", u.Id); + return true; + } + catch + { + return false; + } + } + + public override bool PerformDelete() + { + var userGroup = UserGroup.GetUserGroup(ParentID); + if (userGroup == null) + return false; + userGroup.Delete(); + return true; + } + + private string _returnUrl = ""; + public override string ReturnUrl + { + get { return _returnUrl; } + } + + public override string AssignedApp + { + get { return DefaultApps.users.ToString(); } + } + } +} \ No newline at end of file From 11b97c5291d01d292ffb02a4479b8a197f1cd379 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Thu, 20 Apr 2017 09:45:48 +0100 Subject: [PATCH 15/15] Removed unnecessary null check --- src/Umbraco.Core/Services/SectionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index 9de793e37d..dcfe3748ec 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -212,7 +212,7 @@ namespace Umbraco.Core.Services public void MakeNew(string name, string alias, string icon) { var sections = GetSections(); - var nextSortOrder = sections != null && sections.Any() ? sections.Max(x => x.SortOrder) + 1 : 1; + var nextSortOrder = sections.Any() ? sections.Max(x => x.SortOrder) + 1 : 1; MakeNew(name, alias, icon, nextSortOrder); }