diff --git a/src/Squidex.Domain.Apps.Entities/Apps/RoleExtensions.cs b/src/Squidex.Domain.Apps.Entities/Apps/RoleExtensions.cs index ada562aa9..c9b0f7dfd 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/RoleExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/RoleExtensions.cs @@ -5,6 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.Linq; +using Squidex.Infrastructure.Security; using Squidex.Shared; namespace Squidex.Domain.Apps.Entities.Apps @@ -31,5 +34,28 @@ namespace Squidex.Domain.Apps.Entities.Apps return permissions; } + + public static PermissionSet WithoutApp(this PermissionSet set, string name) + { + var prefix = Permissions.ForApp(Permissions.App, name).Id; + + return new PermissionSet(set.Select(x => + { + var id = x.Id; + + if (string.Equals(id, prefix, StringComparison.OrdinalIgnoreCase)) + { + return Permission.Any; + } + else if (id.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + return id.Substring(prefix.Length + 1); + } + else + { + return id; + } + })); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs b/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs index c57e27f12..191aa2347 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs @@ -27,10 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Apps public async Task> GetPermissionsAsync(IAppEntity app) { - var schemas = await appProvider.GetSchemasAsync(app.Id); - var schemaNames = schemas.Select(x => x.Name).ToList(); - - schemaNames.Insert(0, Permission.Any); + var schemaNames = await GetSchemaNamesAsync(app); var result = new List { Permission.Any }; @@ -61,5 +58,17 @@ namespace Squidex.Domain.Apps.Entities.Apps return result; } + + private async Task> GetSchemaNamesAsync(IAppEntity app) + { + var schemas = await appProvider.GetSchemasAsync(app.Id); + + var schemaNames = new List(); ; + + schemaNames.Add(Permission.Any); + schemaNames.AddRange(schemas.Select(x => x.Name)); + + return schemaNames; + } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs b/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs index f19a9f7f1..e3ba3f69b 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc; using Squidex.Areas.Api.Controllers.Apps.Models; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Pipeline; using Squidex.Shared; @@ -43,15 +44,37 @@ namespace Squidex.Areas.Api.Controllers.Apps [ProducesResponseType(typeof(RolesDto), 200)] [ApiPermission(Permissions.AppRolesRead)] [ApiCosts(0)] - public async Task GetRoles(string app) + public IActionResult GetRoles(string app) { - var response = RolesDto.FromApp(App, await permissionsProvider.GetPermissionsAsync(App)); + var response = RolesDto.FromApp(App); Response.Headers["ETag"] = App.Version.ToString(); return Ok(response); } + /// + /// Get app permissions. + /// + /// The name of the app. + /// + /// 200 => App permissions returned. + /// 404 => App not found. + /// + [HttpGet] + [Route("apps/{app}/roles/permissions")] + [ProducesResponseType(typeof(string[]), 200)] + [ApiPermission(Permissions.AppRolesRead)] + [ApiCosts(0)] + public async Task GetPermissions(string app) + { + var response = await permissionsProvider.GetPermissionsAsync(App); + + Response.Headers["ETag"] = string.Join(";", response).Sha256Base64(); + + return Ok(response); + } + /// /// Add role to app. /// diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs index 1e662aae7..0991e4b46 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs @@ -8,6 +8,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Entities.Apps; namespace Squidex.Areas.Api.Controllers.Apps.Models { @@ -25,9 +26,11 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models [Required] public string[] Permissions { get; set; } - public static RoleDto FromRole(Role role) + public static RoleDto FromRole(Role role, string appName) { - return new RoleDto { Name = role.Name, Permissions = role.Permissions.Select(x => x.Id).ToArray() }; + var permissions = role.Permissions.WithoutApp(appName); + + return new RoleDto { Name = role.Name, Permissions = permissions.ToIds().ToArray() }; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs index 24616f432..c34cfbbdf 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs @@ -20,16 +20,11 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models [Required] public RoleDto[] Roles { get; set; } - /// - /// Suggested permissions. - /// - public string[] AllPermissions { get; set; } - - public static RolesDto FromApp(IAppEntity app, List permissions) + public static RolesDto FromApp(IAppEntity app) { - var roles = app.Roles.Values.Select(RoleDto.FromRole).ToArray(); + var roles = app.Roles.Values.Select(x => RoleDto.FromRole(x, app.Name)).ToArray(); - return new RolesDto { Roles = roles, AllPermissions = permissions.ToList() }; + return new RolesDto { Roles = roles }; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index 61a328bf8..bacfabd87 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Extensions.Actions; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Reflection; using Squidex.Pipeline; @@ -30,8 +31,8 @@ namespace Squidex.Areas.Api.Controllers.Rules [ApiExplorerSettings(GroupName = nameof(Rules))] public sealed class RulesController : ApiController { - private static readonly string RuleActionsEtag = string.Join(";", RuleElementRegistry.Actions.Select(x => x.Key)).Sha256(); - private static readonly string RuleTriggersEtag = string.Join(";", RuleElementRegistry.Triggers.Select(x => x.Key)).Sha256(); + private static readonly string RuleActionsEtag = string.Join(";", RuleElementRegistry.Actions.Select(x => x.Key)).Sha256Base64(); + private static readonly string RuleTriggersEtag = string.Join(";", RuleElementRegistry.Triggers.Select(x => x.Key)).Sha256Base64(); private readonly IAppProvider appProvider; private readonly IRuleEventRepository ruleEventsRepository; diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index e84fe9fd6..51459d275 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -91,6 +91,9 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); + services.AddSingletonAs() + .AsSelf(); + services.AddSingletonAs() .AsSelf(); diff --git a/src/Squidex/app/features/settings/declarations.ts b/src/Squidex/app/features/settings/declarations.ts index 464cce4ab..fdb82e6b5 100644 --- a/src/Squidex/app/features/settings/declarations.ts +++ b/src/Squidex/app/features/settings/declarations.ts @@ -7,20 +7,16 @@ export * from './pages/backups/backups-page.component'; export * from './pages/backups/pipes'; - export * from './pages/clients/client.component'; export * from './pages/clients/clients-page.component'; - export * from './pages/contributors/contributors-page.component'; - export * from './pages/languages/language.component'; export * from './pages/languages/languages-page.component'; - export * from './pages/more/more-page.component'; - export * from './pages/patterns/pattern.component'; export * from './pages/patterns/patterns-page.component'; - export * from './pages/plans/plans-page.component'; +export * from './pages/roles/role.component'; +export * from './pages/roles/roles-page.component'; export * from './settings-area.component'; \ No newline at end of file diff --git a/src/Squidex/app/features/settings/module.ts b/src/Squidex/app/features/settings/module.ts index f43871ef9..b41b44926 100644 --- a/src/Squidex/app/features/settings/module.ts +++ b/src/Squidex/app/features/settings/module.ts @@ -28,6 +28,8 @@ import { PatternComponent, PatternsPageComponent, PlansPageComponent, + RoleComponent, + RolesPageComponent, SettingsAreaComponent } from './declarations'; @@ -129,6 +131,26 @@ const routes: Routes = [ } ] }, + { + path: 'roles', + component: RolesPageComponent, + children: [ + { + path: 'history', + component: HistoryComponent, + data: { + channel: 'settings.roles' + } + }, + { + path: 'help', + component: HelpComponent, + data: { + helpPage: '05-integrated/roles' + } + } + ] + }, { path: 'languages', component: LanguagesPageComponent, @@ -172,6 +194,8 @@ const routes: Routes = [ PatternComponent, PatternsPageComponent, PlansPageComponent, + RoleComponent, + RolesPageComponent, SettingsAreaComponent ] }) diff --git a/src/Squidex/app/features/settings/pages/clients/client.component.html b/src/Squidex/app/features/settings/pages/clients/client.component.html index 069e7d6b2..12d8c6738 100644 --- a/src/Squidex/app/features/settings/pages/clients/client.component.html +++ b/src/Squidex/app/features/settings/pages/clients/client.component.html @@ -71,7 +71,7 @@
diff --git a/src/Squidex/app/features/settings/pages/clients/client.component.ts b/src/Squidex/app/features/settings/pages/clients/client.component.ts index 5fe8357d4..402b1b49b 100644 --- a/src/Squidex/app/features/settings/pages/clients/client.component.ts +++ b/src/Squidex/app/features/settings/pages/clients/client.component.ts @@ -13,6 +13,7 @@ import { AccessTokenDto, AppClientDto, AppClientsService, + AppRoleDto, AppsState, ClientsState, DialogModel, @@ -32,7 +33,8 @@ export class ClientComponent implements OnChanges { @Input() public client: AppClientDto; - public clientRoles = [ 'Owner', 'Developer', 'Editor', 'Reader' ]; + @Input() + public clientRoles: AppRoleDto[]; public isRenaming = false; diff --git a/src/Squidex/app/features/settings/pages/clients/clients-page.component.html b/src/Squidex/app/features/settings/pages/clients/clients-page.component.html index ab463d7dd..958d7721c 100644 --- a/src/Squidex/app/features/settings/pages/clients/clients-page.component.html +++ b/src/Squidex/app/features/settings/pages/clients/clients-page.component.html @@ -19,7 +19,12 @@ No client created yet.
- + + + +