You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Security/Authorization/Middleware/RoleAuthorizationMiddleware.cs

119 lines
3.6 KiB

2 years ago
using Connected.Interop;
using Connected.Middleware;
using Connected.Security.Identity;
using Connected.Security.Membership;
using Connected.Security.Permissions;
using Connected.ServiceModel;
using Microsoft.Extensions.Logging;
using System.Collections.Immutable;
namespace Connected.Security.Authorization.Middleware;
internal class RoleAuthorizationMiddleware : MiddlewareComponent, IAuthorizationMiddleware
{
public RoleAuthorizationMiddleware(IRoleService roleService, IUserService userService, ILogger<RoleAuthorizationMiddleware> logger, IMembershipService membershipService)
{
RoleService = roleService;
UserService = userService;
Logger = logger;
MembershipService = membershipService;
}
public string Id => "Roles";
private IRoleService RoleService { get; }
private IUserService UserService { get; }
private ILogger<RoleAuthorizationMiddleware> Logger { get; }
private IMembershipService MembershipService { get; }
public Task<AuthorizationProviderResult> Authorize(IPermission permission, AuthorizationArgs e, Dictionary<string, object> state)
{
if (state["roles"] is not List<int> roles)
return Task.FromResult(AuthorizationProviderResult.NotHandled);
if (!TypeConversion.TryConvert(permission.Evidence, out int evidence))
return Task.FromResult(AuthorizationProviderResult.NotHandled);
if (roles.Contains(evidence))
{
return permission.Value switch
{
PermissionValue.NotSet => Task.FromResult(AuthorizationProviderResult.NotHandled),
PermissionValue.Allow => Task.FromResult(AuthorizationProviderResult.Success),
PermissionValue.Deny => Task.FromResult(AuthorizationProviderResult.Fail),
_ => throw new NotSupportedException(),
};
}
return Task.FromResult(AuthorizationProviderResult.NotHandled);
}
public async Task<AuthorizationProviderResult> PreAuthorize(AuthorizationArgs e, Dictionary<string, object> state)
{
var roles = await ResolveImplicitRoles(e);
state.Add("roles", roles);
if (e.User > 0)
{
var list = await MembershipService.Query(new MembershipQueryArgs { User = (int)e.User });
if (list is not null && list.Any())
roles.AddRange(list.Select(f => f.Role));
}
if (await RoleService.Select(new NameArgs { Name = Roles.FullControl }) is IRole fullControl && roles.Contains(fullControl.Id))
return AuthorizationProviderResult.Success;
return AuthorizationProviderResult.NotHandled;
}
public async Task<ImmutableList<IPermissionSchemaDescriptor>> QueryDescriptors()
{
if (await RoleService.Query() is not ImmutableList<IRole> roles)
return ImmutableList<IPermissionSchemaDescriptor>.Empty;
var result = new List<IPermissionSchemaDescriptor>();
foreach (var role in roles)
{
result.Add(new PermissionSchemaDescriptor
{
Title = role.Name,
Id = role.Id.ToString()
});
}
return result.ToImmutableList();
}
private async Task<List<int>> ResolveImplicitRoles(AuthorizationArgs e)
{
var result = new List<int>();
if (await RoleService.Select(Roles.Everyone) is IRole everyone)
result.Add(everyone.Id);
if (e.User == 0)
{
if (await RoleService.Select(Roles.Anonymous) is IRole anonymous)
result.Add(anonymous.Id);
}
else
{
if (e.User is null || await UserService.Select(e.User) is null)
{
if (await RoleService.Select(Roles.Anonymous) is IRole anonymous)
result.Add(anonymous.Id);
Logger.LogWarning("Authenticated user not found. Request will be treated as anonymous {user}.", e.User);
return result;
}
if (await RoleService.Select(Roles.Authenticated) is IRole authenticated)
result.Add(authenticated.Id);
}
return result;
}
}