Permissions and authorizations at the user role level have been added to the application.

master
Tudor Stanciu 2023-04-15 01:17:17 +03:00
parent 8e54fdc184
commit cb6fa0507a
12 changed files with 166 additions and 63 deletions

View File

@ -11,7 +11,7 @@
<NBBPackageVersion>6.0.30</NBBPackageVersion>
<EntityFrameworkCorePackageVersion>6.0.1</EntityFrameworkCorePackageVersion>
<NetmashExtensionsSwaggerPackageVersion>1.0.7</NetmashExtensionsSwaggerPackageVersion>
<NetmashTuitioAuthenticationPackageVersion>2.2.0</NetmashTuitioAuthenticationPackageVersion>
<NetmashTuitioAuthenticationPackageVersion>2.2.1</NetmashTuitioAuthenticationPackageVersion>
<NetmashDatabaseMigrationPackageVersion>1.2.0</NetmashDatabaseMigrationPackageVersion>
<CorreoPublishedLanguage>1.0.1</CorreoPublishedLanguage>
</PropertyGroup>

View File

@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
<PackageReference Include="Netmash.Security.Authentication.Tuitio" Version="$(NetmashTuitioAuthenticationPackageVersion)" />
<PackageReference Include="NBB.Messaging.Abstractions" Version="$(NBBPackageVersion)" />
<PackageReference Include="NetworkResurrector.Agent.Wrapper" Version="1.1.0" />
<PackageReference Include="NetworkResurrector.Server.Wrapper" Version="1.1.1" />

View File

@ -0,0 +1,42 @@
using MediatR;
using Netmash.Security.Authentication.Tuitio.Abstractions;
using NetworkResurrector.Api.Domain.Repositories;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NetworkResurrector.Api.Application.Queries
{
public class GetUserPermissions
{
public class Query : IRequest<Model> { }
public class Model
{
public IEnumerable<string> Permissions { get; set; }
}
public class QueryHandler : IRequestHandler<Query, Model>
{
private readonly IUserContextAccessor _userContext;
private readonly ISecurityRepository _securityRepository;
public QueryHandler(IUserContextAccessor userContext, ISecurityRepository securityRepository)
{
_userContext=userContext;
_securityRepository=securityRepository;
}
public async Task<Model> Handle(Query request, CancellationToken cancellationToken)
{
var roles = _userContext.UserRoles.Select(r => r.id);
var permissions = await _securityRepository.GetUserPermissionCodes(roles);
return new Model()
{
Permissions = permissions
};
}
}
}
}

View File

@ -3,6 +3,7 @@ using NetworkResurrector.Api.Domain.Data.EntityTypeConfiguration;
using NetworkResurrector.Api.Domain.Data.EntityTypeConfiguration.Power;
using NetworkResurrector.Api.Domain.Data.EntityTypeConfiguration.Security;
using NetworkResurrector.Api.Domain.Entities;
using NetworkResurrector.Api.Domain.Entities.Security;
namespace NetworkResurrector.Api.Domain.Data.DbContexts
{
@ -10,6 +11,8 @@ namespace NetworkResurrector.Api.Domain.Data.DbContexts
{
public DbSet<Machine> Machines { get; set; }
public DbSet<Entities.Power.PowerActionConfiguration> PowerActionConfigurations { get; set; }
public DbSet<UserRoleAuthorization> UserRoleAuthorizations { get; set; }
public DbSet<PermissionHierarchy> PermissionHierarchies { get; set; }
public NetworkDbContext(DbContextOptions<NetworkDbContext> options)
: base(options)

View File

@ -12,6 +12,7 @@ namespace NetworkResurrector.Api.Domain.Data
public static void AddDataAccess(this IServiceCollection services)
{
services.AddScoped<INetworkRepository, NetworkRepository>();
services.AddScoped<ISecurityRepository, SecurityRepository>();
services
.AddDbContextPool<NetworkDbContext>(

View File

@ -0,0 +1,80 @@
using Microsoft.EntityFrameworkCore;
using NetworkResurrector.Api.Domain.Data.DbContexts;
using NetworkResurrector.Api.Domain.Entities.Security;
using NetworkResurrector.Api.Domain.Repositories;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace NetworkResurrector.Api.Domain.Data.Repositories
{
public class SecurityRepository : ISecurityRepository
{
private readonly NetworkDbContext _dbContext;
public SecurityRepository(NetworkDbContext dbContext)
{
_dbContext=dbContext;
}
private async Task BuildChildrenHierarchy(Permission permission)
{
var children = await _dbContext.PermissionHierarchies
.Include(z => z.Child)
.Where(z => z.ParentPermissionId == permission.PermissionId)
.AsNoTracking()
.ToArrayAsync();
if (children == null || !children.Any())
return;
permission.Children = children;
foreach (var element in permission.Children)
{
await BuildChildrenHierarchy(element.Child);
}
}
private IEnumerable<string> GetPermissionCodes(IEnumerable<Permission> permissions)
{
var result = new List<string>();
foreach (var permission in permissions)
{
result.Add(permission.PermissionCode);
if (permission.Children != null && permission.Children.Count > 0)
{
var children = permission.Children.Select(ch => ch.Child);
var childrenCodes = GetPermissionCodes(children);
result.AddRange(childrenCodes);
}
}
return result.Distinct();
}
private async Task<IEnumerable<Permission>> GetUserPermissions(IEnumerable<int> roles)
{
var authorizations = await _dbContext.UserRoleAuthorizations
.Include(z => z.Permission)
.AsNoTracking()
.Where(z => roles.Contains(z.UserRoleId) && z.Active)
.ToArrayAsync();
var permissions = authorizations.Select(z => z.Permission);
foreach (var permission in permissions)
{
await BuildChildrenHierarchy(permission);
}
return permissions;
}
public async Task<IEnumerable<string>> GetUserPermissionCodes(IEnumerable<int> roles)
{
var permissions = await GetUserPermissions(roles);
var codes = GetPermissionCodes(permissions);
return codes;
}
}
}

View File

@ -1,10 +0,0 @@
namespace NetworkResurrector.Api.Domain.Abstractions
{
public interface IUserService
{
bool UserIsLoggedIn { get; }
string GetUserId();
string GetUserName();
bool UserIsGuest();
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace NetworkResurrector.Api.Domain.Repositories
{
public interface ISecurityRepository
{
Task<IEnumerable<string>> GetUserPermissionCodes(IEnumerable<int> roles);
}
}

View File

@ -0,0 +1,28 @@
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NetworkResurrector.Api.Application.Queries;
using System.Threading.Tasks;
namespace NetworkResurrector.Api.Controllers
{
[Authorize]
[ApiController]
[Route("security")]
public class SecurityController : ControllerBase
{
private readonly IMediator _mediator;
public SecurityController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("permissions")]
public async Task<IActionResult> GetUserPermissions([FromRoute] GetUserPermissions.Query query)
{
var result = await _mediator.Send(query);
return Ok(result);
}
}
}

View File

@ -10,9 +10,7 @@ using Netmash.Infrastructure.DatabaseMigration.Constants;
using Netmash.Security.Authentication.Tuitio;
using NetworkResurrector.Agent.Wrapper;
using NetworkResurrector.Api.Application;
using NetworkResurrector.Api.Domain.Abstractions;
using NetworkResurrector.Api.Domain.Data;
using NetworkResurrector.Api.Services;
using NetworkResurrector.Server.Wrapper;
using Newtonsoft.Json;
@ -28,9 +26,6 @@ namespace NetworkResurrector.Api.Extensions
// Add basic authentication
services.AddTuitioAuthentication(configuration.GetSection("Tuitio")["BaseAddress"]);
services.AddHttpContextAccessor();
services.AddScoped<IUserService, UserService>();
// MediatR
services.AddMediatR(typeof(Application.Queries.GetMachines).Assembly);
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));

View File

@ -20,7 +20,6 @@
<PackageReference Include="NBB.Messaging.Nats" Version="$(NBBPackageVersion)" />
<PackageReference Include="Netmash.Extensions.Swagger" Version="$(NetmashExtensionsSwaggerPackageVersion)" />
<PackageReference Include="Netmash.Infrastructure.DatabaseMigration" Version="$(NetmashDatabaseMigrationPackageVersion)" />
<PackageReference Include="Netmash.Security.Authentication.Tuitio" Version="$(NetmashTuitioAuthenticationPackageVersion)" />
<PackageReference Include="Serilog.AspNetCore" Version="$(SerilogPackageVersion)" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="$(SerilogSinksMSSqlServerPackageVersion)" />
<PackageReference Include="Serilog.Sinks.Seq" Version="$(SerilogSinksSeqPackageVersion)" />

View File

@ -1,46 +0,0 @@
using Microsoft.AspNetCore.Http;
using NetworkResurrector.Api.Domain.Abstractions;
using System;
using System.Linq;
using System.Security.Claims;
namespace NetworkResurrector.Api.Services
{
public class UserService : IUserService
{
private readonly IHttpContextAccessor _httpAccessor;
public UserService(IHttpContextAccessor httpAccessor)
{
_httpAccessor = httpAccessor;
}
public bool UserIsLoggedIn => _httpAccessor.HttpContext.User != null;
public string GetUserId()
{
var userId = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userId))
throw new Exception("User id could not be retrieved from claims.");
return userId;
}
public string GetUserName()
{
var userName = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == ClaimTypes.Name)?.Value;
if (string.IsNullOrEmpty(userName))
throw new Exception("User name could not be retrieved from claims.");
return userName;
}
public bool UserIsGuest()
{
var userIsGuest = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == Netmash.Security.Authentication.Tuitio.Constants.ClaimTypes.IsGuestUser)?.Value;
return !string.IsNullOrEmpty(userIsGuest) && bool.TrueString == userIsGuest;
}
}
}