| | | 1 | | using ArturRios.Heimdall.Domain.Enums; |
| | | 2 | | using Microsoft.EntityFrameworkCore; |
| | | 3 | | using Application = ArturRios.Heimdall.Domain.Entities.Application; |
| | | 4 | | |
| | | 5 | | namespace ArturRios.Heimdall.Shared.Services; |
| | | 6 | | |
| | | 7 | | /// <summary>The outcome of <see cref="ApplicationManagementAccess.ResolveAsync" />.</summary> |
| | | 8 | | public enum ApplicationAccess |
| | | 9 | | { |
| | | 10 | | /// <summary>The actor may manage the application.</summary> |
| | | 11 | | Granted, |
| | | 12 | | |
| | | 13 | | /// <summary>No live application with that id lives in the addressed, live scope.</summary> |
| | | 14 | | NotFound, |
| | | 15 | | |
| | | 16 | | /// <summary>The application exists, but the actor may not manage it.</summary> |
| | | 17 | | Forbidden |
| | | 18 | | } |
| | | 19 | | |
| | | 20 | | /// <summary>The resolved application — only when <see cref="Access" /> is <see cref="ApplicationAccess.Granted" />.</su |
| | 162 | 21 | | public sealed record ApplicationAccessResult(ApplicationAccess Access, Application? Application); |
| | | 22 | | |
| | | 23 | | /// <summary> |
| | | 24 | | /// Who may manage an application's credentials (UC-53…UC-55, FR-AP-12): the rule UC-17, UC-18 |
| | | 25 | | /// and UC-19 already apply to the application itself. |
| | | 26 | | /// </summary> |
| | | 27 | | /// <remarks> |
| | | 28 | | /// <para> |
| | | 29 | | /// A System Admin manages any application. A Scope Admin manages one only when they are its |
| | | 30 | | /// owner <em>and</em> still own its scope: owning the scope is not grounds to manage another |
| | | 31 | | /// owner's application, and an owner UC-22 removed from the scope keeps nothing (AF-17b). |
| | | 32 | | /// </para> |
| | | 33 | | /// <para> |
| | | 34 | | /// Not found is answered before authorization, as UC-17 does, so a missing application and |
| | | 35 | | /// one the actor may not touch stay distinguishable to the people allowed to see either. A |
| | | 36 | | /// logically deleted application, or one whose scope is logically deleted, is not found: |
| | | 37 | | /// its credentials are inert (UC-56, UC-57), so there is nothing to manage. |
| | | 38 | | /// </para> |
| | | 39 | | /// </remarks> |
| | | 40 | | public static class ApplicationManagementAccess |
| | | 41 | | { |
| | | 42 | | public static async Task<ApplicationAccessResult> ResolveAsync( |
| | | 43 | | IQueryable<Application> applications, |
| | | 44 | | Guid scopeId, |
| | | 45 | | Guid applicationId, |
| | | 46 | | int actingRole, |
| | | 47 | | Guid actingPersonId) |
| | | 48 | | { |
| | | 49 | | var application = await applications |
| | | 50 | | .Include(x => x.Scope) |
| | | 51 | | .Include(x => x.Owner) |
| | | 52 | | .ThenInclude(owner => owner.ScopeOwnerships) |
| | | 53 | | .FirstOrDefaultAsync(x => x.PublicId == applicationId && x.Scope.PublicId == scopeId && |
| | | 54 | | !x.IsDeleted && !x.Scope.IsDeleted); |
| | | 55 | | |
| | | 56 | | if (application is null) |
| | | 57 | | { |
| | | 58 | | return new ApplicationAccessResult(ApplicationAccess.NotFound, null); |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | if (actingRole == (int)Roles.SystemAdmin) |
| | | 62 | | { |
| | | 63 | | return new ApplicationAccessResult(ApplicationAccess.Granted, application); |
| | | 64 | | } |
| | | 65 | | |
| | | 66 | | var ownsApplicationAndScope = |
| | | 67 | | application.Owner.PublicId == actingPersonId && |
| | | 68 | | application.Owner.ScopeOwnerships.Any(ownership => ownership.ScopeId == application.ScopeId); |
| | | 69 | | |
| | | 70 | | return ownsApplicationAndScope |
| | | 71 | | ? new ApplicationAccessResult(ApplicationAccess.Granted, application) |
| | | 72 | | : new ApplicationAccessResult(ApplicationAccess.Forbidden, null); |
| | | 73 | | } |
| | | 74 | | } |