mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
242 changed files with 4975 additions and 1294 deletions
@ -0,0 +1,39 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Newtonsoft.Json; |
||||
|
using Squidex.Infrastructure.Json; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Apps.Json |
||||
|
{ |
||||
|
public sealed class RolesConverter : JsonClassConverter<Roles> |
||||
|
{ |
||||
|
protected override void WriteValue(JsonWriter writer, Roles value, JsonSerializer serializer) |
||||
|
{ |
||||
|
var json = new Dictionary<string, string[]>(value.Count); |
||||
|
|
||||
|
foreach (var role in value) |
||||
|
{ |
||||
|
json.Add(role.Key, role.Value.Permissions.ToIds().ToArray()); |
||||
|
} |
||||
|
|
||||
|
serializer.Serialize(writer, json); |
||||
|
} |
||||
|
|
||||
|
protected override Roles ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) |
||||
|
{ |
||||
|
var json = serializer.Deserialize<Dictionary<string, string[]>>(reader); |
||||
|
|
||||
|
return new Roles(json.ToImmutableDictionary(x => x.Key, x => new Role(x.Key, new PermissionSet(x.Value)))); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,91 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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 System.Diagnostics.Contracts; |
||||
|
using P = Squidex.Shared.Permissions; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Apps |
||||
|
{ |
||||
|
public sealed class Role : Named |
||||
|
{ |
||||
|
public const string Editor = "Editor"; |
||||
|
public const string Developer = "Developer"; |
||||
|
public const string Owner = "Owner"; |
||||
|
public const string Reader = "Reader"; |
||||
|
|
||||
|
private static readonly HashSet<string> DefaultRolesSet = new HashSet<string> |
||||
|
{ |
||||
|
Editor, |
||||
|
Developer, |
||||
|
Owner, |
||||
|
Reader |
||||
|
}; |
||||
|
|
||||
|
public PermissionSet Permissions { get; } |
||||
|
|
||||
|
public Role(string name, PermissionSet permissions) |
||||
|
: base(name) |
||||
|
{ |
||||
|
Guard.NotNull(permissions, nameof(permissions)); |
||||
|
|
||||
|
Permissions = permissions; |
||||
|
} |
||||
|
|
||||
|
public Role(string name, params Permission[] permissions) |
||||
|
: this(name, new PermissionSet(permissions)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
[Pure] |
||||
|
public Role Update(string[] permissions) |
||||
|
{ |
||||
|
return new Role(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.AppAssets, app), |
||||
|
P.ForApp(P.AppCommon, app), |
||||
|
P.ForApp(P.AppContents, 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)); |
||||
|
} |
||||
|
|
||||
|
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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,29 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using Squidex.Infrastructure; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Core.Apps |
|
||||
{ |
|
||||
public static class RoleExtension |
|
||||
{ |
|
||||
public static AppPermission ToAppPermission(this AppClientPermission clientPermission) |
|
||||
{ |
|
||||
Guard.Enum(clientPermission, nameof(clientPermission)); |
|
||||
|
|
||||
return (AppPermission)Enum.Parse(typeof(AppPermission), clientPermission.ToString()); |
|
||||
} |
|
||||
|
|
||||
public static AppPermission ToAppPermission(this AppContributorPermission contributorPermission) |
|
||||
{ |
|
||||
Guard.Enum(contributorPermission, nameof(contributorPermission)); |
|
||||
|
|
||||
return (AppPermission)Enum.Parse(typeof(AppPermission), contributorPermission.ToString()); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,69 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Diagnostics.Contracts; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Apps |
||||
|
{ |
||||
|
public sealed class Roles : DictionaryWrapper<string, Role> |
||||
|
{ |
||||
|
public static readonly Roles Empty = new Roles(); |
||||
|
|
||||
|
private Roles() |
||||
|
: base(ImmutableDictionary<string, Role>.Empty) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Roles(ImmutableDictionary<string, Role> inner) |
||||
|
: base(inner) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
[Pure] |
||||
|
public Roles Add(string name) |
||||
|
{ |
||||
|
var newRole = new Role(name); |
||||
|
|
||||
|
return new Roles(Inner.Add(name, newRole)); |
||||
|
} |
||||
|
|
||||
|
[Pure] |
||||
|
public Roles Remove(string name) |
||||
|
{ |
||||
|
return new Roles(Inner.Remove(name)); |
||||
|
} |
||||
|
|
||||
|
[Pure] |
||||
|
public Roles Update(string name, params string[] permissions) |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(name, nameof(name)); |
||||
|
Guard.NotNull(permissions, nameof(permissions)); |
||||
|
|
||||
|
if (!TryGetValue(name, out var role)) |
||||
|
{ |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
return new Roles(Inner.SetItem(name, role.Update(permissions))); |
||||
|
} |
||||
|
|
||||
|
public static Roles CreateDefaults(string app) |
||||
|
{ |
||||
|
return new Roles( |
||||
|
new Dictionary<string, Role> |
||||
|
{ |
||||
|
[Role.Developer] = Role.CreateDeveloper(app), |
||||
|
[Role.Editor] = Role.CreateEditor(app), |
||||
|
[Role.Owner] = Role.CreateOwner(app), |
||||
|
[Role.Reader] = Role.CreateReader(app) |
||||
|
}.ToImmutableDictionary()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps.Commands |
||||
|
{ |
||||
|
public sealed class AddRole : AppCommand |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps.Commands |
||||
|
{ |
||||
|
public sealed class DeleteRole : AppCommand |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -1,19 +1,16 @@ |
|||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Squidex Headless CMS
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
// All rights reserved. Licensed under the MIT license.
|
// All rights reserved. Licensed under the MIT license.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using Squidex.Domain.Apps.Core.Apps; |
namespace Squidex.Domain.Apps.Entities.Apps.Commands |
||||
|
|
||||
namespace Squidex.Pipeline |
|
||||
{ |
{ |
||||
public sealed class MustBeAppOwnerAttribute : AppPermissionAttribute |
public sealed class UpdateRole : AppCommand |
||||
{ |
{ |
||||
public MustBeAppOwnerAttribute() |
public string Name { get; set; } |
||||
: base(AppPermission.Owner) |
|
||||
{ |
public string[] Permissions { get; set; } |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Squidex.Domain.Apps.Core.Apps; |
||||
|
using Squidex.Domain.Apps.Entities.Apps.Commands; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps.Guards |
||||
|
{ |
||||
|
public static class GuardAppRoles |
||||
|
{ |
||||
|
public static void CanAdd(Roles roles, AddRole command) |
||||
|
{ |
||||
|
Guard.NotNull(command, nameof(command)); |
||||
|
|
||||
|
Validate.It(() => "Cannot add role.", e => |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(command.Name)) |
||||
|
{ |
||||
|
e("Name is required.", nameof(command.Name)); |
||||
|
} |
||||
|
else if (roles.ContainsKey(command.Name)) |
||||
|
{ |
||||
|
e("A role with the same name already exists."); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static void CanDelete(Roles roles, DeleteRole command, AppContributors contributors, AppClients clients) |
||||
|
{ |
||||
|
Guard.NotNull(command, nameof(command)); |
||||
|
|
||||
|
GetRoleOrThrow(roles, command.Name); |
||||
|
|
||||
|
Validate.It(() => "Cannot delete role.", e => |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(command.Name)) |
||||
|
{ |
||||
|
e("Name is required.", nameof(command.Name)); |
||||
|
} |
||||
|
else if (Role.IsDefaultRole(command.Name)) |
||||
|
{ |
||||
|
e("Cannot delete a default role."); |
||||
|
} |
||||
|
|
||||
|
if (clients.Values.Any(x => string.Equals(x.Role, command.Name, StringComparison.OrdinalIgnoreCase))) |
||||
|
{ |
||||
|
e("Cannot remove a role when a client is assigned."); |
||||
|
} |
||||
|
|
||||
|
if (contributors.Values.Any(x => string.Equals(x, command.Name, StringComparison.OrdinalIgnoreCase))) |
||||
|
{ |
||||
|
e("Cannot remove a role when a contributor is assigned."); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static void CanUpdate(Roles roles, UpdateRole command) |
||||
|
{ |
||||
|
Guard.NotNull(command, nameof(command)); |
||||
|
|
||||
|
GetRoleOrThrow(roles, command.Name); |
||||
|
|
||||
|
Validate.It(() => "Cannot delete role.", e => |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(command.Name)) |
||||
|
{ |
||||
|
e("Name is required.", nameof(command.Name)); |
||||
|
} |
||||
|
else if (Role.IsDefaultRole(command.Name)) |
||||
|
{ |
||||
|
e("Cannot update a default role."); |
||||
|
} |
||||
|
|
||||
|
if (command.Permissions == null) |
||||
|
{ |
||||
|
e("Permissions is required.", nameof(command.Permissions)); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static Role GetRoleOrThrow(Roles roles, string name) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(name)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (!roles.TryGetValue(name, out var role)) |
||||
|
{ |
||||
|
throw new DomainObjectNotFoundException(name, "Roles", typeof(IAppEntity)); |
||||
|
} |
||||
|
|
||||
|
return role; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// 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 |
||||
|
{ |
||||
|
public static class RoleExtensions |
||||
|
{ |
||||
|
public static string[] Prefix(this string[] permissions, string name) |
||||
|
{ |
||||
|
var result = new string[permissions.Length + 1]; |
||||
|
|
||||
|
result[0] = Permissions.ForApp(Permissions.AppCommon, name).Id; |
||||
|
|
||||
|
if (permissions.Length > 0) |
||||
|
{ |
||||
|
var prefix = Permissions.ForApp(Permissions.App, name).Id; |
||||
|
|
||||
|
for (var i = 0; i < permissions.Length; i++) |
||||
|
{ |
||||
|
result[i + 1] = string.Concat(prefix, ".", permissions[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
permissions = result; |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
using Squidex.Shared; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps |
||||
|
{ |
||||
|
public sealed class RolePermissionsProvider |
||||
|
{ |
||||
|
private readonly IAppProvider appProvider; |
||||
|
|
||||
|
public RolePermissionsProvider(IAppProvider appProvider) |
||||
|
{ |
||||
|
Guard.NotNull(appProvider, nameof(appProvider)); |
||||
|
|
||||
|
this.appProvider = appProvider; |
||||
|
} |
||||
|
|
||||
|
public async Task<List<string>> GetPermissionsAsync(IAppEntity app) |
||||
|
{ |
||||
|
var schemaNames = await GetSchemaNamesAsync(app); |
||||
|
|
||||
|
var result = new List<string> { Permission.Any }; |
||||
|
|
||||
|
foreach (var permission in Permissions.ForAppsNonSchema) |
||||
|
{ |
||||
|
if (permission.Length > Permissions.App.Length + 1) |
||||
|
{ |
||||
|
var trimmed = permission.Substring(Permissions.App.Length + 1); |
||||
|
|
||||
|
if (trimmed.Length > 0) |
||||
|
{ |
||||
|
result.Add(trimmed); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
foreach (var permission in Permissions.ForAppsSchema) |
||||
|
{ |
||||
|
var trimmed = permission.Substring(Permissions.App.Length + 1); |
||||
|
|
||||
|
foreach (var schema in schemaNames) |
||||
|
{ |
||||
|
var replaced = trimmed.Replace("{name}", schema); |
||||
|
|
||||
|
result.Add(replaced); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private async Task<List<string>> GetSchemaNamesAsync(IAppEntity app) |
||||
|
{ |
||||
|
var schemas = await appProvider.GetSchemasAsync(app.Id); |
||||
|
|
||||
|
var schemaNames = new List<string>(); ; |
||||
|
|
||||
|
schemaNames.Add(Permission.Any); |
||||
|
schemaNames.AddRange(schemas.Select(x => x.Name)); |
||||
|
|
||||
|
return schemaNames; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Events.Apps |
||||
|
{ |
||||
|
[EventType(nameof(AppRoleAdded))] |
||||
|
public sealed class AppRoleAdded : AppEvent |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Events.Apps |
||||
|
{ |
||||
|
[EventType(nameof(AppRoleDeleted))] |
||||
|
public sealed class AppRoleDeleted : AppEvent |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -1,19 +1,19 @@ |
|||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Squidex Headless CMS
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
// All rights reserved. Licensed under the MIT license.
|
// All rights reserved. Licensed under the MIT license.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using Squidex.Domain.Apps.Core.Apps; |
using Squidex.Infrastructure.EventSourcing; |
||||
|
|
||||
namespace Squidex.Pipeline |
namespace Squidex.Domain.Apps.Events.Apps |
||||
{ |
{ |
||||
public sealed class MustBeAppDeveloperAttribute : AppPermissionAttribute |
[EventType(nameof(AppRoleUpdated))] |
||||
|
public sealed class AppRoleUpdated : AppEvent |
||||
{ |
{ |
||||
public MustBeAppDeveloperAttribute() |
public string Name { get; set; } |
||||
: base(AppPermission.Developer) |
|
||||
{ |
public string[] Permissions { get; set; } |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,133 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Security |
||||
|
{ |
||||
|
public sealed class Permission : IComparable<Permission>, IEquatable<Permission> |
||||
|
{ |
||||
|
public const string Any = "*"; |
||||
|
|
||||
|
private static readonly char[] MainSeparators = { '.' }; |
||||
|
private static readonly char[] AlternativeSeparators = { '|' }; |
||||
|
private readonly string id; |
||||
|
private readonly Lazy<HashSet<string>[]> idParts; |
||||
|
|
||||
|
public string Id |
||||
|
{ |
||||
|
get { return id; } |
||||
|
} |
||||
|
|
||||
|
public Permission(string id) |
||||
|
{ |
||||
|
Guard.NotNullOrEmpty(id, nameof(id)); |
||||
|
|
||||
|
this.id = id; |
||||
|
|
||||
|
idParts = new Lazy<HashSet<string>[]>(() => id |
||||
|
.Split(MainSeparators, StringSplitOptions.RemoveEmptyEntries) |
||||
|
.Select(x => |
||||
|
{ |
||||
|
if (x == Any) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var alternatives = x.Split(AlternativeSeparators, StringSplitOptions.RemoveEmptyEntries); |
||||
|
|
||||
|
return new HashSet<string>(alternatives, StringComparer.OrdinalIgnoreCase); |
||||
|
}) |
||||
|
.ToArray()); |
||||
|
} |
||||
|
|
||||
|
public bool Allows(Permission permission) |
||||
|
{ |
||||
|
if (permission == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var lhs = idParts.Value; |
||||
|
var rhs = permission.idParts.Value; |
||||
|
|
||||
|
if (lhs.Length > rhs.Length) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
for (var i = 0; i < lhs.Length; i++) |
||||
|
{ |
||||
|
var l = lhs[i]; |
||||
|
var r = rhs[i]; |
||||
|
|
||||
|
if (l != null && (r == null || !l.Intersect(r).Any())) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public bool Includes(Permission permission) |
||||
|
{ |
||||
|
if (permission == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var lhs = idParts.Value; |
||||
|
var rhs = permission.idParts.Value; |
||||
|
|
||||
|
for (var i = 0; i < Math.Min(lhs.Length, rhs.Length); i++) |
||||
|
{ |
||||
|
var l = lhs[i]; |
||||
|
var r = rhs[i]; |
||||
|
|
||||
|
if (l != null && r != null && !l.Intersect(r).Any()) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public bool StartsWith(string id) |
||||
|
{ |
||||
|
return id.StartsWith(id, StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
return Equals(obj as Permission); |
||||
|
} |
||||
|
|
||||
|
public bool Equals(Permission other) |
||||
|
{ |
||||
|
return other != null && string.Equals(id, other.id, StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return id.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return id; |
||||
|
} |
||||
|
|
||||
|
public int CompareTo(Permission other) |
||||
|
{ |
||||
|
return other == null ? -1 : string.Compare(id, other.id, StringComparison.Ordinal); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,91 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Security |
||||
|
{ |
||||
|
public sealed class PermissionSet : IReadOnlyCollection<Permission> |
||||
|
{ |
||||
|
public static readonly PermissionSet Empty = new PermissionSet(new string[0]); |
||||
|
|
||||
|
private readonly List<Permission> permissions; |
||||
|
private readonly Lazy<string> display; |
||||
|
|
||||
|
public int Count |
||||
|
{ |
||||
|
get { return permissions.Count; } |
||||
|
} |
||||
|
|
||||
|
public PermissionSet(params Permission[] permissions) |
||||
|
: this((IEnumerable<Permission>)permissions) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public PermissionSet(params string[] permissions) |
||||
|
: this(permissions?.Select(x => new Permission(x))) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public PermissionSet(IEnumerable<string> permissions) |
||||
|
: this(permissions?.Select(x => new Permission(x))) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public PermissionSet(IEnumerable<Permission> permissions) |
||||
|
{ |
||||
|
Guard.NotNull(permissions, nameof(permissions)); |
||||
|
|
||||
|
this.permissions = permissions.ToList(); |
||||
|
|
||||
|
display = new Lazy<string>(() => string.Join(";", this.permissions)); |
||||
|
} |
||||
|
|
||||
|
public bool Allows(Permission other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return permissions.Any(x => x.Allows(other)); |
||||
|
} |
||||
|
|
||||
|
public bool Includes(Permission other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return permissions.Any(x => x.Includes(other)); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return display.Value; |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<string> ToIds() |
||||
|
{ |
||||
|
return permissions.Select(x => x.Id); |
||||
|
} |
||||
|
|
||||
|
public IEnumerator<Permission> GetEnumerator() |
||||
|
{ |
||||
|
return permissions.GetEnumerator(); |
||||
|
} |
||||
|
|
||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||
|
{ |
||||
|
return permissions.GetEnumerator(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,22 +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"; |
|
||||
|
|
||||
public static readonly string AppOwner = "app:owner"; |
|
||||
|
|
||||
public static readonly string AppEditor = "app:editor"; |
|
||||
|
|
||||
public static readonly string AppReader = "app:reader"; |
|
||||
|
|
||||
public static readonly string AppDeveloper = "app:dev"; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,180 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
|
||||
|
namespace Squidex.Shared |
||||
|
{ |
||||
|
public static class Permissions |
||||
|
{ |
||||
|
private static readonly List<string> ForAppsNonSchemaList = new List<string>(); |
||||
|
private static readonly List<string> ForAppsSchemaList = new List<string>(); |
||||
|
|
||||
|
public static IReadOnlyList<string> ForAppsNonSchema |
||||
|
{ |
||||
|
get { return ForAppsNonSchemaList; } |
||||
|
} |
||||
|
|
||||
|
public static IReadOnlyList<string> ForAppsSchema |
||||
|
{ |
||||
|
get { return ForAppsSchemaList; } |
||||
|
} |
||||
|
|
||||
|
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"; |
||||
|
public const string AdminRestoreCreate = "squidex.admin.restore.create"; |
||||
|
|
||||
|
public const string AdminEvents = "squidex.admin.events"; |
||||
|
public const string AdminEventsRead = "squidex.admin.events.read"; |
||||
|
public const string AdminEventsManage = "squidex.admin.events.manage"; |
||||
|
|
||||
|
public const string AdminUsers = "squidex.admin.users"; |
||||
|
public const string AdminUsersRead = "squidex.admin.users.read"; |
||||
|
public const string AdminUsersCreate = "squidex.admin.users.create"; |
||||
|
public const string AdminUsersUpdate = "squidex.admin.users.update"; |
||||
|
public const string AdminUsersUnlock = "squidex.admin.users.unlock"; |
||||
|
public const string AdminUsersLock = "squidex.admin.users.lock"; |
||||
|
|
||||
|
public const string App = "squidex.apps.{app}"; |
||||
|
public const string AppCommon = "squidex.apps.{app}.common"; |
||||
|
|
||||
|
public const string AppDelete = "squidex.apps.{app}.delete"; |
||||
|
|
||||
|
public const string AppClients = "squidex.apps.{app}.clients"; |
||||
|
public const string AppClientsRead = "squidex.apps.{app}.clients.read"; |
||||
|
public const string AppClientsCreate = "squidex.apps.{app}.clients.create"; |
||||
|
public const string AppClientsUpdate = "squidex.apps.{app}.clients.update"; |
||||
|
public const string AppClientsDelete = "squidex.apps.{app}.clients.delete"; |
||||
|
|
||||
|
public const string AppContributors = "squidex.apps.{app}.contributors"; |
||||
|
public const string AppContributorsRead = "squidex.apps.{app}.contributors.read"; |
||||
|
public const string AppContributorsAssign = "squidex.apps.{app}.contributors.assign"; |
||||
|
public const string AppContributorsRevoke = "squidex.apps.{app}.contributors.revoke"; |
||||
|
|
||||
|
public const string AppLanguages = "squidex.apps.{app}.languages"; |
||||
|
public const string AppLanguagesCreate = "squidex.apps.{app}.languages.create"; |
||||
|
public const string AppLanguagesUpdate = "squidex.apps.{app}.languages.update"; |
||||
|
public const string AppLanguagesDelete = "squidex.apps.{app}.languages.delete"; |
||||
|
|
||||
|
public const string AppRoles = "squidex.apps.{app}.roles"; |
||||
|
public const string AppRolesRead = "squidex.apps.{app}.roles.read"; |
||||
|
public const string AppRolesCreate = "squidex.apps.{app}.roles.create"; |
||||
|
public const string AppRolesUpdate = "squidex.apps.{app}.roles.update"; |
||||
|
public const string AppRolesDelete = "squidex.apps.{app}.roles.delete"; |
||||
|
|
||||
|
public const string AppPatterns = "squidex.apps.{app}.patterns"; |
||||
|
public const string AppPatternsRead = "squidex.apps.{app}.patterns.read"; |
||||
|
public const string AppPatternsCreate = "squidex.apps.{app}.patterns.create"; |
||||
|
public const string AppPatternsUpdate = "squidex.apps.{app}.patterns.update"; |
||||
|
public const string AppPatternsDelete = "squidex.apps.{app}.patterns.delete"; |
||||
|
|
||||
|
public const string AppBackups = "squidex.apps.{app}.backups"; |
||||
|
public const string AppBackupsRead = "squidex.apps.{app}.backups.read"; |
||||
|
public const string AppBackupsCreate = "squidex.apps.{app}.backups.create"; |
||||
|
public const string AppBackupsDelete = "squidex.apps.{app}.backups.delete"; |
||||
|
|
||||
|
public const string AppPlans = "squidex.apps.{app}.plans"; |
||||
|
public const string AppPlansRead = "squidex.apps.{app}.plans.read"; |
||||
|
public const string AppPlansChange = "squidex.apps.{app}.plans.change"; |
||||
|
|
||||
|
public const string AppAssets = "squidex.apps.{app}.assets"; |
||||
|
public const string AppAssetsRead = "squidex.apps.{app}.assets.read"; |
||||
|
public const string AppAssetsCreate = "squidex.apps.{app}.assets.create"; |
||||
|
public const string AppAssetsUpdate = "squidex.apps.{app}.assets.update"; |
||||
|
public const string AppAssetsDelete = "squidex.apps.{app}.assets.delete"; |
||||
|
|
||||
|
public const string AppRules = "squidex.apps.{app}.rules"; |
||||
|
public const string AppRulesRead = "squidex.apps.{app}.rules.read"; |
||||
|
public const string AppRulesCreate = "squidex.apps.{app}.rules.create"; |
||||
|
public const string AppRulesUpdate = "squidex.apps.{app}.rules.update"; |
||||
|
public const string AppRulesDisable = "squidex.apps.{app}.rules.disable"; |
||||
|
public const string AppRulesDelete = "squidex.apps.{app}.rules.delete"; |
||||
|
|
||||
|
public const string AppSchemas = "squidex.apps.{app}.schemas.{name}"; |
||||
|
public const string AppSchemasRead = "squidex.apps.{app}.schemas.{name}.read"; |
||||
|
public const string AppSchemasCreate = "squidex.apps.{app}.schemas.{name}.create"; |
||||
|
public const string AppSchemasUpdate = "squidex.apps.{app}.schemas.{name}.update"; |
||||
|
public const string AppSchemasScripts = "squidex.apps.{app}.schemas.{name}.scripts"; |
||||
|
public const string AppSchemasPublish = "squidex.apps.{app}.schemas.{name}.publish"; |
||||
|
public const string AppSchemasDelete = "squidex.apps.{app}.schemas.{name}.delete"; |
||||
|
|
||||
|
public const string AppContents = "squidex.apps.{app}.contents.{name}"; |
||||
|
public const string AppContentsRead = "squidex.apps.{app}.contents.{name}.read"; |
||||
|
public const string AppContentsGraphQL = "squidex.apps.{app}.contents.{name}.graphql"; |
||||
|
public const string AppContentsCreate = "squidex.apps.{app}.contents.{name}.create"; |
||||
|
public const string AppContentsUpdate = "squidex.apps.{app}.contents.{name}.update"; |
||||
|
public const string AppContentsDiscard = "squidex.apps.{app}.contents.{name}.discard"; |
||||
|
public const string AppContentsArchive = "squidex.apps.{app}.contents.{name}.archive"; |
||||
|
public const string AppContentsRestore = "squidex.apps.{app}.contents.{name}.restore"; |
||||
|
public const string AppContentsPublish = "squidex.apps.{app}.contents.{name}.publish"; |
||||
|
public const string AppContentsUnpublish = "squidex.apps.{app}.contents.{name}.unpublish"; |
||||
|
public const string AppContentsDelete = "squidex.apps.{app}.contents.{name}.delete"; |
||||
|
|
||||
|
public const string AppApi = "squidex.apps.{app}.api"; |
||||
|
|
||||
|
static Permissions() |
||||
|
{ |
||||
|
foreach (var field in typeof(Permissions).GetFields(BindingFlags.Public | BindingFlags.Static)) |
||||
|
{ |
||||
|
if (field.IsLiteral && !field.IsInitOnly) |
||||
|
{ |
||||
|
var value = (string)field.GetValue(null); |
||||
|
|
||||
|
if (value.StartsWith(App, StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
if (value.IndexOf("{name}", App.Length, StringComparison.OrdinalIgnoreCase) >= 0) |
||||
|
{ |
||||
|
ForAppsSchemaList.Add(value); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ForAppsNonSchemaList.Add(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static Permission ForApp(string id, string app = "*", string schema = "*") |
||||
|
{ |
||||
|
Guard.NotNull(id, nameof(id)); |
||||
|
|
||||
|
return new Permission(id.Replace("{app}", app ?? "*").Replace("{name}", schema ?? "*")); |
||||
|
} |
||||
|
|
||||
|
public static PermissionSet ToAppPermissions(this PermissionSet permissions, string app) |
||||
|
{ |
||||
|
var matching = permissions.Where(x => x.StartsWith($"squidex.apps.{app}")); |
||||
|
|
||||
|
return new PermissionSet(matching); |
||||
|
} |
||||
|
|
||||
|
public static string[] ToAppNames(this PermissionSet permissions) |
||||
|
{ |
||||
|
var matching = permissions.Where(x => x.StartsWith($"squidex.apps.")); |
||||
|
|
||||
|
var result = |
||||
|
matching |
||||
|
.Select(x => x.Id.Split('.')) |
||||
|
.Select(x => x[2]) |
||||
|
.Distinct() |
||||
|
.ToArray(); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,145 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
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; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Apps |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Manages and configures apps.
|
||||
|
/// </summary>
|
||||
|
[ApiExplorerSettings(GroupName = nameof(Apps))] |
||||
|
public sealed class AppRolesController : ApiController |
||||
|
{ |
||||
|
private readonly RolePermissionsProvider permissionsProvider; |
||||
|
|
||||
|
public AppRolesController(ICommandBus commandBus, RolePermissionsProvider permissionsProvider) |
||||
|
: base(commandBus) |
||||
|
{ |
||||
|
this.permissionsProvider = permissionsProvider; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get app roles.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <returns>
|
||||
|
/// 200 => App roles returned.
|
||||
|
/// 404 => App not found.
|
||||
|
/// </returns>
|
||||
|
[HttpGet] |
||||
|
[Route("apps/{app}/roles/")] |
||||
|
[ProducesResponseType(typeof(RolesDto), 200)] |
||||
|
[ApiPermission(Permissions.AppRolesRead)] |
||||
|
[ApiCosts(0)] |
||||
|
public IActionResult GetRoles(string app) |
||||
|
{ |
||||
|
var response = RolesDto.FromApp(App); |
||||
|
|
||||
|
Response.Headers["ETag"] = App.Version.ToString(); |
||||
|
|
||||
|
return Ok(response); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get app permissions.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <returns>
|
||||
|
/// 200 => App permissions returned.
|
||||
|
/// 404 => App not found.
|
||||
|
/// </returns>
|
||||
|
[HttpGet] |
||||
|
[Route("apps/{app}/roles/permissions")] |
||||
|
[ProducesResponseType(typeof(string[]), 200)] |
||||
|
[ApiPermission(Permissions.AppRolesRead)] |
||||
|
[ApiCosts(0)] |
||||
|
public async Task<IActionResult> GetPermissions(string app) |
||||
|
{ |
||||
|
var response = await permissionsProvider.GetPermissionsAsync(App); |
||||
|
|
||||
|
Response.Headers["ETag"] = string.Join(";", response).Sha256Base64(); |
||||
|
|
||||
|
return Ok(response); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Add role to app.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <param name="request">Role object that needs to be added to the app.</param>
|
||||
|
/// <returns>
|
||||
|
/// 200 => User assigned to app.
|
||||
|
/// 400 => Role name already in use.
|
||||
|
/// 404 => App not found.
|
||||
|
/// </returns>
|
||||
|
[HttpPost] |
||||
|
[Route("apps/{app}/roles/")] |
||||
|
[ProducesResponseType(typeof(ErrorDto), 400)] |
||||
|
[ApiPermission(Permissions.AppRolesCreate)] |
||||
|
[ApiCosts(1)] |
||||
|
public async Task<IActionResult> PostRole(string app, [FromBody] AddRoleDto request) |
||||
|
{ |
||||
|
var command = request.ToCommand(); |
||||
|
var context = await CommandBus.PublishAsync(command); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Update an existing app role.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <param name="role">The name of the role to be updated.</param>
|
||||
|
/// <param name="request">Role to be updated for the app.</param>
|
||||
|
/// <returns>
|
||||
|
/// 204 => Role updated.
|
||||
|
/// 400 => Role request not valid.
|
||||
|
/// 404 => Role or app not found.
|
||||
|
/// </returns>
|
||||
|
[HttpPut] |
||||
|
[Route("apps/{app}/roles/{role}/")] |
||||
|
[ApiPermission(Permissions.AppRolesUpdate)] |
||||
|
[ApiCosts(1)] |
||||
|
public async Task<IActionResult> UpdateRole(string app, string role, [FromBody] UpdateRoleDto request) |
||||
|
{ |
||||
|
await CommandBus.PublishAsync(request.ToCommand(role)); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Remove role from app.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <param name="role">The name of the role.</param>
|
||||
|
/// <returns>
|
||||
|
/// 204 => Role deleted.
|
||||
|
/// 400 => Role is in use by contributor or client or default role.
|
||||
|
/// 404 => Role or app not found.
|
||||
|
/// </returns>
|
||||
|
[HttpDelete] |
||||
|
[Route("apps/{app}/roles/{role}/")] |
||||
|
[ProducesResponseType(typeof(ErrorDto), 400)] |
||||
|
[ApiPermission(Permissions.AppRolesDelete)] |
||||
|
[ApiCosts(1)] |
||||
|
public async Task<IActionResult> DeleteRole(string app, string role) |
||||
|
{ |
||||
|
await CommandBus.PublishAsync(new DeleteRole { Name = role }); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Squidex.Domain.Apps.Entities.Apps.Commands; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Apps.Models |
||||
|
{ |
||||
|
public sealed class AddRoleDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The role name.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
public AddRole ToCommand() |
||||
|
{ |
||||
|
return new AddRole { Name = Name }; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
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 |
||||
|
{ |
||||
|
public sealed class RoleDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The role name.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The number of clients with this role.
|
||||
|
/// </summary>
|
||||
|
public int NumClients { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The number of contributors with this role.
|
||||
|
/// </summary>
|
||||
|
public int NumContributors { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associated list of permissions.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string[] Permissions { get; set; } |
||||
|
|
||||
|
public static RoleDto FromRole(Role role, IAppEntity app) |
||||
|
{ |
||||
|
var permissions = role.Permissions.WithoutApp(app.Name); |
||||
|
|
||||
|
return new RoleDto |
||||
|
{ |
||||
|
Name = role.Name, |
||||
|
NumClients = app.Clients.Count(x => string.Equals(x.Value.Role, role.Name, StringComparison.OrdinalIgnoreCase)), |
||||
|
NumContributors = app.Contributors.Count(x => string.Equals(x.Value, role.Name, StringComparison.OrdinalIgnoreCase)), |
||||
|
Permissions = permissions.ToIds().ToArray() |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using Squidex.Domain.Apps.Entities.Apps; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Apps.Models |
||||
|
{ |
||||
|
public sealed class RolesDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The app roles.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public RoleDto[] Roles { get; set; } |
||||
|
|
||||
|
public static RolesDto FromApp(IAppEntity app) |
||||
|
{ |
||||
|
var roles = app.Roles.Values.Select(x => RoleDto.FromRole(x, app)).ToArray(); |
||||
|
|
||||
|
return new RolesDto { Roles = roles }; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Squidex.Domain.Apps.Entities.Apps.Commands; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Apps.Models |
||||
|
{ |
||||
|
public sealed class UpdateRoleDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Associated list of permissions.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string[] Permissions { get; set; } |
||||
|
|
||||
|
public UpdateRole ToCommand(string name) |
||||
|
{ |
||||
|
return new UpdateRole { Name = name, Permissions = Permissions }; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue