diff --git a/ReleaseNotes.xml b/ReleaseNotes.xml index fda3a55..1d684ca 100644 --- a/ReleaseNotes.xml +++ b/ReleaseNotes.xml @@ -178,6 +178,7 @@ • Permissions and authorizations at the user role level have been added to the application. • The "Netmash.Security.Authentication.Tuitio" nuget package has been upgraded in backend. • Added cache and authorization policies. + • Added authorization rules based on permissions. \ No newline at end of file diff --git a/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs b/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs index 3d03d93..d571638 100644 --- a/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs +++ b/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs @@ -3,6 +3,7 @@ public struct Policies { public const string - OperateMachines = "OPERATE_MACHINES"; + OperateMachines = "OPERATE_MACHINES", + ViewMachines = "VIEW_MACHINES"; } } diff --git a/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs b/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs index 55ca576..e907de9 100644 --- a/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs +++ b/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs @@ -16,9 +16,13 @@ namespace NetworkResurrector.Api.Authorization { policy.Requirements.Add(new OperateMachinesRequirement()); }); + options.AddPolicy(Policies.ViewMachines, policy => + { + policy.Requirements.Add(new ViewMachinesRequirement()); + }); }); - services.AddScoped(); + services.AddScoped(); } } } diff --git a/src/api/NetworkResurrector.Api/Authorization/Handlers/OperateMachinesHandler.cs b/src/api/NetworkResurrector.Api/Authorization/Handlers/OperateMachinesHandler.cs deleted file mode 100644 index 060c36f..0000000 --- a/src/api/NetworkResurrector.Api/Authorization/Handlers/OperateMachinesHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using NetworkResurrector.Api.Application.Services.Abstractions; -using NetworkResurrector.Api.Authorization.Requirements; -using System.Linq; -using System.Threading.Tasks; - -namespace NetworkResurrector.Api.Authorization.Handlers -{ - public class OperateMachinesHandler : AuthorizationHandler - { - private readonly IUserContext _userContext; - - public OperateMachinesHandler(IUserContext userContext) - { - _userContext=userContext; - } - - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OperateMachinesRequirement requirement) - { - var permissions = await _userContext.GetUserPermissions(); - if (permissions.Contains(requirement.PermissionCode)) - { - context.Succeed(requirement); - } - } - } -} diff --git a/src/api/NetworkResurrector.Api/Authorization/Handlers/PermissionsBasedAuthorizationHandler.cs b/src/api/NetworkResurrector.Api/Authorization/Handlers/PermissionsBasedAuthorizationHandler.cs new file mode 100644 index 0000000..fb4b8cb --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/Handlers/PermissionsBasedAuthorizationHandler.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Authorization; +using NetworkResurrector.Api.Application.Services.Abstractions; +using NetworkResurrector.Api.Authorization.Requirements; +using NetworkResurrector.Api.Extensions; +using System.Linq; +using System.Threading.Tasks; + +namespace NetworkResurrector.Api.Authorization.Handlers +{ + public class PermissionsBasedAuthorizationHandler : AuthorizationHandler + { + private readonly IUserContext _userContext; + + public PermissionsBasedAuthorizationHandler(IUserContext userContext) + { + _userContext=userContext; + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IPermissionsBasedAuthorizationRequirement requirement) + { + var permissions = await _userContext.GetUserPermissions(); + var condition1 = requirement.AllRequired.IsNullOrEmpty() || requirement.AllRequired.All(permission => permissions.Contains(permission)); + var condition2 = requirement.OneOf.IsNullOrEmpty() || requirement.OneOf.Any(permission => permissions.Contains(permission)); + if (condition1 && condition2) + { + context.Succeed(requirement); + } + } + } +} diff --git a/src/api/NetworkResurrector.Api/Authorization/Requirements/IPermissionsBasedAuthorizationRequirement.cs b/src/api/NetworkResurrector.Api/Authorization/Requirements/IPermissionsBasedAuthorizationRequirement.cs new file mode 100644 index 0000000..2809bbd --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/Requirements/IPermissionsBasedAuthorizationRequirement.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Authorization; + +namespace NetworkResurrector.Api.Authorization.Requirements +{ + public interface IPermissionsBasedAuthorizationRequirement : IAuthorizationRequirement + { + string[] AllRequired { get; } + string[] OneOf { get; } + } +} diff --git a/src/api/NetworkResurrector.Api/Authorization/Requirements/OperateMachinesRequirement.cs b/src/api/NetworkResurrector.Api/Authorization/Requirements/OperateMachinesRequirement.cs index 8ccdf05..24d0d2c 100644 --- a/src/api/NetworkResurrector.Api/Authorization/Requirements/OperateMachinesRequirement.cs +++ b/src/api/NetworkResurrector.Api/Authorization/Requirements/OperateMachinesRequirement.cs @@ -1,10 +1,12 @@ -using Microsoft.AspNetCore.Authorization; -using NetworkResurrector.Api.Domain.Constants; +using NetworkResurrector.Api.Domain.Constants; +using System; namespace NetworkResurrector.Api.Authorization.Requirements { - public class OperateMachinesRequirement : IAuthorizationRequirement + public class OperateMachinesRequirement : IPermissionsBasedAuthorizationRequirement { - public readonly string PermissionCode = PermissionCodes.OPERATE_MACHINES; + public string[] AllRequired => new string[] { PermissionCodes.OPERATE_MACHINES }; + + public string[] OneOf => Array.Empty(); } } diff --git a/src/api/NetworkResurrector.Api/Authorization/Requirements/ViewMachinesRequirement.cs b/src/api/NetworkResurrector.Api/Authorization/Requirements/ViewMachinesRequirement.cs new file mode 100644 index 0000000..ac0eda8 --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/Requirements/ViewMachinesRequirement.cs @@ -0,0 +1,11 @@ +using NetworkResurrector.Api.Domain.Constants; +using System; + +namespace NetworkResurrector.Api.Authorization.Requirements +{ + public class ViewMachinesRequirement : IPermissionsBasedAuthorizationRequirement + { + public string[] AllRequired => Array.Empty(); + public string[] OneOf => new string[] { PermissionCodes.VIEW_MACHINES, PermissionCodes.GUEST_ACCESS }; + } +} diff --git a/src/api/NetworkResurrector.Api/Controllers/NetworkController.cs b/src/api/NetworkResurrector.Api/Controllers/NetworkController.cs index aadda5d..21fc044 100644 --- a/src/api/NetworkResurrector.Api/Controllers/NetworkController.cs +++ b/src/api/NetworkResurrector.Api/Controllers/NetworkController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using NetworkResurrector.Api.Application.Queries; +using NetworkResurrector.Api.Authorization.Constants; using System.Threading.Tasks; namespace NetworkResurrector.Api.Controllers @@ -19,6 +20,7 @@ namespace NetworkResurrector.Api.Controllers } [HttpGet("machines")] + [Authorize(Policy = Policies.ViewMachines)] public async Task GetMachines([FromRoute] GetMachines.Query query) { var result = await _mediator.Send(query); diff --git a/src/api/NetworkResurrector.Api/Extensions/DataTypeExtensions.cs b/src/api/NetworkResurrector.Api/Extensions/DataTypeExtensions.cs index 0e249de..7e6e184 100644 --- a/src/api/NetworkResurrector.Api/Extensions/DataTypeExtensions.cs +++ b/src/api/NetworkResurrector.Api/Extensions/DataTypeExtensions.cs @@ -4,5 +4,8 @@ { public static string Nullify(this string value) => string.IsNullOrWhiteSpace(value) ? null : value; + + public static bool IsNullOrEmpty(this string[] array) + => array == null || array.Length == 0; } }