* Add key to UserGroupDto * Fix renaming table in sqlite The SqliteSyntaxProvider needed an overload to use the correct query * Start work on user group GUID migration * Add key index to UserGroupDto * Copy over data when migrating sqlite * Make sqlite column migration work * Remove PostMigrations These should be replaced with Notification usage * Remove outer scope from Upgrader * Remove unececary null check * Add marker base class for migrations * Enable scopeless migrations * Remove unnecessary state check The final state of the migration is no longer necessarily the final state of the plan. * Extend ExecutedMigrationPlan * Ensure that MigrationPlanExecutor.Execute always returns a result. * Always save final state, regardless of errors * Remove obsolete Execute * Add Umbraco specific migration notification * Publish notification after umbraco migration * Throw the exception that failed a migration after publishing notification * Handle notification publishing in DatabaseBuilder * Fix tests * Remember to complete scope * Clean up MigrationPlanExecutor * Run each package migration in a separate scope * Add PartialMigrationsTests * Add unhappy path test * Fix bug shown by test * Move PartialMigrationsTests into the correct folder * Comment out refresh cache in data type migration Need to add this back again as a notification handler or something. * Start working on a notification test * Allow migrations to request a cache rebuild * Set RebuildCache from MigrateDataTypeConfigurations * Clean MigrationPlanExecutor * Add comment explaining the need to partial migration success * Fix tests * Allow overriding DefinePlan of UmbracoPlan This is needed to test the DatabaseBuilder * Fix notification test * Don't throw exception to be immediately re-caught * Assert that scopes notification are always published * Ensure that scopes are created when requested * Make test classes internal. It doesn't really matter, but this way it doesn't show up in intellisense * Add notification handler for clearing cookies * Add CompatibilitySuppressions * Use unscoped migration for adding GUID to user group * Make sqlite migration work It's really not pretty, square peg, round hole. * Don't re-enable foreign keys This will happen automatically next time a connection is started. * Scope database when using SQLServer * Don't call complete transaction * Tidy up a couple of comment * Only allow scoping the database from UnscopedMigrationBase * Fix comment * Remove remark in UnscopedMigrationBase as it's no longer true * Add keys when creating default user groups * Map database value from DTO to entity * Fix migration Rename also renamed the foreign keys, making it not work * Make migration idempotent * Fix unit test * Update CompatibilitySuppressions.xml * Add GetUserGroupByKey to UserService * Add ByKey endpoint * Add UniqueId to AppendGroupBy Otherwise MSSQL grenades * Ensure that languages are returned by PerformGetByQuery * add POC displaying model * Clean up by key controller * Add GetAllEndpoint * Add delete endpoint * Use GetKey to get GUID from id Instead of pulling up the entire entity. * Add UserGroup2Permission table * Fetch the new permissions when getting user groups * Dont ToString int to parse it to a short I'm pretty sure this is some way old migration type code that doesn't make any sense anymore * Add new relation to GetDeleteClauses * Persist the permissions * Split UserGroupViewModel into multiple models This is to make it possible to make endpoints more rest-ish * Bootstrap create and update endpoints * Make GetAllUserGroupController paged * Add method to create IUserGroup from UserGroupSaveModel * Add sanity check version of endpoint * Fix persisting permissions * Map section aliases to the name the frontend expects This is a temporary fix till we find out how we really want to handle this * Fix up post merge * Make naming more consistent * Implement initial update endpoint * Fix media start node * Clean name for XSS when mapping to IUserGroup * Use a set instead of a list for permission names We don't want dupes * Make permission column nvarchar max * Add UserGroupOperationStatuses * Add IUserGroupAuthorizationService * Add specific user group creation method to user service * Move validating and authorizing into its own methods * Add operation result to action result mapping * Update create controller to use the create method * Fix create end point * Comment out getting current user untill we have auth * Add usergroup service * Obsolete usergroup things from IUserService * Add update to UserGroupService interface * User IUserGroupService in controllers * User async notifications overloads * Move authorize user group creation into its own service * Add AuthorizeUserGroupUpdate method * Make new service implementations internal and sealed * Add update user * Add GetAll to usergroup service * Remove or obsolete usages of GetAllUserGroups * Add usergroup service to DI * Remove usage of GetGroupsByAlias * Remove usages of GetUserGroupByAlias * Remove usage of GetUserGroupById * Add new table when creating a new database * Implement Delete * Add skip and take to getall * Move skip take into the service * Fixup suggestions in user group service * Fixup unit tests * Allow admins to change user groups they're not a part of * Add CompatibilitySuppressions * Update openapi * Uppdate OpenApi.json again * Add missing compatibility suppression * Added missing type info in ProducesResponseTypeAttribute * Added INamedEntityViewModel and added on the relevant view models * Fixed bug, resulting in serialization not being the same as swagger reported. Now all types objects implementing an interface, is serialized with the $type property * updated OpenApi.json * Added missing title in notfound response * Typo * .Result to .GetAwaiter().GetResult() * Update comment to mention it should be implemented on CurrentUserController * Validate that start nodes actually exists * Handle not found consistently * Use iso codes instead of ids * Update OpenAPI * Automatically infer statuscode in problemdetails * Ensure that the language exists * Fix usergroup 2 permission index * Validate that group name and alias is not too long * Only return status from validation We're just returning the same usergroups, and this is less boilerplate code * Handle empty and null group names * Remove group prefix from statuses * Add some basic validation tests * Don't allow updating a usergroup to having a duplicate alias --------- Co-authored-by: Bjarke Berg <mail@bergmania.dk>
191 lines
5.5 KiB
C#
191 lines
5.5 KiB
C#
using System.Collections;
|
|
using System.Runtime.Serialization;
|
|
using Umbraco.Cms.Core.Models.Entities;
|
|
using Umbraco.Cms.Core.Strings;
|
|
using Umbraco.Extensions;
|
|
|
|
namespace Umbraco.Cms.Core.Models.Membership;
|
|
|
|
/// <summary>
|
|
/// Represents a Group for a Backoffice User
|
|
/// </summary>
|
|
[Serializable]
|
|
[DataContract(IsReference = true)]
|
|
public class UserGroup : EntityBase, IUserGroup, IReadOnlyUserGroup
|
|
{
|
|
// Custom comparer for enumerable
|
|
private static readonly DelegateEqualityComparer<IEnumerable<string>> _stringEnumerableComparer =
|
|
new(
|
|
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
|
|
enum1 => enum1.GetHashCode());
|
|
|
|
private readonly IShortStringHelper _shortStringHelper;
|
|
private string _alias;
|
|
private string? _icon;
|
|
private string _name;
|
|
private bool _hasAccessToAllLanguages;
|
|
private IEnumerable<string>? _permissions;
|
|
private ISet<string> _permissionNames = new HashSet<string>();
|
|
private List<string> _sectionCollection;
|
|
private List<int> _languageCollection;
|
|
private int? _startContentId;
|
|
private int? _startMediaId;
|
|
|
|
/// <summary>
|
|
/// Constructor to create a new user group
|
|
/// </summary>
|
|
public UserGroup(IShortStringHelper shortStringHelper)
|
|
{
|
|
_alias = string.Empty;
|
|
_name = string.Empty;
|
|
_shortStringHelper = shortStringHelper;
|
|
_sectionCollection = new List<string>();
|
|
_languageCollection = new List<int>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor to create an existing user group
|
|
/// </summary>
|
|
/// <param name="userCount"></param>
|
|
/// <param name="alias"></param>
|
|
/// <param name="name"></param>
|
|
/// <param name="permissions"></param>
|
|
/// <param name="icon"></param>
|
|
/// <param name="shortStringHelper"></param>
|
|
public UserGroup(
|
|
IShortStringHelper shortStringHelper,
|
|
int userCount,
|
|
string? alias,
|
|
string? name,
|
|
IEnumerable<string> permissions,
|
|
string? icon)
|
|
: this(shortStringHelper)
|
|
{
|
|
UserCount = userCount;
|
|
_alias = alias ?? string.Empty;
|
|
_name = name ?? string.Empty;
|
|
_permissions = permissions;
|
|
_icon = icon;
|
|
}
|
|
|
|
[DataMember]
|
|
public int? StartMediaId
|
|
{
|
|
get => _startMediaId;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _startMediaId, nameof(StartMediaId));
|
|
}
|
|
|
|
[DataMember]
|
|
public int? StartContentId
|
|
{
|
|
get => _startContentId;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _startContentId, nameof(StartContentId));
|
|
}
|
|
|
|
[DataMember]
|
|
public string? Icon
|
|
{
|
|
get => _icon;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _icon, nameof(Icon));
|
|
}
|
|
|
|
[DataMember]
|
|
public string Alias
|
|
{
|
|
get => _alias;
|
|
set => SetPropertyValueAndDetectChanges(
|
|
value.ToCleanString(_shortStringHelper, CleanStringType.Alias | CleanStringType.UmbracoCase), ref _alias!,
|
|
nameof(Alias));
|
|
}
|
|
|
|
[DataMember]
|
|
public string? Name
|
|
{
|
|
get => _name;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _name!, nameof(Name));
|
|
}
|
|
|
|
[DataMember]
|
|
public bool HasAccessToAllLanguages
|
|
{
|
|
get => _hasAccessToAllLanguages;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _hasAccessToAllLanguages, nameof(HasAccessToAllLanguages));
|
|
}
|
|
|
|
/// <summary>
|
|
/// The set of default permissions for the user group
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
[DataMember]
|
|
public IEnumerable<string>? Permissions
|
|
{
|
|
get => _permissions;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _permissions, nameof(Permissions), _stringEnumerableComparer);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ISet<string> PermissionNames
|
|
{
|
|
get => _permissionNames;
|
|
set => SetPropertyValueAndDetectChanges(value, ref _permissionNames!, nameof(PermissionNames), _stringEnumerableComparer);
|
|
}
|
|
|
|
public IEnumerable<string> AllowedSections => _sectionCollection;
|
|
|
|
public int UserCount { get; }
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<int> AllowedLanguages
|
|
{
|
|
get => _languageCollection;
|
|
}
|
|
|
|
public void RemoveAllowedLanguage(int languageId)
|
|
{
|
|
if (_languageCollection.Contains(languageId))
|
|
{
|
|
_languageCollection.Remove(languageId);
|
|
}
|
|
}
|
|
|
|
public void AddAllowedLanguage(int languageId)
|
|
{
|
|
if (_languageCollection.Contains(languageId) == false)
|
|
{
|
|
_languageCollection.Add(languageId);
|
|
}
|
|
}
|
|
|
|
public void ClearAllowedLanguages() => _languageCollection.Clear();
|
|
|
|
public void ClearAllowedSections() => _sectionCollection.Clear();
|
|
|
|
protected override void PerformDeepClone(object clone)
|
|
{
|
|
base.PerformDeepClone(clone);
|
|
|
|
var clonedEntity = (UserGroup)clone;
|
|
|
|
// manually clone the start node props
|
|
clonedEntity._sectionCollection = new List<string>(_sectionCollection);
|
|
}
|
|
}
|