using KTUSAPS.Data; using KTUSAPS.Data.Model; using KTUSAPS.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; namespace KTUSAPS.Auth { public class SaPsAuthorizationHandler : IAuthorizationHandler { private readonly IServiceProvider serviceProvider; public SaPsAuthorizationHandler(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } public async Task HandleAsync(AuthorizationHandlerContext context) { var cache = new AuthProcessingCache(context.User, serviceProvider); foreach (var requirement in context.Requirements) { if (requirement is AdminRequirement adminRequirement) await HandleRequirementAsync(context, adminRequirement, cache); if (requirement is MyIssueRequirement myIssueRequirement) await HandleRequirementAsync(context, myIssueRequirement, cache); } } private async Task HandleRequirementAsync(AuthorizationHandlerContext context, MyIssueRequirement myIssueRequirement, AuthProcessingCache cache) { if(context.Resource is not Issue issue) throw new Exception($"'{nameof(MyIssueRequirement)}' must be issued with resource of type '{nameof(Issue)}'"); if(issue.UserID == context.User.GetUserId()) { context.Succeed(myIssueRequirement); return; } if(await cache.DetermineIsAdminAsync()) { context.Succeed(myIssueRequirement); return; } } private async Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminRequirement adminRequirement, AuthProcessingCache cache) { if(await cache.DetermineIsAdminAsync()) { context.Succeed(adminRequirement); return; } context.Fail(); } private class AuthProcessingCache { private readonly ClaimsPrincipal _user; private readonly IServiceProvider _serviceProvider; private bool? isAdmin; public bool IsAdmin => determineIsAdminCached(); public AuthProcessingCache(ClaimsPrincipal user, IServiceProvider serviceProvider) { _user = user; _serviceProvider = serviceProvider; } private bool determineIsAdminCached() { if (isAdmin == null) isAdmin = determineIsAdmin(); return isAdmin.Value; } private bool determineIsAdmin() { var objectId = _user.GetObjectId(); if (objectId == default) return false; using var scope = _serviceProvider.CreateScope(); var dataContext = scope.ServiceProvider.GetRequiredService(); var admin = dataContext.Admins.Where(a => a.UserId == objectId).FirstOrDefault(); if (admin != default) return true; return false; } private async ValueTask determineIsAdminAsync() { var idclaim = _user.Claims.Where(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier").FirstOrDefault(); if (idclaim == default) return false; using var scope = _serviceProvider.CreateScope(); var dataContext = scope.ServiceProvider.GetRequiredService(); var admin = await dataContext.Admins.Where(a => a.UserId == idclaim.Value).FirstOrDefaultAsync(); if (admin != default) return true; return false; } public async ValueTask DetermineIsAdminAsync() { if (isAdmin == null) isAdmin = await determineIsAdminAsync(); return isAdmin.Value; } } } }