diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs index 57b3f79f5..649926b55 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs @@ -12,9 +12,9 @@ namespace Squidex.Domain.Apps.Core.Apps { public sealed class AppClient { - private readonly string secret; private readonly string name; - private readonly AppClientPermission permission; + private readonly string secret; + private readonly string role; public string Name { @@ -26,28 +26,28 @@ namespace Squidex.Domain.Apps.Core.Apps get { return secret; } } - public AppClientPermission Permission + public string Role { - get { return permission; } + get { return role; } } - public AppClient(string name, string secret, AppClientPermission permission) + public AppClient(string name, string secret, string role) { Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrEmpty(secret, nameof(secret)); - Guard.Enum(permission, nameof(permission)); + Guard.NotNullOrEmpty(role, nameof(role)); this.name = name; + this.role = role; this.secret = secret; - this.permission = permission; } [Pure] - public AppClient Update(AppClientPermission newPermission) + public AppClient Update(string newRole) { - Guard.Enum(newPermission, nameof(newPermission)); + Guard.NotNullOrEmpty(newRole, nameof(newRole)); - return new AppClient(name, secret, newPermission); + return new AppClient(name, secret, newRole); } [Pure] @@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Core.Apps { Guard.NotNullOrEmpty(newName, nameof(newName)); - return new AppClient(newName, secret, permission); + return new AppClient(newName, secret, role); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs index 5af29a523..425c14dbf 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs @@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Apps { Guard.NotNullOrEmpty(id, nameof(id)); - return new AppClients(Inner.Add(id, new AppClient(id, secret, AppClientPermission.Editor))); + return new AppClients(Inner.Add(id, new AppClient(id, secret, Role.Editor))); } [Pure] @@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Core.Apps } [Pure] - public AppClients Update(string id, AppClientPermission permission) + public AppClients Update(string id, string role) { Guard.NotNullOrEmpty(id, nameof(id)); @@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Apps return this; } - return new AppClients(Inner.SetItem(id, client.Update(permission))); + return new AppClients(Inner.SetItem(id, client.Update(role))); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs index be224b206..e3bd81ab4 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs @@ -11,27 +11,27 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Apps { - public sealed class AppContributors : DictionaryWrapper + public sealed class AppContributors : DictionaryWrapper { public static readonly AppContributors Empty = new AppContributors(); private AppContributors() - : base(ImmutableDictionary.Empty) + : base(ImmutableDictionary.Empty) { } - public AppContributors(ImmutableDictionary inner) + public AppContributors(ImmutableDictionary inner) : base(inner) { } [Pure] - public AppContributors Assign(string contributorId, AppContributorPermission permission) + public AppContributors Assign(string contributorId, string role) { Guard.NotNullOrEmpty(contributorId, nameof(contributorId)); - Guard.Enum(permission, nameof(permission)); + Guard.NotNullOrEmpty(role, nameof(role)); - return new AppContributors(Inner.SetItem(contributorId, permission)); + return new AppContributors(Inner.SetItem(contributorId, role)); } [Pure] diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs index 1f726ea95..b3955699f 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs @@ -17,7 +17,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json { protected override void WriteValue(JsonWriter writer, AppContributors value, JsonSerializer serializer) { - var json = new Dictionary(value.Count); + var json = new Dictionary(value.Count); foreach (var contributor in value) { @@ -29,7 +29,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json protected override AppContributors ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) { - var json = serializer.Deserialize>(reader); + var json = serializer.Deserialize>(reader); return new AppContributors(json.ToImmutableDictionary()); } diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonAppClient.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonAppClient.cs index 6a6b7a333..a3380c03c 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonAppClient.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonAppClient.cs @@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json public string Secret { get; set; } [JsonProperty] - public AppClientPermission Permission { get; set; } + public string Role { get; set; } public JsonAppClient() { @@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json public AppClient ToClient() { - return new AppClient(Name, Secret, Permission); + return new AppClient(Name, Secret, Role); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs new file mode 100644 index 000000000..6e47889e9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs @@ -0,0 +1,89 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; +using Squidex.Infrastructure.Security; +using System.Collections.Generic; +using P = Squidex.Shared.Permissions; + +namespace Squidex.Domain.Apps.Core.Apps +{ + public sealed class Role + { + public const string Editor = "Editor"; + public const string Developer = "Developer"; + public const string Owner = "Owner"; + public const string Reader = "Reader"; + + private static readonly HashSet DefaultRolesSet = new HashSet + { + Editor, + Developer, + Owner, + Reader + }; + + public string Name { get; } + + public PermissionSet Permissions { get; } + + public Role(string name, PermissionSet permissions) + { + Guard.NotNullOrEmpty(name, nameof(name)); + Guard.NotNull(permissions, nameof(permissions)); + + Name = name; + + Permissions = permissions; + } + + public Role(string name, params Permission[] permissions) + : this(name, new PermissionSet(permissions)) + { + } + + public static bool IsDefaultRole(string role) + { + return role != null && DefaultRolesSet.Contains(role); + } + + public static Role CreateOwner(string app) + { + return new Role(Owner, + P.ForApp(P.App, app)); + } + + public static Role CreateEditor(string app) + { + return new Role(Editor, + P.ForApp(P.AppCommon, app), + P.ForApp(P.AppContents, app), + P.ForApp(P.AppAssets, app)); + } + + public static Role CreateReader(string app) + { + return new Role(Reader, + P.ForApp(P.AppAssetsRead, app), + P.ForApp(P.AppCommon, app), + P.ForApp(P.AppContentsRead, app), + P.ForApp(P.AppContentsGraphQL, app)); + } + + public static Role CreateDeveloper(string app) + { + return new Role(Developer, + P.ForApp(P.AppApi, app), + P.ForApp(P.AppAssets, app), + P.ForApp(P.AppCommon, app), + P.ForApp(P.AppContents, app), + P.ForApp(P.AppPatterns, app), + P.ForApp(P.AppRules, app), + P.ForApp(P.AppSchemas, app)); + } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/RoleExtension.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/RoleExtension.cs deleted file mode 100644 index c765c6187..000000000 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/RoleExtension.cs +++ /dev/null @@ -1,77 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.Infrastructure; -using Squidex.Infrastructure.Security; -using Squidex.Shared; -using System.Linq; - -namespace Squidex.Domain.Apps.Core.Apps -{ - public static class RoleExtension - { - public static string[] ToPermissionIds(this AppClientPermission clientPermission, string app) - { - return clientPermission.ToPermissions(app).ToIds().ToArray(); - } - - public static string[] ToPermissionIds(this AppContributorPermission contributorPermission, string app) - { - return contributorPermission.ToPermissions(app).ToIds().ToArray(); - } - - public static PermissionSet ToPermissions(this AppClientPermission clientPermission, string app) - { - Guard.Enum(clientPermission, nameof(clientPermission)); - Guard.NotNullOrEmpty(app, nameof(app)); - - switch (clientPermission) - { - case AppClientPermission.Developer: - return ToPermissions(AppContributorPermission.Developer, app); - case AppClientPermission.Editor: - return ToPermissions(AppContributorPermission.Editor, app); - case AppClientPermission.Reader: - return new PermissionSet( - Permissions.ForApp(Permissions.AppCommon, app), - Permissions.ForApp(Permissions.AppContentsRead, app), - Permissions.ForApp(Permissions.AppContentsGraphQL, app)); - } - - return PermissionSet.Empty; - } - - public static PermissionSet ToPermissions(this AppContributorPermission contributorPermission, string app) - { - Guard.Enum(contributorPermission, nameof(contributorPermission)); - Guard.NotNullOrEmpty(app, nameof(app)); - - switch (contributorPermission) - { - case AppContributorPermission.Owner: - return new PermissionSet( - Permissions.ForApp(Permissions.App, app)); - case AppContributorPermission.Developer: - return new PermissionSet( - Permissions.ForApp(Permissions.AppApi, app), - Permissions.ForApp(Permissions.AppAssets, app), - Permissions.ForApp(Permissions.AppCommon, app), - Permissions.ForApp(Permissions.AppContents, app), - Permissions.ForApp(Permissions.AppPatterns, app), - Permissions.ForApp(Permissions.AppRules, app), - Permissions.ForApp(Permissions.AppSchemas, app)); - case AppContributorPermission.Editor: - return new PermissionSet( - Permissions.ForApp(Permissions.AppCommon, app), - Permissions.ForApp(Permissions.AppContents, app), - Permissions.ForApp(Permissions.AppAssets, app)); - } - - return PermissionSet.Empty; - } - } -} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs new file mode 100644 index 000000000..8f4a73590 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs @@ -0,0 +1,39 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Squidex.Domain.Apps.Core.Apps +{ + public sealed class Roles : DictionaryWrapper + { + public static readonly Roles Empty = new Roles(); + + private Roles() + : base(ImmutableDictionary.Empty) + { + } + + public Roles(ImmutableDictionary inner) + : base(inner) + { + } + + public static Roles CreateDefaults(string app) + { + return new Roles( + new Dictionary + { + [Role.Developer] = Role.CreateDeveloper(app), + [Role.Editor] = Role.CreateEditor(app), + [Role.Owner] = Role.CreateOwner(app), + [Role.Reader] = Role.CreateReader(app) + }.ToImmutableDictionary()); + } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Model/DefaultPermissions.cs b/src/Squidex.Domain.Apps.Core.Model/DefaultPermissions.cs deleted file mode 100644 index 8ac50d04d..000000000 --- a/src/Squidex.Domain.Apps.Core.Model/DefaultPermissions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Squidex.Domain.Apps.Core -{ - public sealed class DefaultPermissions - { - } -} diff --git a/src/Squidex.Domain.Apps.Entities/AppProvider.cs b/src/Squidex.Domain.Apps.Entities/AppProvider.cs index df424635c..06f964cb4 100644 --- a/src/Squidex.Domain.Apps.Entities/AppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/AppProvider.cs @@ -20,6 +20,7 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Orleans; +using Squidex.Infrastructure.Security; using Squidex.Shared; namespace Squidex.Domain.Apps.Entities @@ -159,9 +160,10 @@ namespace Squidex.Domain.Apps.Entities }); } - public Task> GetUserApps(string userId, string[] permissions) + public Task> GetUserApps(string userId, PermissionSet permissions) { Guard.NotNull(userId, nameof(userId)); + Guard.NotNull(permissions, nameof(permissions)); return localCache.GetOrCreateAsync($"GetUserApps({userId})", async () => { diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs index 1efd1e404..6055ce679 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs @@ -226,9 +226,9 @@ namespace Squidex.Domain.Apps.Entities.Apps RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed())); } - if (command.Permission.HasValue) + if (command.Role != null) { - RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value })); + RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Role = command.Role })); } } @@ -327,7 +327,7 @@ namespace Squidex.Domain.Apps.Entities.Apps private static AppContributorAssigned CreateInitialOwner(RefToken actor) { - return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner }; + return new AppContributorAssigned { ContributorId = actor.Identifier, Role = Role.Owner }; } protected override AppState OnEvent(Envelope @event) diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs index e8aee083d..2ac42e428 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Apps : base(typeNameRegistry) { AddEventMessage( - "assigned {user:[Contributor]} as {[Permission]}"); + "assigned {user:[Contributor]} as {[Role]}"); AddEventMessage( "removed {user:[Contributor]} from app"); @@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Entities.Apps return Task.FromResult( ForEvent(@event, channel) - .AddParameter("Contributor", @event.ContributorId).AddParameter("Permission", @event.Permission)); + .AddParameter("Contributor", @event.ContributorId).AddParameter("Role", @event.Role)); } protected Task On(AppClientAttached @event) diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs index c03cfd358..4fab08754 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Apps; +using Roles = Squidex.Domain.Apps.Core.Apps.Role; namespace Squidex.Domain.Apps.Entities.Apps.Commands { @@ -13,8 +13,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands { public string ContributorId { get; set; } - public bool FromRestore { get; set; } + public string Role { get; set; } = Roles.Developer; - public AppContributorPermission Permission { get; set; } + public bool FromRestore { get; set; } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs index 6002bba30..e5808d6eb 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Apps; - namespace Squidex.Domain.Apps.Entities.Apps.Commands { public sealed class UpdateClient : AppCommand @@ -15,6 +13,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands public string Name { get; set; } - public AppClientPermission? Permission { get; set; } + public string Role { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs index 68b6454e5..bbe63278e 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs @@ -58,14 +58,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards e("Client id is required.", nameof(command.Id)); } - if (string.IsNullOrWhiteSpace(command.Name) && command.Permission == null) + if (string.IsNullOrWhiteSpace(command.Name) && command.Role == null) { - e("Either name or permission must be defined.", nameof(command.Name), nameof(command.Permission)); + e("Either name or role must be defined.", nameof(command.Name), nameof(command.Role)); } - if (command.Permission.HasValue && !command.Permission.Value.IsEnumValue()) + if (command.Role != null && !Role.IsDefaultRole(command.Role)) { - e("Permission is not valid.", nameof(command.Permission)); + e("Role is not valid.", nameof(command.Role)); } if (client == null) @@ -78,9 +78,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards e("Client has already this name.", nameof(command.Name)); } - if (command.Permission == client.Permission) + if (command.Role == client.Role) { - e("Client has already this permission.", nameof(command.Permission)); + e("Client has already this role.", nameof(command.Role)); } }); } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs index bdeff29a6..cb90a0ddc 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs @@ -25,9 +25,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards return Validate.It(() => "Cannot assign contributor.", async e => { - if (!command.Permission.IsEnumValue()) + if (!Role.IsDefaultRole(command.Role)) { - e("Permission is not valid.", nameof(command.Permission)); + e("Role is not valid.", nameof(command.Role)); } if (string.IsNullOrWhiteSpace(command.ContributorId)) @@ -47,14 +47,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards if (string.Equals(command.ContributorId, command.Actor?.Identifier, StringComparison.OrdinalIgnoreCase) && !command.FromRestore) { - throw new SecurityException("You cannot change your own permission."); + throw new SecurityException("You cannot change your own role."); } if (contributors.TryGetValue(command.ContributorId, out var existing)) { - if (existing == command.Permission) + if (existing == command.Role) { - e("Contributor has already this permission.", nameof(command.Permission)); + e("Contributor has already this role.", nameof(command.Role)); } } else if (plan.MaxContributors == contributors.Count) @@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards e("Contributor id is required.", nameof(command.ContributorId)); } - var ownerIds = contributors.Where(x => x.Value == AppContributorPermission.Owner).Select(x => x.Key).ToList(); + var ownerIds = contributors.Where(x => x.Value == Role.Owner).Select(x => x.Key).ToList(); if (ownerIds.Count == 1 && ownerIds.Contains(command.ContributorId)) { diff --git a/src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs b/src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs index 73300f0d2..bf81d2612 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs @@ -17,6 +17,8 @@ namespace Squidex.Domain.Apps.Entities.Apps { string Name { get; } + Roles Roles { get; } + AppPlan Plan { get; } AppClients Clients { get; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs index 9a60918c9..087bebad9 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs @@ -22,6 +22,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.State [JsonProperty] public string Name { get; set; } + [JsonProperty] + public Roles Roles { get; set; } = Roles.Empty; + [JsonProperty] public AppPlan Plan { get; set; } @@ -42,6 +45,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.State protected void On(AppCreated @event) { + Roles = Roles.CreateDefaults(@event.Name); + SimpleMapper.Map(@event, this); } @@ -52,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.State protected void On(AppContributorAssigned @event) { - Contributors = Contributors.Assign(@event.ContributorId, @event.Permission); + Contributors = Contributors.Assign(@event.ContributorId, @event.Role); } protected void On(AppContributorRemoved @event) @@ -67,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.State protected void On(AppClientUpdated @event) { - Clients = Clients.Update(@event.Id, @event.Permission); + Clients = Clients.Update(@event.Id, @event.Role); } protected void On(AppClientRenamed @event) diff --git a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index b2f2081c8..7c90a1584 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -252,7 +252,7 @@ namespace Squidex.Domain.Apps.Entities.Backup AppId = CurrentJob.AppId, ContributorId = actor.Identifier, FromRestore = true, - Permission = AppContributorPermission.Developer + Role = Role.Developer }); } diff --git a/src/Squidex.Domain.Apps.Entities/IAppProvider.cs b/src/Squidex.Domain.Apps.Entities/IAppProvider.cs index ecc2be270..f40ca30ca 100644 --- a/src/Squidex.Domain.Apps.Entities/IAppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/IAppProvider.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Infrastructure.Security; namespace Squidex.Domain.Apps.Entities { @@ -28,6 +29,6 @@ namespace Squidex.Domain.Apps.Entities Task> GetRulesAsync(Guid appId); - Task> GetUserApps(string userId, string[] permissions); + Task> GetUserApps(string userId, PermissionSet permissions); } } diff --git a/src/Squidex.Domain.Apps.Events/Apps/AppClientUpdated.cs b/src/Squidex.Domain.Apps.Events/Apps/AppClientUpdated.cs index b60d6e1dd..e09e4f6cb 100644 --- a/src/Squidex.Domain.Apps.Events/Apps/AppClientUpdated.cs +++ b/src/Squidex.Domain.Apps.Events/Apps/AppClientUpdated.cs @@ -5,16 +5,15 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Events.Apps { - [EventType(nameof(AppClientUpdated))] + [EventType(nameof(AppClientUpdated), 2)] public sealed class AppClientUpdated : AppEvent { public string Id { get; set; } - public AppClientPermission Permission { get; set; } + public string Role { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Events/Apps/AppContributorAssigned.cs b/src/Squidex.Domain.Apps.Events/Apps/AppContributorAssigned.cs index e32fdd0f3..63f4bba59 100644 --- a/src/Squidex.Domain.Apps.Events/Apps/AppContributorAssigned.cs +++ b/src/Squidex.Domain.Apps.Events/Apps/AppContributorAssigned.cs @@ -5,16 +5,15 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Events.Apps { - [EventType(nameof(AppContributorAssigned))] + [EventType(nameof(AppContributorAssigned), 2)] public sealed class AppContributorAssigned : AppEvent { public string ContributorId { get; set; } - public AppContributorPermission Permission { get; set; } + public string Role { get; set; } } } diff --git a/src/Squidex.Infrastructure/Security/Permission.cs b/src/Squidex.Infrastructure/Security/Permission.cs index 19ebd77e0..57477dd29 100644 --- a/src/Squidex.Infrastructure/Security/Permission.cs +++ b/src/Squidex.Infrastructure/Security/Permission.cs @@ -18,7 +18,7 @@ namespace Squidex.Infrastructure.Security private static readonly char[] AlternativeSeparators = { '|' }; private readonly string description; private readonly string id; - private readonly HashSet[] idParts; + private readonly Lazy[]> idParts; public string Id { @@ -38,7 +38,7 @@ namespace Squidex.Infrastructure.Security this.id = id; - idParts = id + idParts = new Lazy[]>(() => id .Split(MainSeparators, StringSplitOptions.RemoveEmptyEntries) .Select(x => { @@ -51,7 +51,7 @@ namespace Squidex.Infrastructure.Security return new HashSet(alternatives, StringComparer.OrdinalIgnoreCase); }) - .ToArray(); + .ToArray()); } public bool Allows(Permission permission) @@ -61,17 +61,20 @@ namespace Squidex.Infrastructure.Security return false; } - if (idParts.Length > permission.idParts.Length) + var lhs = idParts.Value; + var rhs = permission.idParts.Value; + + if (lhs.Length > rhs.Length) { return false; } - for (var i = 0; i < idParts.Length; i++) + for (var i = 0; i < lhs.Length; i++) { - var lhs = idParts[i]; - var rhs = permission.idParts[i]; + var l = lhs[i]; + var r = rhs[i]; - if (lhs != null && (rhs == null || !lhs.Intersect(rhs).Any())) + if (l != null && (r == null || !l.Intersect(r).Any())) { return false; } @@ -87,12 +90,15 @@ namespace Squidex.Infrastructure.Security return false; } - for (var i = 0; i < Math.Min(idParts.Length, permission.idParts.Length); i++) + var lhs = idParts.Value; + var rhs = permission.idParts.Value; + + for (var i = 0; i < Math.Min(lhs.Length, rhs.Length); i++) { - var lhs = idParts[i]; - var rhs = permission.idParts[i]; + var l = lhs[i]; + var r = rhs[i]; - if (lhs != null && rhs != null && !lhs.Intersect(rhs).Any()) + if (l != null && r != null && !l.Intersect(r).Any()) { return false; } @@ -101,6 +107,11 @@ namespace Squidex.Infrastructure.Security return true; } + public bool StartsWith(string id) + { + return id.StartsWith(id, StringComparison.OrdinalIgnoreCase); + } + public override bool Equals(object obj) { return Equals(obj as Permission); diff --git a/src/Squidex.Shared/Permissions.cs b/src/Squidex.Shared/Permissions.cs index a2dea3c59..4a4c675ca 100644 --- a/src/Squidex.Shared/Permissions.cs +++ b/src/Squidex.Shared/Permissions.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; -using System.Collections.Generic; using System.Linq; using Squidex.Infrastructure; using Squidex.Infrastructure.Security; @@ -114,19 +112,20 @@ namespace Squidex.Shared return new Permission(id.Replace("{app}", app ?? "*").Replace("{name}", schema ?? "*")); } - public static string[] ToAppPermissionIds(this IEnumerable permissions, string app) + public static PermissionSet ToAppPermissions(this PermissionSet permissions, string app) { - var result = permissions.Where(x => x.StartsWith($"squidex.apps.{app}", StringComparison.OrdinalIgnoreCase)).ToArray(); + var matching = permissions.Where(x => x.StartsWith($"squidex.apps.{app}")); - return result; + return new PermissionSet(matching); } - public static string[] ToAppNames(this IEnumerable permissions) + public static string[] ToAppNames(this PermissionSet permissions) { + var matching = permissions.Where(x => x.StartsWith($"squidex.apps.")); + var result = - permissions - .Where(x => x.StartsWith("squidex.apps.", StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Split('.')) + matching + .Select(x => x.Id.Split('.')) .Select(x => x[2]) .Distinct() .ToArray(); diff --git a/src/Squidex.Shared/Users/UserExtensions.cs b/src/Squidex.Shared/Users/UserExtensions.cs index 2fdf9d681..b5cc7a123 100644 --- a/src/Squidex.Shared/Users/UserExtensions.cs +++ b/src/Squidex.Shared/Users/UserExtensions.cs @@ -9,6 +9,7 @@ using System; using System.Linq; using System.Security.Claims; using Squidex.Infrastructure; +using Squidex.Infrastructure.Security; using Squidex.Shared.Identity; namespace Squidex.Shared.Users @@ -100,14 +101,14 @@ namespace Squidex.Shared.Users return user.GetClaimValue(SquidexClaimTypes.DisplayName); } - public static string[] Permissions(this ClaimsPrincipal principal) + public static PermissionSet Permissions(this ClaimsPrincipal principal) { - return principal.Claims.Where(x => x.Type == SquidexClaimTypes.Permissions).Select(x => x.Value).ToArray(); + return new PermissionSet(principal.Claims.Where(x => x.Type == SquidexClaimTypes.Permissions).Select(x => new Permission(x.Value))); } - public static string[] Permissions(this IUser user) + public static PermissionSet Permissions(this IUser user) { - return user.GetClaimValues(SquidexClaimTypes.Permissions); + return new PermissionSet(user.GetClaimValues(SquidexClaimTypes.Permissions).Select(x => new Permission(x))); } public static string GetClaimValue(this IUser user, string type) diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs index 0bb6b4eb2..792296462 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs @@ -48,7 +48,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models var response = new AppCreatedDto { Id = result.IdOrValue.ToString(), - Permissions = AppContributorPermission.Owner.ToPermissions(name).Select(x => x.Id).ToArray(), + Permissions = Role.CreateOwner(name).Permissions.ToIds().ToArray(), PlanName = apps.GetPlan(null)?.Name, PlanUpgrade = apps.GetPlanUpgrade(null)?.Name, Version = result.Version diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs index 370c19558..dfbeec425 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs @@ -6,13 +6,15 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using Newtonsoft.Json; using NodaTime; -using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Infrastructure.Reflection; +using Squidex.Infrastructure.Security; using Squidex.Pipeline; using Squidex.Shared; @@ -62,19 +64,23 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// public string PlanUpgrade { get; set; } - public static AppDto FromApp(IAppEntity app, string userId, string[] permissions, IAppPlansProvider plans) + public static AppDto FromApp(IAppEntity app, string userId, PermissionSet userPermissions, IAppPlansProvider plans) { - var response = SimpleMapper.Map(app, new AppDto()); + var permissions = new List(); - if (app.Contributors.TryGetValue(userId, out var role)) + if (app.Contributors.TryGetValue(userId, out var roleName) && app.Roles.TryGetValue(roleName, out var role)) { - response.Permissions = role.ToPermissionIds(app.Name); + permissions.AddRange(role.Permissions); } - else + + if (userPermissions != null) { - response.Permissions = permissions.ToAppPermissionIds(app.Name); + permissions.AddRange(userPermissions.ToAppPermissions(app.Name)); } + var response = SimpleMapper.Map(app, new AppDto()); + + response.Permissions = permissions.Select(x => x.Id).ToArray(); response.PlanName = plans.GetPlanForApp(app)?.Name; response.PlanUpgrade = plans.GetPlanUpgradeForApp(app)?.Name; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignAppContributorDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignAppContributorDto.cs index 988c36336..98cf7b9e5 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignAppContributorDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignAppContributorDto.cs @@ -7,8 +7,6 @@ using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure.Reflection; @@ -23,10 +21,9 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public string ContributorId { get; set; } /// - /// The permission level as a contributor. + /// The role of the contributor. /// - [JsonConverter(typeof(StringEnumConverter))] - public AppContributorPermission Permission { get; set; } + public string Role { get; set; } public AssignContributor ToCommand() { diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs index 81252db26..4fde2f7b1 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs @@ -8,10 +8,10 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure.Reflection; +using Roles = Squidex.Domain.Apps.Core.Apps.Role; namespace Squidex.Areas.Api.Controllers.Apps.Models { @@ -36,11 +36,9 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public string Name { get; set; } /// - /// The permissions of the client. + /// The role of the client. /// - [Required] - [JsonConverter(typeof(StringEnumConverter))] - public AppClientPermission Permission { get; set; } + public string Role { get; set; } public static ClientDto FromKvp(KeyValuePair kvp) { @@ -49,7 +47,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public static ClientDto FromCommand(AttachClient command) { - return SimpleMapper.Map(command, new ClientDto { Name = command.Id, Permission = AppClientPermission.Editor }); + return SimpleMapper.Map(command, new ClientDto { Name = command.Id, Role = Roles.Editor }); } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs index 522b4d2b4..0bc35ef83 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs @@ -7,8 +7,6 @@ using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Squidex.Domain.Apps.Core.Apps; namespace Squidex.Areas.Api.Controllers.Apps.Models { @@ -21,9 +19,8 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public string ContributorId { get; set; } /// - /// The permission level as a contributor. + /// The role of the contributor. /// - [JsonConverter(typeof(StringEnumConverter))] - public AppContributorPermission Permission { get; set; } + public string Role { get; set; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs index 2251382f8..a3352dd48 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs @@ -29,7 +29,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models { var plan = plans.GetPlanForApp(app); - var contributors = app.Contributors.Select(x => new ContributorDto { ContributorId = x.Key, Permission = x.Value }).ToArray(); + var contributors = app.Contributors.Select(x => new ContributorDto { ContributorId = x.Key, Role = x.Value }).ToArray(); return new ContributorsDto { Contributors = contributors, MaxContributors = plan.MaxContributors }; } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppClientDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppClientDto.cs index fa6e3a16c..944604cfd 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppClientDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppClientDto.cs @@ -6,9 +6,6 @@ // ========================================================================== using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure.Reflection; @@ -23,10 +20,9 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public string Name { get; set; } /// - /// The permissions of the client. + /// The role of the client. /// - [JsonConverter(typeof(StringEnumConverter))] - public AppClientPermission? Permission { get; set; } + public string Role { get; set; } public UpdateClient ToCommand(string clientId) { diff --git a/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs b/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs index 88732f8ac..bf048c0ba 100644 --- a/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs @@ -6,6 +6,7 @@ // ========================================================================== using System.ComponentModel.DataAnnotations; +using System.Linq; using Squidex.Infrastructure.Reflection; using Squidex.Shared.Users; @@ -45,7 +46,9 @@ namespace Squidex.Areas.Api.Controllers.Users.Models public static UserDto FromUser(IUser user) { - return SimpleMapper.Map(user, new UserDto { DisplayName = user.DisplayName(), Permissions = user.Permissions() }); + var permissions = user.Permissions().ToIds().ToArray(); + + return SimpleMapper.Map(user, new UserDto { DisplayName = user.DisplayName(), Permissions = permissions }); } } } diff --git a/src/Squidex/Pipeline/AppResolver.cs b/src/Squidex/Pipeline/AppResolver.cs index 29ce17fbd..ab52a55b6 100644 --- a/src/Squidex/Pipeline/AppResolver.cs +++ b/src/Squidex/Pipeline/AppResolver.cs @@ -10,7 +10,6 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Infrastructure.Security; @@ -61,7 +60,7 @@ namespace Squidex.Pipeline if (permissions.Count == 0) { - var set = new PermissionSet(user.Permissions().Select(x => new Permission(x))); + var set = user.Permissions(); if (!set.Includes(Permissions.ForApp(Permissions.App, appName))) { @@ -87,9 +86,9 @@ namespace Squidex.Pipeline { var clientId = user.GetClientId(); - if (clientId != null && app.Clients.TryGetValue(clientId, out var client)) + if (clientId != null && app.Clients.TryGetValue(clientId, out var client) && app.Roles.TryGetValue(client.Role, out var role)) { - return client.Permission.ToPermissions(app.Name); + return role.Permissions; } return PermissionSet.Empty; @@ -99,9 +98,9 @@ namespace Squidex.Pipeline { var subjectId = user.OpenIdSubject(); - if (subjectId != null && app.Contributors.TryGetValue(subjectId, out var permission)) + if (subjectId != null && app.Contributors.TryGetValue(subjectId, out var roleName) && app.Roles.TryGetValue(roleName, out var role)) { - return permission.ToPermissions(app.Name); + return role.Permissions; } return PermissionSet.Empty; diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs index 66fac6cec..5a3d8cb6d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps clients = clients.Add("3", "my-secret"); clients = clients.Add("4", "my-secret"); - clients = clients.Update("3", AppClientPermission.Editor); + clients = clients.Update("3", Role.Editor); clients = clients.Rename("3", "My Client 3"); clients = clients.Rename("2", "My Client 2"); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs index 1c5b606ef..4a97de100 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs @@ -23,15 +23,15 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var clients_1 = clients_0.Add("2", "my-secret"); - clients_1["2"].Should().BeEquivalentTo(new AppClient("2", "my-secret", AppClientPermission.Editor)); + clients_1["2"].Should().BeEquivalentTo(new AppClient("2", "my-secret", Role.Editor)); } [Fact] public void Should_assign_clients_with_permission() { - var clients_1 = clients_0.Add("2", new AppClient("my-name", "my-secret", AppClientPermission.Reader)); + var clients_1 = clients_0.Add("2", new AppClient("my-name", "my-secret", Role.Reader)); - clients_1["2"].Should().BeEquivalentTo(new AppClient("my-name", "my-secret", AppClientPermission.Reader)); + clients_1["2"].Should().BeEquivalentTo(new AppClient("my-name", "my-secret", Role.Reader)); } [Fact] @@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var clients_1 = clients_0.Rename("1", "new-name"); - clients_1["1"].Should().BeEquivalentTo(new AppClient("new-name", "my-secret", AppClientPermission.Editor)); + clients_1["1"].Should().BeEquivalentTo(new AppClient("new-name", "my-secret", Role.Editor)); } [Fact] @@ -61,15 +61,15 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_update_client() { - var client_1 = clients_0.Update("1", AppClientPermission.Reader); + var client_1 = clients_0.Update("1", Role.Reader); - client_1["1"].Should().BeEquivalentTo(new AppClient("1", "my-secret", AppClientPermission.Reader)); + client_1["1"].Should().BeEquivalentTo(new AppClient("1", "my-secret", Role.Reader)); } [Fact] public void Should_return_same_clients_if_client_to_update_not_found() { - var clients_1 = clients_0.Update("2", AppClientPermission.Reader); + var clients_1 = clients_0.Update("2", Role.Reader); Assert.Same(clients_0, clients_1); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs index 7b674ce74..2f42622f7 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs @@ -22,9 +22,9 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var contributors = AppContributors.Empty; - contributors = contributors.Assign("1", AppContributorPermission.Developer); - contributors = contributors.Assign("2", AppContributorPermission.Editor); - contributors = contributors.Assign("3", AppContributorPermission.Owner); + contributors = contributors.Assign("1", Role.Developer); + contributors = contributors.Assign("2", Role.Editor); + contributors = contributors.Assign("3", Role.Owner); var serialized = JToken.FromObject(contributors, serializer).ToObject(serializer); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsTests.cs index 3c6e17e51..faf082a3d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsTests.cs @@ -19,26 +19,26 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_assign_new_contributor() { - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Developer); - var contributors_2 = contributors_1.Assign("2", AppContributorPermission.Editor); + var contributors_1 = contributors_0.Assign("1", Role.Developer); + var contributors_2 = contributors_1.Assign("2", Role.Editor); - Assert.Equal(AppContributorPermission.Developer, contributors_2["1"]); - Assert.Equal(AppContributorPermission.Editor, contributors_2["2"]); + Assert.Equal(Role.Developer, contributors_2["1"]); + Assert.Equal(Role.Editor, contributors_2["2"]); } [Fact] public void Should_replace_contributor_if_already_exists() { - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Developer); - var contributors_2 = contributors_1.Assign("1", AppContributorPermission.Owner); + var contributors_1 = contributors_0.Assign("1", Role.Developer); + var contributors_2 = contributors_1.Assign("1", Role.Owner); - Assert.Equal(AppContributorPermission.Owner, contributors_2["1"]); + Assert.Equal(Role.Owner, contributors_2["1"]); } [Fact] public void Should_remove_contributor() { - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Developer); + var contributors_1 = contributors_0.Assign("1", Role.Developer); var contributors_2 = contributors_1.Remove("1"); Assert.Empty(contributors_2); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs index 38d9a4169..164e2130b 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs @@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Entities.Apps LastEvents .ShouldHaveSameEvents( CreateEvent(new AppCreated { Name = AppName }), - CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Permission = AppContributorPermission.Owner }), + CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Role = Role.Owner }), CreateEvent(new AppLanguageAdded { Language = Language.EN }), CreateEvent(new AppPatternAdded { PatternId = patternId1, Name = "Number", Pattern = "[0-9]" }), CreateEvent(new AppPatternAdded { PatternId = patternId2, Name = "Numbers", Pattern = "[0-9]*" }) @@ -158,7 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task AssignContributor_should_create_events_and_update_state() { - var command = new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }; + var command = new AssignContributor { ContributorId = contributorId, Role = Role.Editor }; await ExecuteCreateAsync(); @@ -166,11 +166,11 @@ namespace Squidex.Domain.Apps.Entities.Apps result.ShouldBeEquivalent(EntityCreatedResult.Create(contributorId, 5)); - Assert.Equal(AppContributorPermission.Editor, sut.Snapshot.Contributors[contributorId]); + Assert.Equal(Role.Editor, sut.Snapshot.Contributors[contributorId]); LastEvents .ShouldHaveSameEvents( - CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Permission = AppContributorPermission.Editor }) + CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Role = Role.Editor }) ); } @@ -236,7 +236,7 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task UpdateClient_should_create_events_and_update_state() { - var command = new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer }; + var command = new UpdateClient { Id = clientId, Name = clientNewName, Role = Role.Developer }; await ExecuteCreateAsync(); await ExecuteAttachClientAsync(); @@ -250,7 +250,7 @@ namespace Squidex.Domain.Apps.Entities.Apps LastEvents .ShouldHaveSameEvents( CreateEvent(new AppClientRenamed { Id = clientId, Name = clientNewName }), - CreateEvent(new AppClientUpdated { Id = clientId, Permission = AppClientPermission.Developer }) + CreateEvent(new AppClientUpdated { Id = clientId, Role = Role.Developer }) ); } @@ -402,7 +402,7 @@ namespace Squidex.Domain.Apps.Entities.Apps private Task ExecuteAssignContributorAsync() { - return sut.ExecuteAsync(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); + return sut.ExecuteAsync(CreateCommand(new AssignContributor { ContributorId = contributorId, Role = Role.Editor })); } private Task ExecuteAttachClientAsync() diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppClientsTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppClientsTests.cs index a7f2b73ff..9b6f99775 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppClientsTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppClientsTests.cs @@ -94,25 +94,25 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards } [Fact] - public void UpdateClient_should_throw_exception_if_client_has_no_name_and_permission() + public void UpdateClient_should_throw_exception_if_client_has_no_name_and_role() { var command = new UpdateClient { Id = "ios" }; var clients_1 = clients_0.Add("ios", "secret"); ValidationAssert.Throws(() => GuardAppClients.CanUpdate(clients_1, command), - new ValidationError("Either name or permission must be defined.", "Name", "Permission")); + new ValidationError("Either name or role must be defined.", "Name", "Role")); } [Fact] - public void UpdateClient_should_throw_exception_if_client_has_invalid_permission() + public void UpdateClient_should_throw_exception_if_client_has_invalid_role() { - var command = new UpdateClient { Id = "ios", Permission = (AppClientPermission)10 }; + var command = new UpdateClient { Id = "ios", Role = "Invalid" }; var clients_1 = clients_0.Add("ios", "secret"); ValidationAssert.Throws(() => GuardAppClients.CanUpdate(clients_1, command), - new ValidationError("Permission is not valid.", "Permission")); + new ValidationError("Role is not valid.", "Role")); } [Fact] @@ -127,20 +127,20 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards } [Fact] - public void UpdateClient_should_throw_exception_if_client_has_same_permission() + public void UpdateClient_should_throw_exception_if_client_has_same_role() { - var command = new UpdateClient { Id = "ios", Permission = AppClientPermission.Editor }; + var command = new UpdateClient { Id = "ios", Role = Role.Editor }; var clients_1 = clients_0.Add("ios", "secret"); ValidationAssert.Throws(() => GuardAppClients.CanUpdate(clients_1, command), - new ValidationError("Client has already this permission.", "Permission")); + new ValidationError("Client has already this role.", "Role")); } [Fact] public void UpdateClient_should_not_throw_exception_if_command_is_valid() { - var command = new UpdateClient { Id = "ios", Name = "iOS", Permission = AppClientPermission.Reader }; + var command = new UpdateClient { Id = "ios", Name = "iOS", Role = Role.Reader }; var clients_1 = clients_0.Add("ios", "secret"); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs index a61f1f099..c7234b2e5 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs @@ -60,29 +60,29 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards } [Fact] - public Task CanAssign_should_throw_exception_if_permission_not_valid() + public Task CanAssign_should_throw_exception_if_role_not_valid() { - var command = new AssignContributor { ContributorId = "1", Permission = (AppContributorPermission)10 }; + var command = new AssignContributor { ContributorId = "1", Role = "Invalid" }; return ValidationAssert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan), - new ValidationError("Permission is not valid.", "Permission")); + new ValidationError("Role is not valid.", "Role")); } [Fact] - public Task CanAssign_should_throw_exception_if_user_already_exists_with_same_permission() + public Task CanAssign_should_throw_exception_if_user_already_exists_with_same_role() { - var command = new AssignContributor { ContributorId = "1" }; + var command = new AssignContributor { ContributorId = "1", Role = Role.Owner }; - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Owner); + var contributors_1 = contributors_0.Assign("1", Role.Owner); return ValidationAssert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_1, command, users, appPlan), - new ValidationError("Contributor has already this permission.", "Permission")); + new ValidationError("Contributor has already this role.", "Role")); } [Fact] public Task CanAssign_should_throw_exception_if_user_not_found() { - var command = new AssignContributor { ContributorId = "notfound", Permission = (AppContributorPermission)10 }; + var command = new AssignContributor { ContributorId = "notfound", Role = Role.Owner }; return Assert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan)); } @@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards [Fact] public Task CanAssign_should_throw_exception_if_user_is_actor() { - var command = new AssignContributor { ContributorId = "3", Permission = AppContributorPermission.Editor, Actor = new RefToken("user", "3") }; + var command = new AssignContributor { ContributorId = "3", Role = Role.Editor, Actor = new RefToken("user", "3") }; return Assert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan)); } @@ -103,8 +103,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards var command = new AssignContributor { ContributorId = "3" }; - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Owner); - var contributors_2 = contributors_1.Assign("2", AppContributorPermission.Editor); + var contributors_1 = contributors_0.Assign("1", Role.Owner); + var contributors_2 = contributors_1.Assign("2", Role.Editor); return ValidationAssert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_2, command, users, appPlan), new ValidationError("You have reached the maximum number of contributors for your plan.")); @@ -129,25 +129,25 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards } [Fact] - public Task CanAssign_should_not_throw_exception_if_contributor_has_another_permission() + public Task CanAssign_should_not_throw_exception_if_contributor_has_another_role() { - var command = new AssignContributor { ContributorId = "1" }; + var command = new AssignContributor { ContributorId = "1", Role = Role.Developer }; - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Editor); + var contributors_1 = contributors_0.Assign("1", Role.Editor); return GuardAppContributors.CanAssign(contributors_1, command, users, appPlan); } [Fact] - public Task CanAssign_should_not_throw_exception_if_contributor_max_reached_but_permission_changed() + public Task CanAssign_should_not_throw_exception_if_contributor_max_reached_but_role_changed() { A.CallTo(() => appPlan.MaxContributors) .Returns(2); - var command = new AssignContributor { ContributorId = "1" }; + var command = new AssignContributor { ContributorId = "1", Role = Role.Developer }; - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Editor); - var contributors_2 = contributors_1.Assign("2", AppContributorPermission.Editor); + var contributors_1 = contributors_0.Assign("1", Role.Editor); + var contributors_2 = contributors_1.Assign("2", Role.Editor); return GuardAppContributors.CanAssign(contributors_2, command, users, appPlan); } @@ -174,8 +174,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { var command = new RemoveContributor { ContributorId = "1" }; - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Owner); - var contributors_2 = contributors_1.Assign("2", AppContributorPermission.Editor); + var contributors_1 = contributors_0.Assign("1", Role.Owner); + var contributors_2 = contributors_1.Assign("2", Role.Editor); ValidationAssert.Throws(() => GuardAppContributors.CanRemove(contributors_2, command), new ValidationError("Cannot remove the only owner.")); @@ -186,8 +186,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { var command = new RemoveContributor { ContributorId = "1" }; - var contributors_1 = contributors_0.Assign("1", AppContributorPermission.Owner); - var contributors_2 = contributors_1.Assign("2", AppContributorPermission.Owner); + var contributors_1 = contributors_0.Assign("1", Role.Owner); + var contributors_2 = contributors_1.Assign("2", Role.Owner); GuardAppContributors.CanRemove(contributors_2, command); } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexCommandMiddlewareTests.cs index 1f581b135..7fba44816 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexCommandMiddlewareTests.cs @@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes .Returns(J.AsTask(appState)); A.CallTo(() => appState.Contributors) - .Returns(AppContributors.Empty.Assign(userId, AppContributorPermission.Owner)); + .Returns(AppContributors.Empty.Assign(userId, Role.Owner)); var context = new CommandContext(new ArchiveApp { AppId = appId }, commandBus) diff --git a/tools/Migrate_01/OldEvents/AppClientChanged.cs b/tools/Migrate_01/OldEvents/AppClientChanged.cs index 5a00db775..d2caf075a 100644 --- a/tools/Migrate_01/OldEvents/AppClientChanged.cs +++ b/tools/Migrate_01/OldEvents/AppClientChanged.cs @@ -6,9 +6,7 @@ // ========================================================================== using System; -using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Events; -using Squidex.Domain.Apps.Events.Apps; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Reflection; diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClientPermission.cs b/tools/Migrate_01/OldEvents/AppClientPermission.cs similarity index 88% rename from src/Squidex.Domain.Apps.Core.Model/Apps/AppClientPermission.cs rename to tools/Migrate_01/OldEvents/AppClientPermission.cs index 33749b3e1..4e22ca94d 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClientPermission.cs +++ b/tools/Migrate_01/OldEvents/AppClientPermission.cs @@ -5,8 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.Domain.Apps.Core.Apps +using System; + +namespace Migrate_01.OldEvents { + [Obsolete] public enum AppClientPermission { Developer, diff --git a/tools/Migrate_01/OldEvents/AppClientUpdated.cs b/tools/Migrate_01/OldEvents/AppClientUpdated.cs new file mode 100644 index 000000000..41786be33 --- /dev/null +++ b/tools/Migrate_01/OldEvents/AppClientUpdated.cs @@ -0,0 +1,45 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Events; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Reflection; +using AppClientUpdatedV2 = Squidex.Domain.Apps.Events.Apps.AppClientUpdated; + +namespace Migrate_01.OldEvents +{ + [EventType(nameof(AppClientUpdated))] + [Obsolete] + public sealed class AppClientUpdated : AppEvent, IMigratedEvent + { + public string Id { get; set; } + + public AppClientPermission Permission { get; set; } + + public IEvent Migrate() + { + var result = SimpleMapper.Map(this, new AppClientUpdatedV2()); + + switch (Permission) + { + case AppClientPermission.Developer: + result.Role = Role.Developer; + break; + case AppClientPermission.Editor: + result.Role = Role.Editor; + break; + case AppClientPermission.Reader: + result.Role = Role.Reader; + break; + } + + return result; + } + } +} \ No newline at end of file diff --git a/tools/Migrate_01/OldEvents/AppContributorAssigned.cs b/tools/Migrate_01/OldEvents/AppContributorAssigned.cs new file mode 100644 index 000000000..fbf25e427 --- /dev/null +++ b/tools/Migrate_01/OldEvents/AppContributorAssigned.cs @@ -0,0 +1,45 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Events; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Reflection; +using AppContributorAssignedV2 = Squidex.Domain.Apps.Events.Apps.AppContributorAssigned; + +namespace Migrate_01.OldEvents +{ + [EventType(nameof(AppContributorAssigned))] + [Obsolete] + public sealed class AppContributorAssigned : AppEvent, IMigratedEvent + { + public string ContributorId { get; set; } + + public AppContributorPermission Permission { get; set; } + + public IEvent Migrate() + { + var result = SimpleMapper.Map(this, new AppContributorAssignedV2()); + + switch (Permission) + { + case AppContributorPermission.Owner: + result.Role = Role.Owner; + break; + case AppContributorPermission.Developer: + result.Role = Role.Developer; + break; + case AppContributorPermission.Editor: + result.Role = Role.Editor; + break; + } + + return result; + } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributorPermission.cs b/tools/Migrate_01/OldEvents/AppContributorPermission.cs similarity index 88% rename from src/Squidex.Domain.Apps.Core.Model/Apps/AppContributorPermission.cs rename to tools/Migrate_01/OldEvents/AppContributorPermission.cs index a1916542f..0135058d7 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributorPermission.cs +++ b/tools/Migrate_01/OldEvents/AppContributorPermission.cs @@ -5,8 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.Domain.Apps.Core.Apps +using System; + +namespace Migrate_01.OldEvents { + [Obsolete] public enum AppContributorPermission { Owner,