diff --git a/dependencies.props b/dependencies.props index fe0615c..cb965a6 100644 --- a/dependencies.props +++ b/dependencies.props @@ -13,6 +13,8 @@ 1.0.7 2.2.1 1.2.0 + 1.0.1 1.0.1 + 4.2.2 \ No newline at end of file diff --git a/src/api/NetworkResurrector.Api.Application/DependencyInjectionExtensions.cs b/src/api/NetworkResurrector.Api.Application/DependencyInjectionExtensions.cs index 8d235f9..dfc82a3 100644 --- a/src/api/NetworkResurrector.Api.Application/DependencyInjectionExtensions.cs +++ b/src/api/NetworkResurrector.Api.Application/DependencyInjectionExtensions.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using NetworkResurrector.Api.Application.Services; using NetworkResurrector.Api.Application.Services.Abstractions; +using NetworkResurrector.Api.Application.Services.Decorators; namespace NetworkResurrector.Api.Application { @@ -9,6 +10,8 @@ namespace NetworkResurrector.Api.Application public static void AddApplicationServices(this IServiceCollection services) { services.AddSingleton(); + services.AddScoped(); + services.Decorate(); } } } diff --git a/src/api/NetworkResurrector.Api.Application/NetworkResurrector.Api.Application.csproj b/src/api/NetworkResurrector.Api.Application/NetworkResurrector.Api.Application.csproj index 0a0c3a2..e14826d 100644 --- a/src/api/NetworkResurrector.Api.Application/NetworkResurrector.Api.Application.csproj +++ b/src/api/NetworkResurrector.Api.Application/NetworkResurrector.Api.Application.csproj @@ -11,10 +11,12 @@ + + diff --git a/src/api/NetworkResurrector.Api.Application/Queries/GetUserPermissions.cs b/src/api/NetworkResurrector.Api.Application/Queries/GetUserPermissions.cs index 227967b..4196532 100644 --- a/src/api/NetworkResurrector.Api.Application/Queries/GetUserPermissions.cs +++ b/src/api/NetworkResurrector.Api.Application/Queries/GetUserPermissions.cs @@ -1,5 +1,6 @@ using MediatR; using Netmash.Security.Authentication.Tuitio.Abstractions; +using NetworkResurrector.Api.Application.Services.Abstractions; using NetworkResurrector.Api.Domain.Repositories; using System.Collections.Generic; using System.Linq; @@ -19,19 +20,16 @@ namespace NetworkResurrector.Api.Application.Queries public class QueryHandler : IRequestHandler { - private readonly IUserContextAccessor _userContext; - private readonly ISecurityRepository _securityRepository; + private readonly IUserContext _userContext; - public QueryHandler(IUserContextAccessor userContext, ISecurityRepository securityRepository) + public QueryHandler(IUserContext userContext) { _userContext=userContext; - _securityRepository=securityRepository; } public async Task Handle(Query request, CancellationToken cancellationToken) { - var roles = _userContext.UserRoles.Select(r => r.id); - var permissions = await _securityRepository.GetUserPermissionCodes(roles); + var permissions = await _userContext.GetUserPermissions(); return new Model() { Permissions = permissions diff --git a/src/api/NetworkResurrector.Api.Application/Services/Abstractions/IUserContext.cs b/src/api/NetworkResurrector.Api.Application/Services/Abstractions/IUserContext.cs new file mode 100644 index 0000000..52e0582 --- /dev/null +++ b/src/api/NetworkResurrector.Api.Application/Services/Abstractions/IUserContext.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace NetworkResurrector.Api.Application.Services.Abstractions +{ + public interface IUserContext + { + Task> GetUserPermissions(); + } +} diff --git a/src/api/NetworkResurrector.Api.Application/Services/Decorators/UserContextCache.cs b/src/api/NetworkResurrector.Api.Application/Services/Decorators/UserContextCache.cs new file mode 100644 index 0000000..b8c2147 --- /dev/null +++ b/src/api/NetworkResurrector.Api.Application/Services/Decorators/UserContextCache.cs @@ -0,0 +1,35 @@ +using Netmash.Extensions.Caching.Services; +using Netmash.Security.Authentication.Tuitio.Abstractions; +using NetworkResurrector.Api.Application.Services.Abstractions; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace NetworkResurrector.Api.Application.Services.Decorators +{ + internal class UserContextCache : IUserContext + { + private readonly IUserContextAccessor _userContextAccessor; + private readonly IUserContext _inner; + private readonly ICacheService _cache; + + public UserContextCache(IUserContextAccessor userContextAccessor, IUserContext inner, ICacheService cache) + { + _userContextAccessor=userContextAccessor; + _inner=inner; + _cache=cache; + } + + public async Task> GetUserPermissions() + { + var key = $"UserPermissions_{_userContextAccessor.UserId}"; + var result = await _cache.GetAsync>(key); + if (result == null) + { + result = await _inner.GetUserPermissions(); + await _cache.SetAsync(key, result); + } + return result; + } + } +} diff --git a/src/api/NetworkResurrector.Api.Application/Services/UserContext.cs b/src/api/NetworkResurrector.Api.Application/Services/UserContext.cs new file mode 100644 index 0000000..0e682ed --- /dev/null +++ b/src/api/NetworkResurrector.Api.Application/Services/UserContext.cs @@ -0,0 +1,28 @@ +using Netmash.Security.Authentication.Tuitio.Abstractions; +using NetworkResurrector.Api.Application.Services.Abstractions; +using NetworkResurrector.Api.Domain.Repositories; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace NetworkResurrector.Api.Application.Services +{ + internal class UserContext : IUserContext + { + private readonly IUserContextAccessor _userContext; + private readonly ISecurityRepository _securityRepository; + + public UserContext(IUserContextAccessor userContext, ISecurityRepository securityRepository) + { + _userContext=userContext; + _securityRepository=securityRepository; + } + + public async Task> GetUserPermissions() + { + var roles = _userContext.UserRoles.Select(r => r.id); + var permissions = await _securityRepository.GetUserPermissionCodes(roles); + return permissions; + } + } +} diff --git a/src/api/NetworkResurrector.Api.Domain/Constants/PermissionCodes.cs b/src/api/NetworkResurrector.Api.Domain/Constants/PermissionCodes.cs new file mode 100644 index 0000000..c1a46b6 --- /dev/null +++ b/src/api/NetworkResurrector.Api.Domain/Constants/PermissionCodes.cs @@ -0,0 +1,14 @@ +namespace NetworkResurrector.Api.Domain.Constants +{ + public struct PermissionCodes + { + public const string + VIEW_DASHBOARD = "VIEW_DASHBOARD", + MANAGE_USERS = "MANAGE_USERS", + MANAGE_SETTINGS = "MANAGE_SETTINGS", + VIEW_MACHINES = "VIEW_MACHINES", + MANAGE_MACHINES = "MANAGE_MACHINES", + OPERATE_MACHINES = "OPERATE_MACHINES", + GUEST_ACCESS = "GUEST_ACCESS"; + } +} diff --git a/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs b/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs new file mode 100644 index 0000000..3d03d93 --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/Constants/Policies.cs @@ -0,0 +1,8 @@ +namespace NetworkResurrector.Api.Authorization.Constants +{ + public struct Policies + { + public const string + OperateMachines = "OPERATE_MACHINES"; + } +} diff --git a/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs b/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs new file mode 100644 index 0000000..55ca576 --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/DependencyInjectionExtensions.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; +using NetworkResurrector.Api.Authorization.Constants; +using NetworkResurrector.Api.Authorization.Handlers; +using NetworkResurrector.Api.Authorization.Requirements; + +namespace NetworkResurrector.Api.Authorization +{ + public static class DependencyInjectionExtensions + { + public static void AddLocalAuthorization(this IServiceCollection services) + { + services.AddAuthorization(options => + { + options.AddPolicy(Policies.OperateMachines, policy => + { + policy.Requirements.Add(new OperateMachinesRequirement()); + }); + }); + + services.AddScoped(); + } + } +} diff --git a/src/api/NetworkResurrector.Api/Authorization/Handlers/OperateMachinesHandler.cs b/src/api/NetworkResurrector.Api/Authorization/Handlers/OperateMachinesHandler.cs new file mode 100644 index 0000000..060c36f --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/Handlers/OperateMachinesHandler.cs @@ -0,0 +1,27 @@ +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/Requirements/OperateMachinesRequirement.cs b/src/api/NetworkResurrector.Api/Authorization/Requirements/OperateMachinesRequirement.cs new file mode 100644 index 0000000..8ccdf05 --- /dev/null +++ b/src/api/NetworkResurrector.Api/Authorization/Requirements/OperateMachinesRequirement.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Authorization; +using NetworkResurrector.Api.Domain.Constants; + +namespace NetworkResurrector.Api.Authorization.Requirements +{ + public class OperateMachinesRequirement : IAuthorizationRequirement + { + public readonly string PermissionCode = PermissionCodes.OPERATE_MACHINES; + } +} diff --git a/src/api/NetworkResurrector.Api/Controllers/ResurrectorController.cs b/src/api/NetworkResurrector.Api/Controllers/ResurrectorController.cs index 46fe152..e469c2d 100644 --- a/src/api/NetworkResurrector.Api/Controllers/ResurrectorController.cs +++ b/src/api/NetworkResurrector.Api/Controllers/ResurrectorController.cs @@ -1,6 +1,7 @@ using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using NetworkResurrector.Api.Authorization.Constants; using NetworkResurrector.Api.PublishedLanguage.Commands; using System.Threading.Tasks; @@ -26,6 +27,7 @@ namespace NetworkResurrector.Api.Controllers } [HttpPost("ping")] + [Authorize(Policy = Policies.OperateMachines)] public async Task PingMachine([FromBody] PingMachine pingMachine) { var result = await _mediator.Send(pingMachine); diff --git a/src/api/NetworkResurrector.Api/Extensions/StartupExtensions.cs b/src/api/NetworkResurrector.Api/Extensions/StartupExtensions.cs index 3ce270f..85f7d1e 100644 --- a/src/api/NetworkResurrector.Api/Extensions/StartupExtensions.cs +++ b/src/api/NetworkResurrector.Api/Extensions/StartupExtensions.cs @@ -3,6 +3,7 @@ using MediatR.Pipeline; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Netmash.Extensions.Caching; using Netmash.Extensions.Swagger; using Netmash.Extensions.Swagger.Constants; using Netmash.Infrastructure.DatabaseMigration; @@ -10,6 +11,7 @@ using Netmash.Infrastructure.DatabaseMigration.Constants; using Netmash.Security.Authentication.Tuitio; using NetworkResurrector.Agent.Wrapper; using NetworkResurrector.Api.Application; +using NetworkResurrector.Api.Authorization; using NetworkResurrector.Api.Domain.Data; using NetworkResurrector.Server.Wrapper; using Newtonsoft.Json; @@ -26,6 +28,12 @@ namespace NetworkResurrector.Api.Extensions // Add basic authentication services.AddTuitioAuthentication(configuration.GetSection("Tuitio")["BaseAddress"]); + // Add authorization + services.AddLocalAuthorization(); + + // Add cache service + services.AddCacheService(); + // MediatR services.AddMediatR(typeof(Application.Queries.GetMachines).Assembly); services.AddScoped(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));