Browse Source

Feature/permission improvements (#402)

* Exclude permissions.

* Tests renamed.
pull/403/head
Sebastian Stehle 7 years ago
committed by GitHub
parent
commit
b1ccc5b1ca
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/Squidex.Infrastructure/CollectionExtensions.cs
  2. 81
      src/Squidex.Infrastructure/Security/Permission.Part.cs
  3. 64
      src/Squidex.Infrastructure/Security/Permission.cs
  4. 24
      src/Squidex.Web/PermissionExtensions.cs
  5. 41
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  6. 34
      tests/Squidex.Infrastructure.Tests/Security/PermissionSetTests.cs
  7. 83
      tests/Squidex.Infrastructure.Tests/Security/PermissionTests.cs

5
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -43,6 +43,11 @@ namespace Squidex.Infrastructure
return new HashSet<T>(enumerable); return new HashSet<T>(enumerable);
} }
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> enumerable, IEqualityComparer<T> comparer)
{
return new HashSet<T>(enumerable, comparer);
}
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> source) public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> source)
{ {
return source ?? Enumerable.Empty<T>(); return source ?? Enumerable.Empty<T>();

81
src/Squidex.Infrastructure/Security/Permission.Part.cs

@ -0,0 +1,81 @@
// ==========================================================================
// 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 partial class Permission
{
internal struct Part
{
private static readonly char[] AlternativeSeparators = { '|' };
private static readonly char[] MainSeparators = { '.' };
public readonly HashSet<string> Alternatives;
public readonly bool Exclusion;
public Part(HashSet<string> alternatives, bool exclusion)
{
Alternatives = alternatives;
Exclusion = exclusion;
}
public static Part[] ParsePath(string path)
{
return path
.Split(MainSeparators, StringSplitOptions.RemoveEmptyEntries)
.Select(Parse)
.ToArray();
}
public static Part Parse(string part)
{
var isExclusion = false;
if (part.StartsWith(Exclude, StringComparison.OrdinalIgnoreCase))
{
isExclusion = true;
part = part.Substring(1);
}
HashSet<string> alternatives = null;
if (part != Any)
{
alternatives =
part.Split(AlternativeSeparators, StringSplitOptions.RemoveEmptyEntries)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
}
return new Part(alternatives, isExclusion);
}
public static bool Intersects(ref Part lhs, ref Part rhs, bool allowNull)
{
if (lhs.Alternatives == null)
{
return true;
}
if (allowNull && rhs.Alternatives == null)
{
return true;
}
bool shouldIntersect = !(lhs.Exclusion ^ rhs.Exclusion);
return rhs.Alternatives != null && lhs.Alternatives.Intersect(rhs.Alternatives).Any() == shouldIntersect;
}
}
}
}

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

@ -6,19 +6,16 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace Squidex.Infrastructure.Security namespace Squidex.Infrastructure.Security
{ {
public sealed class Permission : IComparable<Permission>, IEquatable<Permission> public sealed partial class Permission : IComparable<Permission>, IEquatable<Permission>
{ {
public const string Any = "*"; public const string Any = "*";
public const string Exclude = "^";
private static readonly char[] MainSeparators = { '.' };
private static readonly char[] AlternativeSeparators = { '|' };
private readonly string id; private readonly string id;
private readonly Lazy<HashSet<string>[]> idParts; private readonly Lazy<Part[]> idParts;
public string Id public string Id
{ {
@ -31,20 +28,7 @@ namespace Squidex.Infrastructure.Security
this.id = id; this.id = id;
idParts = new Lazy<HashSet<string>[]>(() => id idParts = new Lazy<Part[]>(() => Part.ParsePath(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) public bool Allows(Permission permission)
@ -54,20 +38,29 @@ namespace Squidex.Infrastructure.Security
return false; return false;
} }
var lhs = idParts.Value; return Covers(idParts.Value, permission.idParts.Value);
var rhs = permission.idParts.Value; }
if (lhs.Length > rhs.Length) public bool Includes(Permission permission)
{
if (permission == null)
{ {
return false; return false;
} }
for (var i = 0; i < lhs.Length; i++) return PartialCovers(idParts.Value, permission.idParts.Value);
}
private static bool Covers(Part[] given, Part[] requested)
{
if (given.Length > requested.Length)
{ {
var l = lhs[i]; return false;
var r = rhs[i]; }
if (l != null && (r == null || !l.Intersect(r).Any())) for (var i = 0; i < given.Length; i++)
{
if (!Part.Intersects(ref given[i], ref requested[i], false))
{ {
return false; return false;
} }
@ -76,22 +69,11 @@ namespace Squidex.Infrastructure.Security
return true; return true;
} }
public bool Includes(Permission permission) private static bool PartialCovers(Part[] given, Part[] requested)
{ {
if (permission == null) for (var i = 0; i < Math.Min(given.Length, requested.Length); i++)
{
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]; if (!Part.Intersects(ref given[i], ref requested[i], true))
var r = rhs[i];
if (l != null && r != null && !l.Intersect(r).Any())
{ {
return false; return false;
} }

24
src/Squidex.Web/PermissionExtensions.cs

@ -7,6 +7,7 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using AllPermissions = Squidex.Shared.Permissions;
namespace Squidex.Web namespace Squidex.Web
{ {
@ -17,22 +18,27 @@ namespace Squidex.Web
return httpContext.Context().Permissions; return httpContext.Context().Permissions;
} }
public static bool HasPermission(this HttpContext httpContext, Permission permission, PermissionSet permissions = null) public static bool Includes(this HttpContext httpContext, Permission permission, PermissionSet additional = null)
{ {
return httpContext.Permissions().Includes(permission) || permissions?.Includes(permission) == true; return httpContext.Permissions().Includes(permission) || additional?.Includes(permission) == true;
} }
public static bool HasPermission(this HttpContext httpContext, string id, string app = "*", string schema = "*", PermissionSet permissions = null) public static bool Includes(this ApiController controller, Permission permission, PermissionSet additional = null)
{ {
return httpContext.HasPermission(Shared.Permissions.ForApp(id, app, schema), permissions); return controller.HttpContext.Includes(permission) || additional?.Includes(permission) == true;
} }
public static bool HasPermission(this ApiController controller, Permission permission, PermissionSet permissions = null) public static bool HasPermission(this HttpContext httpContext, Permission permission, PermissionSet additional = null)
{ {
return controller.HttpContext.HasPermission(permission, permissions); return httpContext.Permissions().Allows(permission) || additional?.Allows(permission) == true;
} }
public static bool HasPermission(this ApiController controller, string id, string app = "*", string schema = "*", PermissionSet permissions = null) public static bool HasPermission(this ApiController controller, Permission permission, PermissionSet additional = null)
{
return controller.HttpContext.HasPermission(permission) || additional?.Allows(permission) == true;
}
public static bool HasPermission(this ApiController controller, string id, string app = "*", string schema = "*", PermissionSet additional = null)
{ {
if (app == "*") if (app == "*")
{ {
@ -50,7 +56,9 @@ namespace Squidex.Web
} }
} }
return controller.HasPermission(Shared.Permissions.ForApp(id, app, schema), permissions); var permission = AllPermissions.ForApp(id, app, schema);
return controller.HasPermission(permission, additional);
} }
} }
} }

41
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -23,6 +23,8 @@ using Squidex.Shared;
using Squidex.Web; using Squidex.Web;
using AllPermissions = Squidex.Shared.Permissions; using AllPermissions = Squidex.Shared.Permissions;
#pragma warning disable RECS0033 // Convert 'if' to '||' expression
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
public sealed class AppDto : Resource public sealed class AppDto : Resource
@ -88,8 +90,15 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
result.Permissions = permissions.ToIds(); result.Permissions = permissions.ToIds();
result.PlanName = plans.GetPlanForApp(app)?.Name; result.PlanName = plans.GetPlanForApp(app)?.Name;
result.CanAccessApi = controller.HasPermission(AllPermissions.AppApi, app.Name, "*", permissions); if (controller.Includes(AllPermissions.ForApp(AllPermissions.AppApi, app.Name), permissions))
result.CanAccessContent = controller.HasPermission(AllPermissions.AppContentsRead, app.Name, "*", permissions); {
result.CanAccessApi = true;
}
if (controller.Includes(AllPermissions.ForApp(AllPermissions.AppContents, app.Name, "*"), permissions))
{
result.CanAccessContent = true;
}
if (controller.HasPermission(AllPermissions.AppPlansChange, app.Name)) if (controller.HasPermission(AllPermissions.AppPlansChange, app.Name))
{ {
@ -122,72 +131,72 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
AddGetLink("ping", controller.Url<PingController>(x => nameof(x.GetAppPing), values)); AddGetLink("ping", controller.Url<PingController>(x => nameof(x.GetAppPing), values));
if (controller.HasPermission(AllPermissions.AppDelete, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppDelete, Name, additional: permissions))
{ {
AddDeleteLink("delete", controller.Url<AppsController>(x => nameof(x.DeleteApp), values)); AddDeleteLink("delete", controller.Url<AppsController>(x => nameof(x.DeleteApp), values));
} }
if (controller.HasPermission(AllPermissions.AppAssetsRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppAssetsRead, Name, additional: permissions))
{ {
AddGetLink("assets", controller.Url<AssetsController>(x => nameof(x.GetAssets), values)); AddGetLink("assets", controller.Url<AssetsController>(x => nameof(x.GetAssets), values));
} }
if (controller.HasPermission(AllPermissions.AppBackupsRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppBackupsRead, Name, additional: permissions))
{ {
AddGetLink("backups", controller.Url<BackupsController>(x => nameof(x.GetBackups), values)); AddGetLink("backups", controller.Url<BackupsController>(x => nameof(x.GetBackups), values));
} }
if (controller.HasPermission(AllPermissions.AppClientsRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppClientsRead, Name, additional: permissions))
{ {
AddGetLink("clients", controller.Url<AppClientsController>(x => nameof(x.GetClients), values)); AddGetLink("clients", controller.Url<AppClientsController>(x => nameof(x.GetClients), values));
} }
if (controller.HasPermission(AllPermissions.AppContributorsRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppContributorsRead, Name, additional: permissions))
{ {
AddGetLink("contributors", controller.Url<AppContributorsController>(x => nameof(x.GetContributors), values)); AddGetLink("contributors", controller.Url<AppContributorsController>(x => nameof(x.GetContributors), values));
} }
if (controller.HasPermission(AllPermissions.AppCommon, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppCommon, Name, additional: permissions))
{ {
AddGetLink("languages", controller.Url<AppLanguagesController>(x => nameof(x.GetLanguages), values)); AddGetLink("languages", controller.Url<AppLanguagesController>(x => nameof(x.GetLanguages), values));
} }
if (controller.HasPermission(AllPermissions.AppCommon, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppCommon, Name, additional: permissions))
{ {
AddGetLink("patterns", controller.Url<AppPatternsController>(x => nameof(x.GetPatterns), values)); AddGetLink("patterns", controller.Url<AppPatternsController>(x => nameof(x.GetPatterns), values));
} }
if (controller.HasPermission(AllPermissions.AppPlansRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppPlansRead, Name, additional: permissions))
{ {
AddGetLink("plans", controller.Url<AppPlansController>(x => nameof(x.GetPlans), values)); AddGetLink("plans", controller.Url<AppPlansController>(x => nameof(x.GetPlans), values));
} }
if (controller.HasPermission(AllPermissions.AppRolesRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppRolesRead, Name, additional: permissions))
{ {
AddGetLink("roles", controller.Url<AppRolesController>(x => nameof(x.GetRoles), values)); AddGetLink("roles", controller.Url<AppRolesController>(x => nameof(x.GetRoles), values));
} }
if (controller.HasPermission(AllPermissions.AppRulesRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppRulesRead, Name, additional: permissions))
{ {
AddGetLink("rules", controller.Url<RulesController>(x => nameof(x.GetRules), values)); AddGetLink("rules", controller.Url<RulesController>(x => nameof(x.GetRules), values));
} }
if (controller.HasPermission(AllPermissions.AppCommon, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppCommon, Name, additional: permissions))
{ {
AddGetLink("schemas", controller.Url<SchemasController>(x => nameof(x.GetSchemas), values)); AddGetLink("schemas", controller.Url<SchemasController>(x => nameof(x.GetSchemas), values));
} }
if (controller.HasPermission(AllPermissions.AppWorkflowsRead, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppWorkflowsRead, Name, additional: permissions))
{ {
AddGetLink("workflows", controller.Url<AppWorkflowsController>(x => nameof(x.GetWorkflows), values)); AddGetLink("workflows", controller.Url<AppWorkflowsController>(x => nameof(x.GetWorkflows), values));
} }
if (controller.HasPermission(AllPermissions.AppSchemasCreate, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppSchemasCreate, Name, additional: permissions))
{ {
AddPostLink("schemas/create", controller.Url<SchemasController>(x => nameof(x.PostSchema), values)); AddPostLink("schemas/create", controller.Url<SchemasController>(x => nameof(x.PostSchema), values));
} }
if (controller.HasPermission(AllPermissions.AppAssetsCreate, Name, permissions: permissions)) if (controller.HasPermission(AllPermissions.AppAssetsCreate, Name, additional: permissions))
{ {
AddPostLink("assets/create", controller.Url<SchemasController>(x => nameof(x.PostSchema), values)); AddPostLink("assets/create", controller.Url<SchemasController>(x => nameof(x.PostSchema), values));
} }

34
tests/Squidex.Infrastructure.Tests/Security/PermissionSetTests.cs

@ -35,7 +35,7 @@ namespace Squidex.Infrastructure.Security
} }
[Fact] [Fact]
public void Should_return_true_if_any_permission_gives_permission_to_requested() public void Should_give_permission_if_any_permission_allows()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),
@ -45,57 +45,67 @@ namespace Squidex.Infrastructure.Security
} }
[Fact] [Fact]
public void Should_return_true_if_any_permission_includes_parent_given() public void Should_not_give_permission_if_none_permission_allows()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),
new Permission("app.assets")); new Permission("app.assets"));
Assert.True(sut.Includes(new Permission("app"))); Assert.False(sut.Allows(new Permission("app.schemas")));
} }
[Fact] [Fact]
public void Should_return_true_if_any_permission_includes_child_given() public void Should_not_give_permission_if_requested_is_null()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),
new Permission("app.assets")); new Permission("app.assets"));
Assert.True(sut.Includes(new Permission("app.contents.read"))); Assert.False(sut.Allows(null));
} }
[Fact] [Fact]
public void Should_return_false_if_none_permission_gives_permission_to_requested() public void Should_include_permission_if_any_permission_includes_parent_given()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),
new Permission("app.assets")); new Permission("app.assets"));
Assert.False(sut.Allows(new Permission("app.schemas"))); Assert.True(sut.Includes(new Permission("app")));
} }
[Fact] [Fact]
public void Should_return_false_if_none_permission_includes_given() public void Should_include_permission_if_any_permission_includes_child_given()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),
new Permission("app.assets")); new Permission("app.assets"));
Assert.False(sut.Includes(new Permission("other"))); Assert.True(sut.Includes(new Permission("app.contents.read")));
} }
[Fact] [Fact]
public void Should_return_false_if_permission_to_request_is_null() public void Should_include_permission_even_if_negation_exists()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),
new Permission("app.assets")); new Permission("app.assets"));
Assert.False(sut.Allows(null)); Assert.True(sut.Includes(new Permission("app.contents.read")));
}
[Fact]
public void Should_not_include_permission_if_none_permission_includes_given()
{
var sut = new PermissionSet(
new Permission("app.contents"),
new Permission("app.assets"));
Assert.False(sut.Includes(new Permission("other")));
} }
[Fact] [Fact]
public void Should_return_false_if_permission_to_include_is_null() public void Should_not_include_permission_if_permission_to_include_is_null()
{ {
var sut = new PermissionSet( var sut = new PermissionSet(
new Permission("app.contents"), new Permission("app.contents"),

83
tests/Squidex.Infrastructure.Tests/Security/PermissionTests.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Security
public class PermissionTests public class PermissionTests
{ {
[Fact] [Fact]
public void Should_generate_permissions() public void Should_generate_permission()
{ {
var sut = new Permission("app.contents"); var sut = new Permission("app.contents");
@ -23,112 +23,133 @@ namespace Squidex.Infrastructure.Security
} }
[Fact] [Fact]
public void Should_check_when_permissions_are_not_equal() public void Should_allow_and_include_when_permissions_are_equal()
{ {
var g = new Permission("app.contents"); var g = new Permission("app.contents");
var r = new Permission("app.assets"); var r = new Permission("app.contents");
Assert.False(g.Allows(r));
Assert.False(g.Includes(r)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_permissions_are_equal_with_wildcards() public void Should_not_allow_and_include_when_permissions_are_not_equal()
{ {
var g = new Permission("app.*"); var g = new Permission("app.contents");
var r = new Permission("app.*"); var r = new Permission("app.assets");
Assert.True(g.Allows(r));
Assert.True(g.Includes(r)); Assert.False(g.Allows(r));
Assert.False(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_equal_permissions() public void Should_allow_and_include_when_permissions_have_same_wildcards()
{ {
var g = new Permission("app.contents"); var g = new Permission("app.*");
var r = new Permission("app.contents"); var r = new Permission("app.*");
Assert.True(g.Allows(r)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_given_is_parent_of_requested() public void Should_allow_and_include_when_given_is_parent_of_requested()
{ {
var g = new Permission("app"); var g = new Permission("app");
var r = new Permission("app.contents"); var r = new Permission("app.contents");
Assert.True(g.Allows(r)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_requested_is_parent_of_given() public void Should_not_allow_but_include_when_requested_is_parent_of_given()
{ {
var g = new Permission("app.contents"); var g = new Permission("app.contents");
var r = new Permission("app"); var r = new Permission("app");
Assert.False(g.Allows(r)); Assert.False(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_given_is_wildcard_of_requested() public void Should_allow_and_include_when_given_is_wildcard_of_requested()
{ {
var g = new Permission("app.*"); var g = new Permission("app.*");
var r = new Permission("app.contents"); var r = new Permission("app.contents");
Assert.True(g.Allows(r)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_requested_is_wildcard_of_given() public void Should_not_allow_but_include_when_given_is_wildcard_of_requested()
{ {
var g = new Permission("app.contents"); var g = new Permission("app.contents");
var r = new Permission("app.*"); var r = new Permission("app.*");
Assert.False(g.Allows(r)); Assert.False(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_given_is_has_alternatives_of_requested() public void Should_allow_and_include_when_given_has_alternatives_of_requested()
{ {
var g = new Permission("app.contents|schemas"); var g = new Permission("app.contents|schemas");
var r = new Permission("app.contents"); var r = new Permission("app.contents");
Assert.True(g.Allows(r)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r));
}
[Fact]
public void Should_allow_and_include_when_given_has_not_excluded_requested()
{
var g = new Permission("app.^schemas");
var r = new Permission("app.contents");
Assert.True(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_requested_is_has_alternatives_of_given() public void Should_allow_and_include_when_requested_has_not_excluded_given()
{ {
var g = new Permission("app.contents"); var g = new Permission("app.contents");
var r = new Permission("app.contents|schemas"); var r = new Permission("app.^schemas");
Assert.True(g.Allows(r)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r));
}
[Fact]
public void Should_not_allow_and_include_when_given_has_excluded_requested()
{
var g = new Permission("app.^contents");
var r = new Permission("app.contents");
Assert.False(g.Allows(r));
Assert.False(g.Includes(r));
}
[Fact]
public void Should_not_allow_and_include_when_given_and_requested_have_same_exclusion()
{
var g = new Permission("app.^contents");
var r = new Permission("app.^contents");
Assert.True(g.Allows(r));
Assert.True(g.Includes(r)); Assert.True(g.Includes(r));
} }
[Fact] [Fact]
public void Should_check_when_requested_is_null() public void Should_allow_and_include_when_requested_is_has_alternatives_of_given()
{ {
var g = new Permission("app.contents"); var g = new Permission("app.contents");
var r = new Permission("app.contents|schemas");
Assert.False(g.Allows(null)); Assert.True(g.Allows(r));
Assert.True(g.Includes(r));
Assert.False(g.Includes(null));
} }
[Fact] [Fact]

Loading…
Cancel
Save