Browse Source

Roles controller.

pull/332/head
Sebastian Stehle 7 years ago
parent
commit
05c4c02a20
  1. 35
      src/Squidex.Domain.Apps.Entities/Apps/RoleExtensions.cs
  2. 65
      src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs
  3. 2
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  4. 3
      src/Squidex.Infrastructure/Security/Permission.cs
  5. 52
      src/Squidex.Shared/Permissions.cs
  6. 4
      src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs
  7. 2
      src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs
  8. 4
      src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs
  9. 4
      src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs
  10. 122
      src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs
  11. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/AddLanguageDto.cs
  12. 26
      src/Squidex/Areas/Api/Controllers/Apps/Models/AddRoleDto.cs
  13. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs
  14. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/CreateClientDto.cs
  15. 33
      src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs
  16. 35
      src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs
  17. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateClientDto.cs
  18. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs
  19. 26
      src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateRoleDto.cs
  20. 63
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs
  21. 32
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs
  22. 60
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/RolePermissionsProviderTests.cs

35
src/Squidex.Domain.Apps.Entities/Apps/RoleExtensions.cs

@ -0,0 +1,35 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
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;
}
}
}

65
src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs

@ -0,0 +1,65 @@
// ==========================================================================
// 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 schemas = await appProvider.GetSchemasAsync(app.Id);
var schemaNames = schemas.Select(x => x.Name).ToList();
schemaNames.Insert(0, Permission.Any);
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;
}
}
}

2
src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs

@ -112,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
protected void On(AppRoleUpdated @event) protected void On(AppRoleUpdated @event)
{ {
Roles = Roles.Update(@event.Name, @event.Permissions); Roles = Roles.Update(@event.Name, @event.Permissions.Prefix(Name));
} }
protected void On(AppLanguageAdded @event) protected void On(AppLanguageAdded @event)

3
src/Squidex.Infrastructure/Security/Permission.cs

@ -13,7 +13,8 @@ namespace Squidex.Infrastructure.Security
{ {
public sealed class Permission : IComparable<Permission>, IEquatable<Permission> public sealed class Permission : IComparable<Permission>, IEquatable<Permission>
{ {
private const string Any = "*"; public const string Any = "*";
private static readonly char[] MainSeparators = { '.' }; private static readonly char[] MainSeparators = { '.' };
private static readonly char[] AlternativeSeparators = { '|' }; private static readonly char[] AlternativeSeparators = { '|' };
private readonly string id; private readonly string id;

52
src/Squidex.Shared/Permissions.cs

@ -5,7 +5,10 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
@ -13,6 +16,19 @@ namespace Squidex.Shared
{ {
public static class Permissions 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 All = "squidex.*";
public const string Admin = "squidex.admin.*"; public const string Admin = "squidex.admin.*";
@ -34,12 +50,9 @@ namespace Squidex.Shared
public const string AdminUsersLock = "squidex.admin.users.lock"; public const string AdminUsersLock = "squidex.admin.users.lock";
public const string App = "squidex.apps.{app}"; public const string App = "squidex.apps.{app}";
public const string AppDelete = "squidex.apps.{app}.delete";
public const string AppCommon = "squidex.apps.{app}.common"; public const string AppCommon = "squidex.apps.{app}.common";
public const string AppApi = "squidex.apps.{app}.api"; public const string AppDelete = "squidex.apps.{app}.delete";
public const string AppClients = "squidex.apps.{app}.clients"; public const string AppClients = "squidex.apps.{app}.clients";
public const string AppClientsRead = "squidex.apps.{app}.clients.read"; public const string AppClientsRead = "squidex.apps.{app}.clients.read";
@ -57,6 +70,12 @@ namespace Squidex.Shared
public const string AppLanguagesUpdate = "squidex.apps.{app}.languages.update"; public const string AppLanguagesUpdate = "squidex.apps.{app}.languages.update";
public const string AppLanguagesDelete = "squidex.apps.{app}.languages.delete"; 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 AppPatterns = "squidex.apps.{app}.patterns";
public const string AppPatternsRead = "squidex.apps.{app}.patterns.read"; public const string AppPatternsRead = "squidex.apps.{app}.patterns.read";
public const string AppPatternsCreate = "squidex.apps.{app}.patterns.create"; public const string AppPatternsCreate = "squidex.apps.{app}.patterns.create";
@ -105,6 +124,31 @@ namespace Squidex.Shared
public const string AppContentsUnpublish = "squidex.apps.{app}.contents.{name}.unpublish"; public const string AppContentsUnpublish = "squidex.apps.{app}.contents.{name}.unpublish";
public const string AppContentsDelete = "squidex.apps.{app}.contents.{name}.delete"; 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 = "*") public static Permission ForApp(string id, string app = "*", string schema = "*")
{ {
Guard.NotNull(id, nameof(id)); Guard.NotNull(id, nameof(id));

4
src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs

@ -70,7 +70,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ProducesResponseType(typeof(ClientDto), 201)] [ProducesResponseType(typeof(ClientDto), 201)]
[ApiPermission(Permissions.AppClientsCreate)] [ApiPermission(Permissions.AppClientsCreate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostClient(string app, [FromBody] CreateAppClientDto request) public async Task<IActionResult> PostClient(string app, [FromBody] CreateClientDto request)
{ {
var command = request.ToCommand(); var command = request.ToCommand();
@ -99,7 +99,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[Route("apps/{app}/clients/{clientId}/")] [Route("apps/{app}/clients/{clientId}/")]
[ApiPermission(Permissions.AppClientsUpdate)] [ApiPermission(Permissions.AppClientsUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutClient(string app, string clientId, [FromBody] UpdateAppClientDto request) public async Task<IActionResult> PutClient(string app, string clientId, [FromBody] UpdateClientDto request)
{ {
await CommandBus.PublishAsync(request.ToCommand(clientId)); await CommandBus.PublishAsync(request.ToCommand(clientId));

2
src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs

@ -68,7 +68,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppContributorsAssign)] [ApiPermission(Permissions.AppContributorsAssign)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostContributor(string app, [FromBody] AssignAppContributorDto request) public async Task<IActionResult> PostContributor(string app, [FromBody] AssignContributorDto request)
{ {
var command = request.ToCommand(); var command = request.ToCommand();
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);

4
src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs

@ -66,7 +66,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppLanguagesCreate)] [ApiPermission(Permissions.AppLanguagesCreate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostLanguage(string app, [FromBody] AddAppLanguageDto request) public async Task<IActionResult> PostLanguage(string app, [FromBody] AddLanguageDto request)
{ {
var command = request.ToCommand(); var command = request.ToCommand();
@ -92,7 +92,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[Route("apps/{app}/languages/{language}/")] [Route("apps/{app}/languages/{language}/")]
[ApiPermission(Permissions.AppLanguagesUpdate)] [ApiPermission(Permissions.AppLanguagesUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> Update(string app, string language, [FromBody] UpdateAppLanguageDto request) public async Task<IActionResult> Update(string app, string language, [FromBody] UpdateLanguageDto request)
{ {
await CommandBus.PublishAsync(request.ToCommand(ParseLanguage(language))); await CommandBus.PublishAsync(request.ToCommand(ParseLanguage(language)));

4
src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs

@ -54,7 +54,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
} }
/// <summary> /// <summary>
/// Create a new app patterm. /// Create a new app pattern.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="request">Pattern to be added to the app.</param> /// <param name="request">Pattern to be added to the app.</param>
@ -80,7 +80,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
} }
/// <summary> /// <summary>
/// Update an existing app patterm. /// Update an existing app pattern.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="id">The id of the pattern to be updated.</param> /// <param name="id">The id of the pattern to be updated.</param>

122
src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs

@ -0,0 +1,122 @@
// ==========================================================================
// 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.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 async Task<IActionResult> GetRoles(string app)
{
var response = RolesDto.FromApp(App, await permissionsProvider.GetPermissionsAsync(App));
Response.Headers["ETag"] = App.Version.ToString();
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();
}
}
}

2
src/Squidex/Areas/Api/Controllers/Apps/Models/AddAppLanguageDto.cs → src/Squidex/Areas/Api/Controllers/Apps/Models/AddLanguageDto.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
public sealed class AddAppLanguageDto public sealed class AddLanguageDto
{ {
/// <summary> /// <summary>
/// The language to add. /// The language to add.

26
src/Squidex/Areas/Api/Controllers/Apps/Models/AddRoleDto.cs

@ -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 };
}
}
}

2
src/Squidex/Areas/Api/Controllers/Apps/Models/AssignAppContributorDto.cs → src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
public sealed class AssignAppContributorDto public sealed class AssignContributorDto
{ {
/// <summary> /// <summary>
/// The id or email of the user to add to the app. /// The id or email of the user to add to the app.

2
src/Squidex/Areas/Api/Controllers/Apps/Models/CreateAppClientDto.cs → src/Squidex/Areas/Api/Controllers/Apps/Models/CreateClientDto.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
public sealed class CreateAppClientDto public sealed class CreateClientDto
{ {
/// <summary> /// <summary>
/// The id of the client. /// The id of the client.

33
src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs

@ -0,0 +1,33 @@
// ==========================================================================
// 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.Core.Apps;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
public sealed class RoleDto
{
/// <summary>
/// The role name.
/// </summary>
[Required]
public string Name { get; set; }
/// <summary>
/// Associated list of permissions.
/// </summary>
[Required]
public string[] Permissions { get; set; }
public static RoleDto FromRole(Role role)
{
return new RoleDto { Name = role.Name, Permissions = role.Permissions.Select(x => x.Id).ToArray() };
}
}
}

35
src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs

@ -0,0 +1,35 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
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 roles.
/// </summary>
[Required]
public RoleDto[] Roles { get; set; }
/// <summary>
/// Suggested permissions.
/// </summary>
public string[] AllPermissions { get; set; }
public static RolesDto FromApp(IAppEntity app, List<string> permissions)
{
var roles = app.Roles.Values.Select(RoleDto.FromRole).ToArray();
return new RolesDto { Roles = roles, AllPermissions = permissions.ToList() };
}
}
}

2
src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppClientDto.cs → src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateClientDto.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
public sealed class UpdateAppClientDto public sealed class UpdateClientDto
{ {
/// <summary> /// <summary>
/// The new display name of the client. /// The new display name of the client.

2
src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppLanguageDto.cs → src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
public sealed class UpdateAppLanguageDto public sealed class UpdateLanguageDto
{ {
/// <summary> /// <summary>
/// Set the value to true to make the language the master. /// Set the value to true to make the language the master.

26
src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateRoleDto.cs

@ -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 };
}
}
}

63
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs

@ -32,6 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
private readonly string contributorId = Guid.NewGuid().ToString(); private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientId = "client"; private readonly string clientId = "client";
private readonly string clientNewName = "My Client"; private readonly string clientNewName = "My Client";
private readonly string roleName = "My Role";
private readonly string planId = "premium"; private readonly string planId = "premium";
private readonly AppGrain sut; private readonly AppGrain sut;
private readonly Guid patternId1 = Guid.NewGuid(); private readonly Guid patternId1 = Guid.NewGuid();
@ -313,6 +314,63 @@ namespace Squidex.Domain.Apps.Entities.Apps
); );
} }
[Fact]
public async Task AddRole_should_create_events_and_update_state()
{
var command = new AddRole { Name = roleName };
await ExecuteCreateAsync();
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(5));
Assert.Equal(5, sut.Snapshot.Roles.Count);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppRoleAdded { Name = roleName })
);
}
[Fact]
public async Task DeleteRole_should_create_events_and_update_state()
{
var command = new DeleteRole { Name = roleName };
await ExecuteCreateAsync();
await ExecuteAddRoleAsync();
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(6));
Assert.Equal(4, sut.Snapshot.Roles.Count);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppRoleDeleted { Name = roleName })
);
}
[Fact]
public async Task UpdateRole_should_create_events_and_update_state()
{
var command = new UpdateRole { Name = roleName, Permissions = new[] { "clients.read" } };
await ExecuteCreateAsync();
await ExecuteAddRoleAsync();
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(6));
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppRoleUpdated { Name = roleName, Permissions = new[] { "clients.read" } })
);
}
[Fact] [Fact]
public async Task AddPattern_should_create_events_and_update_state() public async Task AddPattern_should_create_events_and_update_state()
{ {
@ -410,6 +468,11 @@ namespace Squidex.Domain.Apps.Entities.Apps
return sut.ExecuteAsync(CreateCommand(new AttachClient { Id = clientId })); return sut.ExecuteAsync(CreateCommand(new AttachClient { Id = clientId }));
} }
private Task ExecuteAddRoleAsync()
{
return sut.ExecuteAsync(CreateCommand(new AddRole { Name = roleName }));
}
private Task ExecuteAddLanguageAsync(Language language) private Task ExecuteAddLanguageAsync(Language language)
{ {
return sut.ExecuteAsync(CreateCommand(new AddLanguage { Language = language })); return sut.ExecuteAsync(CreateCommand(new AddLanguage { Language = language }));

32
tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs

@ -0,0 +1,32 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Xunit;
namespace Squidex.Domain.Apps.Entities.Apps
{
public class RoleExtensionsRests
{
[Fact]
public void Should_add_common_permission()
{
var source = new string[0];
var result = source.Prefix("my-app");
Assert.Equal(new[] { "squidex.apps.my-app.common" }, result);
}
[Fact]
public void Should_prefix_permission()
{
var source = new[] { "clients.read" };
var result = source.Prefix("my-app");
Assert.Equal("squidex.apps.my-app.clients.read", result[1]);
}
}
}

60
tests/Squidex.Domain.Apps.Entities.Tests/Apps/RolePermissionsProviderTests.cs

@ -0,0 +1,60 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Entities.Schemas;
using Xunit;
#pragma warning disable xUnit2017 // Do not use Contains() to check if a value exists in a collection
namespace Squidex.Domain.Apps.Entities.Apps
{
public class RolePermissionsProviderTests
{
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly IAppEntity app = A.Fake<IAppEntity>();
private readonly RolePermissionsProvider sut;
public RolePermissionsProviderTests()
{
A.CallTo(() => app.Name).Returns("my-app");
sut = new RolePermissionsProvider(appProvider);
}
[Fact]
public async Task Should_provide_all_permissions()
{
A.CallTo(() => appProvider.GetSchemasAsync(A<Guid>.Ignored))
.Returns(new List<ISchemaEntity>
{
CreateSchema("schema1"),
CreateSchema("schema2")
});
var result = await sut.GetPermissionsAsync(app);
Assert.True(result.Contains("*"));
Assert.True(result.Contains("clients.read"));
Assert.True(result.Contains("schemas.*.read"));
Assert.True(result.Contains("schemas.schema1.read"));
Assert.True(result.Contains("schemas.schema2.read"));
}
private ISchemaEntity CreateSchema(string name)
{
var schema = A.Fake<ISchemaEntity>();
A.CallTo(() => schema.Name).Returns(name);
return schema;
}
}
}
Loading…
Cancel
Save