Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf4d68e37d | ||
|
|
d48ad7463b | ||
|
|
600276b481 | ||
|
|
0adaa55856 | ||
|
|
79efde18b5 | ||
|
|
fd2a7cfcad | ||
|
|
f039a7be67 |
+8
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
using Umbraco.Cms.Api.Management.Security.Authorization.Content;
|
||||
using Umbraco.Cms.Api.Management.Security.Authorization.Contextual;
|
||||
using Umbraco.Cms.Api.Management.Security.Authorization.DenyLocalLogin;
|
||||
using Umbraco.Cms.Api.Management.Security.Authorization.Dictionary;
|
||||
using Umbraco.Cms.Api.Management.Security.Authorization.Media;
|
||||
@@ -30,6 +31,7 @@ internal static class BackOfficeAuthPolicyBuilderExtensions
|
||||
builder.Services.AddSingleton<IAuthorizationHandler, UserPermissionHandler>();
|
||||
builder.Services.AddSingleton<IAuthorizationHandler, AllowedApplicationHandler>();
|
||||
builder.Services.AddSingleton<IAuthorizationHandler, BackOfficeHandler>();
|
||||
builder.Services.AddSingleton<IAuthorizationHandler, ContextualPermissionHandler>();
|
||||
|
||||
builder.Services.AddAuthorization(CreatePolicies);
|
||||
return builder;
|
||||
@@ -139,5 +141,11 @@ internal static class BackOfficeAuthPolicyBuilderExtensions
|
||||
policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
|
||||
policy.Requirements.Add(new UserPermissionRequirement());
|
||||
});
|
||||
|
||||
options.AddPolicy(ContextualPermissionHandler.ContextualPermissionsPolicyAlias, policy =>
|
||||
{
|
||||
policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
|
||||
policy.Requirements.Add(new ContextualPermissionRequirement());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Models.Membership.Permissions;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Security.Authorization.Contextual;
|
||||
|
||||
/// <summary>
|
||||
/// Authorizes a certain permission (read,write,browse,...) within a given context (umbraco, my-package,...)
|
||||
/// against the <see cref="IGranularPermission"> granular permissions</see> defined on all <see cref="IUserGroup">user groups</see> the <see cref="IUser">user</see> is part off.
|
||||
/// This is accomplished by validating the <see cref="ContextualPermissionHandler.ContextualPermissionsPolicyAlias">Contextual Permissions Policy</see> trough the <see cref="IAuthorizationService"/>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class ContextualAuthorizeAttribute : Attribute, IAsyncAuthorizationFilter
|
||||
{
|
||||
public ContextualAuthorizeAttribute(string permission, string context)
|
||||
{
|
||||
Permissions = permission.Yield();
|
||||
Context = context;
|
||||
PermissionMatchingBehaviour = PermissionMatchingBehaviour.All;
|
||||
PermissionScopeMatchingBehaviour = PermissionScopeMatchingBehaviour.Any;
|
||||
}
|
||||
|
||||
public ContextualAuthorizeAttribute(
|
||||
IEnumerable<string> permissions,
|
||||
string context,
|
||||
PermissionMatchingBehaviour permissionMatchingBehaviour,
|
||||
PermissionScopeMatchingBehaviour permissionScopeMatchingBehaviour)
|
||||
{
|
||||
Permissions = permissions;
|
||||
Context = context;
|
||||
PermissionMatchingBehaviour = permissionMatchingBehaviour;
|
||||
PermissionScopeMatchingBehaviour = permissionScopeMatchingBehaviour;
|
||||
}
|
||||
|
||||
private IEnumerable<string> Permissions { get; }
|
||||
|
||||
private string Context { get; }
|
||||
|
||||
private PermissionMatchingBehaviour PermissionMatchingBehaviour { get; }
|
||||
|
||||
private PermissionScopeMatchingBehaviour PermissionScopeMatchingBehaviour { get; }
|
||||
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
IAuthorizationService authorizationService = context.HttpContext.RequestServices.GetRequiredService<IAuthorizationService>();
|
||||
|
||||
AuthorizationResult authorizationResult = await authorizationService.AuthorizeResourceAsync(
|
||||
context.HttpContext.User,
|
||||
ContextualPermissionResource.WithSetup(Permissions, Context, PermissionMatchingBehaviour, PermissionScopeMatchingBehaviour),
|
||||
ContextualPermissionHandler.ContextualPermissionsPolicyAlias);
|
||||
|
||||
if (authorizationResult.Succeeded is false)
|
||||
{
|
||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
context.HttpContext.Response.HttpContext.Features.Get<IHttpResponseFeature>()!.ReasonPhrase =
|
||||
"Invalid contextual permission";
|
||||
context.Result = new JsonResult("Invalid contextual permission")
|
||||
{
|
||||
Value = new { Status = "Unauthorized", Message = "Invalid contextual permission" },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Models.Membership.Permissions;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Security.Authorization.Contextual;
|
||||
|
||||
/// Authorizes a certain permission (read,write,browse,...) within a given context (umbraco, my-package,...)
|
||||
/// against the <see cref="IGranularPermission">granular permissions</see> defined on all <see cref="IUserGroup">user groups</see> the <see cref="IUser">user</see> is part off.
|
||||
/// Permissions and Context are checked on a InvariantCultureIgnoreCase basis.
|
||||
public class ContextualPermissionHandler : MustSatisfyRequirementAuthorizationHandler<ContextualPermissionRequirement,
|
||||
ContextualPermissionResource>
|
||||
{
|
||||
public const string ContextualPermissionsPolicyAlias = "Umbraco.ContextualPermissions";
|
||||
private readonly IAuthorizationHelper _authorizationHelper;
|
||||
private readonly IContextualPermissionAuthorizer _contextualPermissionAuthorizer;
|
||||
|
||||
public ContextualPermissionHandler(
|
||||
IAuthorizationHelper authorizationHelper,
|
||||
IContextualPermissionAuthorizer contextualPermissionAuthorizer)
|
||||
{
|
||||
_authorizationHelper = authorizationHelper;
|
||||
_contextualPermissionAuthorizer = contextualPermissionAuthorizer;
|
||||
}
|
||||
|
||||
protected override async Task<bool> IsAuthorized(
|
||||
AuthorizationHandlerContext context,
|
||||
ContextualPermissionRequirement requirement,
|
||||
ContextualPermissionResource resource)
|
||||
{
|
||||
IUser user = _authorizationHelper.GetUmbracoUser(context.User);
|
||||
|
||||
return _contextualPermissionAuthorizer.IsDenied(user, resource) is false;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Security.Authorization.Contextual;
|
||||
|
||||
public class ContextualPermissionRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
@@ -433,6 +433,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
Services.AddSingleton<IMediaPermissionAuthorizer, MediaPermissionAuthorizer>();
|
||||
Services.AddSingleton<IUserGroupPermissionAuthorizer, UserGroupPermissionAuthorizer>();
|
||||
Services.AddSingleton<IUserPermissionAuthorizer, UserPermissionAuthorizer>();
|
||||
Services.AddSingleton<IContextualPermissionAuthorizer, ContextualPermissionAuthorizer>();
|
||||
|
||||
// Segments
|
||||
Services.AddUnique<ISegmentService, NoopSegmentService>();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Umbraco.Cms.Core.Models.Membership.Permissions;
|
||||
|
||||
public interface IExternalGranularPermission : IGranularPermission
|
||||
{
|
||||
new Guid Key { get; set; }
|
||||
|
||||
Guid? IGranularPermission.Key
|
||||
{
|
||||
get => Key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security.Authorization;
|
||||
|
||||
public class ContextualPermissionAuthorizer : IContextualPermissionAuthorizer
|
||||
{
|
||||
public bool IsDenied(IUser currentUser, ContextualPermissionResource resource)
|
||||
{
|
||||
return resource.PermissionMatchingBehaviour == PermissionMatchingBehaviour.Any
|
||||
? !resource.Permissions.Any(ContextualMatch)
|
||||
: !resource.Permissions.All(ContextualMatch);
|
||||
|
||||
bool ContextualMatch(string permissionToCheck) =>
|
||||
currentUser.Groups.SelectMany(g => g.GranularPermissions)
|
||||
.Any(definedContextualPermission =>
|
||||
(resource.PermissionScopeMatchingBehaviour == PermissionScopeMatchingBehaviour.Any
|
||||
|| (resource.PermissionScopeMatchingBehaviour == PermissionScopeMatchingBehaviour.ContextWideOnly && definedContextualPermission.Key is null))
|
||||
&& definedContextualPermission.Context.Equals(resource.Context, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& definedContextualPermission.Permission.Equals(permissionToCheck, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security.Authorization;
|
||||
|
||||
public class ContextualPermissionResource : IPermissionResource
|
||||
{
|
||||
public static ContextualPermissionResource WithPermission(string permission, string context) =>
|
||||
WithAllPermissions(permission.Yield(), context);
|
||||
|
||||
public static ContextualPermissionResource WithContextWidePermission(string permission, string context) =>
|
||||
WithAllContextWidePermissions(permission.Yield(), context);
|
||||
|
||||
public static ContextualPermissionResource WithAnyContextWidePermissions(IEnumerable<string> permissions, string context) =>
|
||||
new(permissions, context, PermissionMatchingBehaviour.Any, PermissionScopeMatchingBehaviour.ContextWideOnly);
|
||||
|
||||
public static ContextualPermissionResource WithAllContextWidePermissions(IEnumerable<string> permissions, string context) =>
|
||||
new(permissions, context, PermissionMatchingBehaviour.All, PermissionScopeMatchingBehaviour.ContextWideOnly);
|
||||
|
||||
public static ContextualPermissionResource WithAnyPermissions(IEnumerable<string> permissions, string context) =>
|
||||
new(permissions, context, PermissionMatchingBehaviour.Any, PermissionScopeMatchingBehaviour.Any);
|
||||
|
||||
public static ContextualPermissionResource WithAllPermissions(IEnumerable<string> permissions, string context) =>
|
||||
new(permissions, context, PermissionMatchingBehaviour.All, PermissionScopeMatchingBehaviour.Any);
|
||||
|
||||
public static ContextualPermissionResource WithSetup(
|
||||
IEnumerable<string> permissions,
|
||||
string context,
|
||||
PermissionMatchingBehaviour permissionMatchingBehaviour,
|
||||
PermissionScopeMatchingBehaviour permissionScopeMatchingBehaviour) =>
|
||||
new(permissions, context, permissionMatchingBehaviour, permissionScopeMatchingBehaviour);
|
||||
|
||||
private ContextualPermissionResource(
|
||||
IEnumerable<string> permissions,
|
||||
string context,
|
||||
PermissionMatchingBehaviour permissionMatchingBehaviour,
|
||||
PermissionScopeMatchingBehaviour permissionScopeMatchingBehaviour)
|
||||
{
|
||||
Permissions = permissions;
|
||||
Context = context;
|
||||
PermissionMatchingBehaviour = permissionMatchingBehaviour;
|
||||
PermissionScopeMatchingBehaviour = permissionScopeMatchingBehaviour;
|
||||
}
|
||||
|
||||
public IEnumerable<string> Permissions { get; }
|
||||
|
||||
public string Context { get; }
|
||||
|
||||
public PermissionMatchingBehaviour PermissionMatchingBehaviour { get; }
|
||||
|
||||
public PermissionScopeMatchingBehaviour PermissionScopeMatchingBehaviour { get; }
|
||||
}
|
||||
|
||||
public enum PermissionMatchingBehaviour
|
||||
{
|
||||
Any,
|
||||
All,
|
||||
}
|
||||
|
||||
public enum PermissionScopeMatchingBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Both context wide and granular
|
||||
/// </summary>
|
||||
Any,
|
||||
|
||||
/// <summary>
|
||||
/// Only Context wide
|
||||
/// </summary>
|
||||
ContextWideOnly,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security.Authorization;
|
||||
|
||||
public interface IContextualPermissionAuthorizer
|
||||
{
|
||||
bool IsDenied(IUser currentUser, ContextualPermissionResource resource);
|
||||
}
|
||||
@@ -111,5 +111,8 @@ public class UmbracoPlan : MigrationPlan
|
||||
|
||||
// To 15.3.0
|
||||
To<V_15_3_0.AddNameAndDescriptionToWebhooks>("{7B11F01E-EE33-4B0B-81A1-F78F834CA45B}");
|
||||
|
||||
// To 16.0.0
|
||||
To<V_16_0_0.AddExternalIdToGranularPermissions>("{7B5D73D9-7C7C-4695-A918-655C4C09D181}");
|
||||
}
|
||||
}
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_16_0_0;
|
||||
|
||||
// todo move to corescope?
|
||||
public class AddExternalIdToGranularPermissions : UnscopedMigrationBase
|
||||
{
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private const string NewColumnName = "umbracoExternalIds";
|
||||
private const string NewIndex = "IX_umbracoUserGroup2GranularPermissionDto_ExternalUniqueId";
|
||||
|
||||
public AddExternalIdToGranularPermissions(
|
||||
IMigrationContext context,
|
||||
ICoreScopeProvider scopeProvider)
|
||||
: base(context)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
protected override void Migrate()
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
if (ColumnExists(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission, NewColumnName) is false)
|
||||
{
|
||||
if (DatabaseType == DatabaseType.SQLite)
|
||||
{
|
||||
UpdateColumnSqlLite();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateColumnSqlServer();
|
||||
}
|
||||
}
|
||||
|
||||
if (IndexExists(NewIndex) is false)
|
||||
{
|
||||
CreateIndex<UserGroup2GranularPermissionDto>(NewIndex);
|
||||
}
|
||||
|
||||
Context.Complete();
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
protected void UpdateColumnSqlServer()
|
||||
{
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)
|
||||
.AddColumn(NewColumnName)
|
||||
.AsGuid()
|
||||
.Nullable()
|
||||
.Do();
|
||||
}
|
||||
|
||||
protected void UpdateColumnSqlLite()
|
||||
{
|
||||
var oldEntities = Database.Fetch<OldUserGroup2GranularPermissionDto>();
|
||||
var newEntities = oldEntities.Select(e => new UserGroup2GranularPermissionDto
|
||||
{
|
||||
Id = e.Id,
|
||||
Context = e.Context,
|
||||
Permission = e.Permission,
|
||||
UniqueId = e.UniqueId,
|
||||
UserGroupKey = e.UserGroupKey
|
||||
});
|
||||
|
||||
Delete.Table(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission).Do();
|
||||
Create.Table<UserGroup2GranularPermissionDto>().Do();
|
||||
|
||||
Database.InsertBulk(newEntities);
|
||||
}
|
||||
|
||||
[TableName(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)]
|
||||
private class OldUserGroup2GranularPermissionDto
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(Name = "PK_umbracoUserGroup2GranularPermissionDto", AutoIncrement = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("userGroupKey")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUserGroup2GranularPermissionDto_UserGroupKey_UniqueId",
|
||||
IncludeColumns = "uniqueId")]
|
||||
[ForeignKey(typeof(UserGroupDto), Column = "key")]
|
||||
public Guid UserGroupKey { get; set; }
|
||||
|
||||
[Column("uniqueId")]
|
||||
[ForeignKey(typeof(NodeDto), Column = "uniqueId")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUserGroup2GranularPermissionDto_UniqueId")]
|
||||
public Guid? UniqueId { get; set; }
|
||||
|
||||
[Column("permission")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
public required string Permission { get; set; }
|
||||
|
||||
[Column("context")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
public required string Context { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,23 @@ public class UserGroup2GranularPermissionDto
|
||||
[ForeignKey(typeof(UserGroupDto), Column = "key")]
|
||||
public Guid UserGroupKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique Umbraco Id
|
||||
/// </summary>
|
||||
[Column("uniqueId")]
|
||||
[ForeignKey(typeof(NodeDto), Column = "uniqueId")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUserGroup2GranularPermissionDto_UniqueId")]
|
||||
public Guid? UniqueId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A Unique Id within the <see cref="Context"/> that is not an Umbraco node
|
||||
/// </summary>
|
||||
[Column("externalUniqueId")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUserGroup2GranularPermissionDto_ExternalUniqueId")]
|
||||
public Guid? ExternalUniqueId { get; set; }
|
||||
|
||||
[Column("permission")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
public required string Permission { get; set; }
|
||||
|
||||
+10
-3
@@ -539,11 +539,18 @@ public class UserGroupRepository : EntityRepositoryBase<int, IUserGroup>, IUserG
|
||||
{
|
||||
var dto = new UserGroup2GranularPermissionDto
|
||||
{
|
||||
UserGroupKey = userGroup.Key, Permission = permission.Permission, Context = permission.Context
|
||||
UserGroupKey = userGroup.Key,
|
||||
Permission = permission.Permission,
|
||||
Context = permission.Context,
|
||||
};
|
||||
if (permission is INodeGranularPermission nodeGranularPermission)
|
||||
switch (permission)
|
||||
{
|
||||
dto.UniqueId = nodeGranularPermission.Key;
|
||||
case INodeGranularPermission nodeGranularPermission:
|
||||
dto.UniqueId = nodeGranularPermission.Key;
|
||||
break;
|
||||
case IExternalGranularPermission externalGranularPermission:
|
||||
dto.ExternalUniqueId = externalGranularPermission.Key;
|
||||
break;
|
||||
}
|
||||
|
||||
return dto;
|
||||
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Models.Membership.Permissions;
|
||||
using Umbraco.Cms.Core.Security.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Security.Authorization;
|
||||
|
||||
[TestFixture]
|
||||
public class ContextualPermissionAuthorizerTests
|
||||
{
|
||||
private const string FirstContext = "firstContext";
|
||||
private const string SecondContext = "secondContext";
|
||||
|
||||
private const string FirstPermission = "firstPermission";
|
||||
private const string SecondPermission = "secondPermission";
|
||||
private const string ThirdPermission = "thirdPermission";
|
||||
|
||||
[Test]
|
||||
public void IsDenied_True_NoGranularPermissions()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>());
|
||||
var resource = ContextualPermissionResource.WithPermission(FirstPermission, FirstContext);
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_True_IncorrectGranularPermission()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = SecondPermission },
|
||||
});
|
||||
var resource = ContextualPermissionResource.WithPermission(FirstPermission, FirstContext);
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_True_IncorrectGranularPermissions()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = ThirdPermission },
|
||||
new UnknownTypeGranularPermission { Context = SecondContext, Permission = FirstPermission },
|
||||
});
|
||||
var resource =
|
||||
ContextualPermissionResource.WithAnyPermissions([FirstPermission, SecondPermission], FirstContext);
|
||||
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_True_NotEnoughGranularPermissions()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = SecondPermission },
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = ThirdPermission },
|
||||
});
|
||||
var resource =
|
||||
ContextualPermissionResource.WithAllPermissions([FirstPermission, SecondPermission], FirstContext);
|
||||
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_True_HasGranularPermissionsButToSpecific()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new DocumentGranularPermission { Permission = FirstPermission, Key = Guid.NewGuid() },
|
||||
});
|
||||
var resource =
|
||||
ContextualPermissionResource.WithContextWidePermission(FirstPermission, DocumentGranularPermission.ContextType);
|
||||
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_False_HasGranularPermission()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = FirstPermission },
|
||||
});
|
||||
var resource = ContextualPermissionResource.WithPermission(FirstPermission, FirstContext);
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_False_HasAnyGranularPermissions()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = SecondPermission },
|
||||
});
|
||||
var resource =
|
||||
ContextualPermissionResource.WithAnyPermissions([FirstPermission, SecondPermission], FirstContext);
|
||||
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_False_HasMoreThanGranularPermissions()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = FirstPermission },
|
||||
new UnknownTypeGranularPermission { Context = FirstContext, Permission = SecondPermission },
|
||||
});
|
||||
var resource =
|
||||
ContextualPermissionResource.WithAllPermissions([FirstPermission, SecondPermission], FirstContext);
|
||||
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(isDenied);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDenied_False_HasSpecificGranularPermissionForAnyMatch()
|
||||
{
|
||||
// arrange
|
||||
var user = BuildUser(new HashSet<IGranularPermission>
|
||||
{
|
||||
new DocumentGranularPermission { Permission = FirstPermission, Key = Guid.NewGuid() },
|
||||
});
|
||||
var resource =
|
||||
ContextualPermissionResource.WithPermission(FirstPermission, DocumentGranularPermission.ContextType);
|
||||
|
||||
var sut = new ContextualPermissionAuthorizer();
|
||||
|
||||
// act
|
||||
var isDenied = sut.IsDenied(user, resource);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(isDenied);
|
||||
}
|
||||
|
||||
private IUser BuildUser(ISet<IGranularPermission> granularPermissions)
|
||||
{
|
||||
var userGroups = new List<IReadOnlyUserGroup>();
|
||||
var groupMock = new Mock<IReadOnlyUserGroup>();
|
||||
groupMock
|
||||
.SetupGet(group => group.GranularPermissions)
|
||||
.Returns(granularPermissions);
|
||||
|
||||
userGroups.Add(groupMock.Object);
|
||||
|
||||
var userMock = new Mock<IUser>();
|
||||
userMock
|
||||
.SetupGet(u => u.Groups)
|
||||
.Returns(userGroups);
|
||||
|
||||
return userMock.Object;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user