From 2e401dbf92a0b1a51eabb674a95dc7a56848da33 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 27 Oct 2018 19:33:14 +0200 Subject: [PATCH] Migrated roles, clients and contributors. --- .../Apps/Role.cs | 3 +- .../Apps/AppHistoryEventsCreator.cs | 6 + .../Apps/Guards/GuardAppContributors.cs | 3 +- .../Contents/ContentQueryService.cs | 21 +- .../History/HistoryEventsCreatorBase.cs | 7 + .../UserManagerExtensions.cs | 5 +- .../Security/PermissionSet.cs | 7 +- .../Identity/ClaimsPrincipalExtensions.cs | 6 + src/Squidex.Shared/Identity/SquidexRoles.cs | 14 - src/Squidex.Shared/Permissions.cs | 1 + src/Squidex.Shared/Users/UserExtensions.cs | 9 +- .../Api/Controllers/Apps/AppsController.cs | 1 + .../Contents/ContentsController.cs | 8 +- .../Users/UserManagementController.cs | 4 +- .../Config/IdentityServerExtensions.cs | 23 +- .../Controllers/Account/AccountController.cs | 3 +- src/Squidex/Areas/IdentityServer/Startup.cs | 1 - ...rleansDashboardAuthenticationMiddleware.cs | 6 +- .../Config/Authentication/OidcServices.cs | 1 + src/Squidex/Config/Domain/EntitiesServices.cs | 3 + src/Squidex/Config/Web/WebServices.cs | 9 +- .../Pipeline/ApiExceptionFilterAttribute.cs | 7 + src/Squidex/Pipeline/ApiPermissionUnifier.cs | 16 +- src/Squidex/WebStartup.cs | 2 +- .../pages/clients/client.component.html | 6 +- .../pages/clients/client.component.ts | 6 +- .../contributors-page.component.html | 4 +- .../contributors-page.component.ts | 6 +- .../settings/settings-area.component.html | 2 +- .../services/app-clients.service.spec.ts | 6 +- .../shared/services/app-clients.service.ts | 8 +- .../services/app-contributors.service.spec.ts | 4 +- .../services/app-contributors.service.ts | 4 +- .../app/shared/state/clients.state.spec.ts | 4 +- src/Squidex/app/shared/state/clients.state.ts | 2 +- .../app/shared/state/contributors.state.ts | 6 +- .../Apps/Guards/GuardAppContributorsTests.cs | 3 +- .../Contents/ContentQueryServiceTests.cs | 327 ++++++++++++------ tools/Migrate_01/MigrationPath.cs | 8 +- tools/Migrate_01/Migrations/RebuildApps.cs | 27 ++ 40 files changed, 385 insertions(+), 204 deletions(-) delete mode 100644 src/Squidex.Shared/Identity/SquidexRoles.cs create mode 100644 tools/Migrate_01/Migrations/RebuildApps.cs diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs index 6e47889e9..5ddec819f 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs @@ -70,8 +70,7 @@ namespace Squidex.Domain.Apps.Core.Apps return new Role(Reader, P.ForApp(P.AppAssetsRead, app), P.ForApp(P.AppCommon, app), - P.ForApp(P.AppContentsRead, app), - P.ForApp(P.AppContentsGraphQL, app)); + P.ForApp(P.AppContentsRead, app)); } public static Role CreateDeveloper(string app) diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs index 2ac42e428..535515f3e 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs @@ -19,6 +19,12 @@ namespace Squidex.Domain.Apps.Entities.Apps public AppHistoryEventsCreator(TypeNameRegistry typeNameRegistry) : base(typeNameRegistry) { + AddEventMessage("AppContributorAssignedEvent", + "assigned {user:[Contributor]} as {[Role]}"); + + AddEventMessage("AppClientUpdatedEvent", + "updated client {[Id]}"); + AddEventMessage( "assigned {user:[Contributor]} as {[Role]}"); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs index cb90a0ddc..c2184fd4e 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs @@ -7,7 +7,6 @@ using System; using System.Linq; -using System.Security; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; @@ -47,7 +46,7 @@ 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 role."); + throw new DomainForbiddenException("You cannot change your own role."); } if (contributors.TryGetValue(command.ContributorId, out var existing)) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs index f3c6fa07b..bfa407ada 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.OData; using Squidex.Domain.Apps.Core.Contents; @@ -21,6 +22,9 @@ using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries.OData; using Squidex.Infrastructure.Reflection; +using Squidex.Shared; +using Squidex.Shared.Identity; +using Squidex.Shared.Users; #pragma warning disable RECS0147 @@ -73,6 +77,8 @@ namespace Squidex.Domain.Apps.Entities.Contents var schema = await GetSchemaAsync(context); + CheckPermission(schema, context.Base.User); + using (Profiler.TraceMethod()) { var isVersioned = version > EtagVersion.Empty; @@ -86,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (content == null || (content.Status != Status.Published && !context.Base.IsFrontendClient) || content.SchemaId.Id != schema.Id) { - throw new DomainObjectNotFoundException(id.ToString(), typeof(ISchemaEntity)); + throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity)); } return Transform(context.Base, schema, true, content); @@ -99,6 +105,8 @@ namespace Squidex.Domain.Apps.Entities.Contents var schema = await GetSchemaAsync(context); + CheckPermission(schema, context.Base.User); + using (Profiler.TraceMethod()) { var status = GetQueryStatus(context.Base); @@ -257,6 +265,17 @@ namespace Squidex.Domain.Apps.Entities.Contents return schema; } + private void CheckPermission(ISchemaEntity schema, ClaimsPrincipal user) + { + var permissions = user.Permissions(); + var permission = Permissions.ForApp(Permissions.AppContentsRead, schema.AppId.Name, schema.Name); + + if (!permissions.Allows(permission)) + { + throw new DomainForbiddenException("You do not have permission for this schema."); + } + } + private static Status[] GetFindStatus(QueryContext context) { if (context.IsFrontendClient) diff --git a/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs b/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs index feaac0b43..498b748f6 100644 --- a/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs +++ b/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs @@ -36,6 +36,13 @@ namespace Squidex.Domain.Apps.Entities.History texts[typeNameRegistry.GetName()] = message; } + protected void AddEventMessage(string type, string message) + { + Guard.NotNullOrEmpty(message, nameof(message)); + + texts[type] = message; + } + protected bool HasEventText(IEvent @event) { var message = typeNameRegistry.GetName(@event.GetType()); diff --git a/src/Squidex.Domain.Users/UserManagerExtensions.cs b/src/Squidex.Domain.Users/UserManagerExtensions.cs index 0e04ce7de..5ed5b7f0d 100644 --- a/src/Squidex.Domain.Users/UserManagerExtensions.cs +++ b/src/Squidex.Domain.Users/UserManagerExtensions.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Squidex.Infrastructure; +using Squidex.Infrastructure.Security; using Squidex.Shared.Users; namespace Squidex.Domain.Users @@ -45,7 +46,7 @@ namespace Squidex.Domain.Users return result; } - public static async Task CreateAsync(this UserManager userManager, IUserFactory factory, string email, string displayName, string password, string[] permissions = null) + public static async Task CreateAsync(this UserManager userManager, IUserFactory factory, string email, string displayName, string password, PermissionSet permissions = null) { var user = factory.Create(email); @@ -85,7 +86,7 @@ namespace Squidex.Domain.Users return userManager.UpdateAsync(user); } - public static async Task UpdateAsync(this UserManager userManager, string id, string email, string displayName, string password, string[] permissions = null) + public static async Task UpdateAsync(this UserManager userManager, string id, string email, string displayName, string password, PermissionSet permissions = null) { var user = await userManager.FindByIdAsync(id); diff --git a/src/Squidex.Infrastructure/Security/PermissionSet.cs b/src/Squidex.Infrastructure/Security/PermissionSet.cs index c9a9eff69..a7b138927 100644 --- a/src/Squidex.Infrastructure/Security/PermissionSet.cs +++ b/src/Squidex.Infrastructure/Security/PermissionSet.cs @@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Security { public sealed class PermissionSet : IReadOnlyCollection { - public static readonly PermissionSet Empty = new PermissionSet(); + public static readonly PermissionSet Empty = new PermissionSet(new string[0]); private readonly List permissions; private readonly Lazy display; @@ -29,6 +29,11 @@ namespace Squidex.Infrastructure.Security { } + public PermissionSet(params string[] permissions) + : this(permissions?.Select(x => new Permission(x))) + { + } + public PermissionSet(IEnumerable permissions) : this(permissions?.Select(x => new Permission(x))) { diff --git a/src/Squidex.Shared/Identity/ClaimsPrincipalExtensions.cs b/src/Squidex.Shared/Identity/ClaimsPrincipalExtensions.cs index 821df27f5..cb4511d41 100644 --- a/src/Squidex.Shared/Identity/ClaimsPrincipalExtensions.cs +++ b/src/Squidex.Shared/Identity/ClaimsPrincipalExtensions.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; +using Squidex.Infrastructure.Security; namespace Squidex.Shared.Identity { @@ -24,6 +25,11 @@ namespace Squidex.Shared.Identity identity.AddClaim(new Claim(SquidexClaimTypes.PictureUrl, pictureUrl)); } + public static PermissionSet Permissions(this ClaimsPrincipal principal) + { + return new PermissionSet(principal.Claims.Where(x => x.Type == SquidexClaimTypes.Permissions).Select(x => new Permission(x.Value))); + } + public static IEnumerable GetSquidexClaims(this ClaimsPrincipal principal) { return principal.Claims.Where(c => c.Type.StartsWith(SquidexClaimTypes.Prefix, StringComparison.Ordinal)); diff --git a/src/Squidex.Shared/Identity/SquidexRoles.cs b/src/Squidex.Shared/Identity/SquidexRoles.cs deleted file mode 100644 index 8c1dcd9d9..000000000 --- a/src/Squidex.Shared/Identity/SquidexRoles.cs +++ /dev/null @@ -1,14 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Shared.Identity -{ - public static class SquidexRoles - { - public static readonly string Administrator = "ADMINISTRATOR"; - } -} diff --git a/src/Squidex.Shared/Permissions.cs b/src/Squidex.Shared/Permissions.cs index 4a4c675ca..8e339de9d 100644 --- a/src/Squidex.Shared/Permissions.cs +++ b/src/Squidex.Shared/Permissions.cs @@ -16,6 +16,7 @@ namespace Squidex.Shared public const string All = "squidex.*"; public const string Admin = "squidex.admin.*"; + public const string AdminOrleans = "squidex.admin.orleans"; public const string AdminRestore = "squidex.admin.restore"; public const string AdminRestoreRead = "squidex.admin.restore.read"; diff --git a/src/Squidex.Shared/Users/UserExtensions.cs b/src/Squidex.Shared/Users/UserExtensions.cs index b5cc7a123..c0479e457 100644 --- a/src/Squidex.Shared/Users/UserExtensions.cs +++ b/src/Squidex.Shared/Users/UserExtensions.cs @@ -51,13 +51,13 @@ namespace Squidex.Shared.Users user.SetClaim(SquidexClaimTypes.ConsentForEmails, value.ToString()); } - public static void SetPermissions(this IUser user, params string[] permissions) + public static void SetPermissions(this IUser user, PermissionSet permissions) { user.RemoveClaims(SquidexClaimTypes.Permissions); foreach (var permission in permissions) { - user.AddClaim(new Claim(SquidexClaimTypes.Permissions, permission)); + user.AddClaim(new Claim(SquidexClaimTypes.Permissions, permission.Id)); } } @@ -101,11 +101,6 @@ namespace Squidex.Shared.Users return user.GetClaimValue(SquidexClaimTypes.DisplayName); } - public static PermissionSet Permissions(this ClaimsPrincipal principal) - { - return new PermissionSet(principal.Claims.Where(x => x.Type == SquidexClaimTypes.Permissions).Select(x => new Permission(x.Value))); - } - public static PermissionSet Permissions(this IUser user) { return new PermissionSet(user.GetClaimValues(SquidexClaimTypes.Permissions).Select(x => new Permission(x))); diff --git a/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs b/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs index bbcd99453..74fc6bfb4 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs @@ -17,6 +17,7 @@ using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Security; using Squidex.Pipeline; using Squidex.Shared; +using Squidex.Shared.Identity; using Squidex.Shared.Users; namespace Squidex.Areas.Api.Controllers.Apps diff --git a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs index 118827b9e..8644d3fe0 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs @@ -57,7 +57,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpGet] [HttpPost] [Route("content/{app}/graphql/")] - [ApiPermission(Permissions.AppContentsGraphQL)] + [ApiPermission] [ApiCosts(2)] public async Task PostGraphQL(string app, [FromBody] GraphQLQuery query) { @@ -88,7 +88,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpGet] [HttpPost] [Route("content/{app}/graphql/batch")] - [ApiPermission(Permissions.AppContentsGraphQL)] + [ApiPermission] [ApiCosts(2)] public async Task PostGraphQLBatch(string app, [FromBody] GraphQLQuery[] batch) { @@ -120,7 +120,7 @@ namespace Squidex.Areas.Api.Controllers.Contents /// [HttpGet] [Route("content/{app}/{name}/")] - [ApiPermission(Permissions.AppContentsRead)] + [ApiPermission] [ApiCosts(2)] public async Task GetContents(string app, string name, [FromQuery] bool archived = false, [FromQuery] string ids = null) { @@ -159,7 +159,7 @@ namespace Squidex.Areas.Api.Controllers.Contents /// [HttpGet] [Route("content/{app}/{name}/{id}/")] - [ApiPermission(Permissions.AppContentsRead)] + [ApiPermission] [ApiCosts(1)] public async Task GetContent(string app, string name, Guid id) { diff --git a/src/Squidex/Areas/Api/Controllers/Users/UserManagementController.cs b/src/Squidex/Areas/Api/Controllers/Users/UserManagementController.cs index 9e0cafdac..be6acdd89 100644 --- a/src/Squidex/Areas/Api/Controllers/Users/UserManagementController.cs +++ b/src/Squidex/Areas/Api/Controllers/Users/UserManagementController.cs @@ -75,7 +75,7 @@ namespace Squidex.Areas.Api.Controllers.Users [ApiPermission(Permissions.AdminUsersCreate)] public async Task PostUser([FromBody] CreateUserDto request) { - var user = await userManager.CreateAsync(userFactory, request.Email, request.DisplayName, request.Password, request.Permissions); + var user = await userManager.CreateAsync(userFactory, request.Email, request.DisplayName, request.Password, new PermissionSet(request.Permissions)); var response = new UserCreatedDto { Id = user.Id }; @@ -87,7 +87,7 @@ namespace Squidex.Areas.Api.Controllers.Users [ApiPermission(Permissions.AdminUsersUpdate)] public async Task PutUser(string id, [FromBody] UpdateUserDto request) { - await userManager.UpdateAsync(id, request.Email, request.DisplayName, request.Password, request.Permissions); + await userManager.UpdateAsync(id, request.Email, request.DisplayName, request.Password, new PermissionSet(request.Permissions)); return NoContent(); } diff --git a/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs b/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs index 57115b2dc..c6b8f2668 100644 --- a/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs +++ b/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs @@ -15,8 +15,8 @@ using Microsoft.Extensions.Options; using Squidex.Config; using Squidex.Domain.Users; using Squidex.Infrastructure.Log; +using Squidex.Infrastructure.Security; using Squidex.Shared; -using Squidex.Shared.Identity; using Squidex.Shared.Users; namespace Squidex.Areas.IdentityServer.Config @@ -30,23 +30,6 @@ namespace Squidex.Areas.IdentityServer.Config return app; } - public static IServiceProvider UseMyAdminRole(this IServiceProvider services) - { - var roleManager = services.GetRequiredService>(); - - Task.Run(async () => - { - if (!await roleManager.RoleExistsAsync(SquidexRoles.Administrator)) - { - var role = services.GetRequiredService().Create(SquidexRoles.Administrator); - - await roleManager.CreateAsync(role); - } - }).Wait(); - - return services; - } - public static IServiceProvider UseMyAdmin(this IServiceProvider services) { var options = services.GetService>().Value; @@ -67,7 +50,9 @@ namespace Squidex.Areas.IdentityServer.Config { try { - await userManager.CreateAsync(userFactory, adminEmail, adminEmail, adminPass, new[] { Permissions.Admin }); + var permissions = new PermissionSet(Permissions.Admin); + + await userManager.CreateAsync(userFactory, adminEmail, adminEmail, adminPass, permissions); } catch (Exception ex) { diff --git a/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs b/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs index 157b31562..ea658852f 100644 --- a/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs +++ b/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs @@ -22,6 +22,7 @@ using Squidex.Domain.Users; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Tasks; +using Squidex.Shared; using Squidex.Shared.Identity; using Squidex.Shared.Users; @@ -315,7 +316,7 @@ namespace Squidex.Areas.IdentityServer.Controllers.Account return TaskHelper.True; } - return MakeIdentityOperation(() => userManager.AddToRoleAsync(user, SquidexRoles.Administrator)); + return MakeIdentityOperation(() => userManager.AddClaimAsync(user, new Claim(SquidexClaimTypes.Permissions, Permissions.Admin))); } private IUser CreateUser(ExternalLoginInfo externalLogin, string email) diff --git a/src/Squidex/Areas/IdentityServer/Startup.cs b/src/Squidex/Areas/IdentityServer/Startup.cs index 39978b113..edb3ba91b 100644 --- a/src/Squidex/Areas/IdentityServer/Startup.cs +++ b/src/Squidex/Areas/IdentityServer/Startup.cs @@ -17,7 +17,6 @@ namespace Squidex.Areas.IdentityServer { public static void ConfigureIdentityServer(this IApplicationBuilder app) { - app.ApplicationServices.UseMyAdminRole(); app.ApplicationServices.UseMyAdmin(); var environment = app.ApplicationServices.GetRequiredService(); diff --git a/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs b/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs index db67abdd3..08da9330f 100644 --- a/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs +++ b/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs @@ -10,12 +10,16 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Http; +using Squidex.Infrastructure.Security; +using Squidex.Shared; using Squidex.Shared.Identity; namespace Squidex.Areas.OrleansDashboard.Middlewares { public sealed class OrleansDashboardAuthenticationMiddleware { + private static readonly Permission OrleansPermissions = new Permission(Permissions.AdminOrleans); + private readonly RequestDelegate next; public OrleansDashboardAuthenticationMiddleware(RequestDelegate next) @@ -27,7 +31,7 @@ namespace Squidex.Areas.OrleansDashboard.Middlewares { var authentication = await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); - if (!authentication.Succeeded || !authentication.Principal.IsInRole(SquidexRoles.Administrator)) + if (!authentication.Succeeded || !authentication.Principal.Permissions().Allows(OrleansPermissions)) { await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { diff --git a/src/Squidex/Config/Authentication/OidcServices.cs b/src/Squidex/Config/Authentication/OidcServices.cs index c2cd360de..9d344601b 100644 --- a/src/Squidex/Config/Authentication/OidcServices.cs +++ b/src/Squidex/Config/Authentication/OidcServices.cs @@ -24,6 +24,7 @@ namespace Squidex.Config.Authentication options.Authority = identityOptions.OidcAuthority; options.ClientId = identityOptions.OidcClient; options.ClientSecret = identityOptions.OidcSecret; + options.Scope.Add(Constants.PermissionsScope); options.RequireHttpsMetadata = false; }); } diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index eae7bdb28..e84fe9fd6 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -243,6 +243,9 @@ namespace Squidex.Config.Domain services.AddTransientAs() .As(); + services.AddTransientAs() + .As(); + services.AddTransientAs() .As(); diff --git a/src/Squidex/Config/Web/WebServices.cs b/src/Squidex/Config/Web/WebServices.cs index 65b899bba..bbb0ad4bd 100644 --- a/src/Squidex/Config/Web/WebServices.cs +++ b/src/Squidex/Config/Web/WebServices.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; using Squidex.Config.Domain; using Squidex.Pipeline; @@ -18,10 +19,10 @@ namespace Squidex.Config.Web services.AddSingletonAs() .AsSelf(); - services.AddSingletonAs() + services.AddSingletonAs() .AsSelf(); - services.AddSingletonAs() + services.AddSingletonAs() .AsSelf(); services.AddSingletonAs() @@ -33,10 +34,12 @@ namespace Squidex.Config.Web services.AddSingletonAs() .AsSelf(); + services.AddSingletonAs() + .As(); + services.AddMvc(options => { options.Filters.Add(); - options.Filters.Add(); options.Filters.Add(); }).AddMySerializers(); diff --git a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs index dc5375d28..62aa39efe 100644 --- a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs +++ b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Squidex.Infrastructure; @@ -30,6 +31,7 @@ namespace Squidex.Pipeline AddHandler(OnDomainObjectVersionException); AddHandler(OnDomainForbiddenException); AddHandler(OnDomainException); + AddHandler(OnSecurityException); } private static IActionResult OnDomainObjectNotFoundException(DomainObjectNotFoundException ex) @@ -52,6 +54,11 @@ namespace Squidex.Pipeline return ErrorResult(403, new ErrorDto { Message = ex.Message }); } + private static IActionResult OnSecurityException(SecurityException ex) + { + return ErrorResult(403, new ErrorDto { Message = ex.Message }); + } + private static IActionResult OnValidationException(ValidationException ex) { return ErrorResult(400, new ErrorDto { Message = ex.Summary, Details = ex.Errors?.Select(e => e.Message).ToArray() }); diff --git a/src/Squidex/Pipeline/ApiPermissionUnifier.cs b/src/Squidex/Pipeline/ApiPermissionUnifier.cs index f6ea7a364..98be130af 100644 --- a/src/Squidex/Pipeline/ApiPermissionUnifier.cs +++ b/src/Squidex/Pipeline/ApiPermissionUnifier.cs @@ -8,26 +8,26 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Authentication; using Squidex.Shared; using Squidex.Shared.Identity; namespace Squidex.Pipeline { - public sealed class ApiPermissionUnifier : IAsyncActionFilter + public sealed class ApiPermissionUnifier : IClaimsTransformation { - public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) - { - var user = context.HttpContext.User; + private const string AdministratorRole = "ADMINISTRATOR"; - var identity = user.Identities.First(); + public Task TransformAsync(ClaimsPrincipal principal) + { + var identity = principal.Identities.First(); - if (string.Equals(identity.FindFirst(identity.RoleClaimType)?.Value, SquidexRoles.Administrator)) + if (string.Equals(identity.FindFirst(identity.RoleClaimType)?.Value, AdministratorRole)) { identity.AddClaim(new Claim(SquidexClaimTypes.Permissions, Permissions.Admin)); } - return next(); + return Task.FromResult(principal); } } } diff --git a/src/Squidex/WebStartup.cs b/src/Squidex/WebStartup.cs index 119a69928..3bc5f10d9 100644 --- a/src/Squidex/WebStartup.cs +++ b/src/Squidex/WebStartup.cs @@ -41,10 +41,10 @@ namespace Squidex { app.ApplicationServices.LogConfiguration(); + app.UseMyTracking(); app.UseMyLocalCache(); app.UseMyCors(); app.UseMyForwardingRules(); - app.UseMyTracking(); app.ConfigureApi(); app.ConfigurePortal(); 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 39ce830d4..069e7d6b2 100644 --- a/src/Squidex/app/features/settings/pages/clients/client.component.html +++ b/src/Squidex/app/features/settings/pages/clients/client.component.html @@ -67,11 +67,11 @@
- Permission: + Role:
- +
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 220ebdf44..5fe8357d4 100644 --- a/src/Squidex/app/features/settings/pages/clients/client.component.ts +++ b/src/Squidex/app/features/settings/pages/clients/client.component.ts @@ -32,7 +32,7 @@ export class ClientComponent implements OnChanges { @Input() public client: AppClientDto; - public clientPermissions = [ 'Developer', 'Editor', 'Reader' ]; + public clientRoles = [ 'Owner', 'Developer', 'Editor', 'Reader' ]; public isRenaming = false; @@ -58,8 +58,8 @@ export class ClientComponent implements OnChanges { this.clientsState.revoke(this.client).pipe(onErrorResumeNext()).subscribe(); } - public update(permission: string) { - this.clientsState.update(this.client, new UpdateAppClientDto(undefined, permission)).pipe(onErrorResumeNext()).subscribe(); + public update(role: string) { + this.clientsState.update(this.client, new UpdateAppClientDto(undefined, role)).pipe(onErrorResumeNext()).subscribe(); } public toggleRename() { diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html index 144d99436..960a56778 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html @@ -32,8 +32,8 @@ {{contributorInfo.contributor.contributorId | sqxUserName}} - + diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts index 91c978c01..de74fde45 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts @@ -53,7 +53,7 @@ export class UsersDataSource implements AutocompleteSource { ] }) export class ContributorsPageComponent implements OnInit { - public usersPermissions = [ 'Owner', 'Developer', 'Editor' ]; + public usersRoles = [ 'Owner', 'Developer', 'Editor', 'Reader' ]; public assignContributorForm = new AssignContributorForm(this.formBuilder); @@ -77,8 +77,8 @@ export class ContributorsPageComponent implements OnInit { this.contributorsState.revoke(contributor).pipe(onErrorResumeNext()).subscribe(); } - public changePermission(contributor: AppContributorDto, permission: string) { - this.contributorsState.assign(new AppContributorDto(contributor.contributorId, permission)).pipe(onErrorResumeNext()).subscribe(); + public changeRole(contributor: AppContributorDto, role: string) { + this.contributorsState.assign(new AppContributorDto(contributor.contributorId, role)).pipe(onErrorResumeNext()).subscribe(); } public assignContributor() { diff --git a/src/Squidex/app/features/settings/settings-area.component.html b/src/Squidex/app/features/settings/settings-area.component.html index 7d9f49144..e2fe4376d 100644 --- a/src/Squidex/app/features/settings/settings-area.component.html +++ b/src/Squidex/app/features/settings/settings-area.component.html @@ -26,7 +26,7 @@