| | | 1 | | using ArturRios.Output; |
| | | 2 | | using ArturRios.Util.Http; |
| | | 3 | | using ArturRios.Util.WebApi.Security.Extensions; |
| | | 4 | | using Microsoft.AspNetCore.Mvc; |
| | | 5 | | using Microsoft.AspNetCore.Mvc.Filters; |
| | | 6 | | |
| | | 7 | | namespace ArturRios.Heimdall.WebApi.Security; |
| | | 8 | | |
| | | 9 | | /// <summary> |
| | | 10 | | /// Global authorization filter confining a subject-rights-only token — <see cref="IdentityUser.SubjectRightsOnly" / |
| | | 11 | | /// set, issued at sign-in to an identity whose processing is restricted (NFR-24) — to the actions |
| | | 12 | | /// marked <see cref="DataSubjectRightAttribute" />, refusing it everywhere else with |
| | | 13 | | /// <c>403 Forbidden</c>. |
| | | 14 | | /// </summary> |
| | | 15 | | /// <remarks> |
| | | 16 | | /// <para> |
| | | 17 | | /// <see cref="ActorLivenessFilter" /> already refuses a restricted identity's token outside |
| | | 18 | | /// those actions, but only while the restriction stands. This filter keeps the token what it |
| | | 19 | | /// was issued as once the restriction is lifted: a subject's means to exercise their own |
| | | 20 | | /// rights, not a way back into the account. A subject who lifts their restriction signs in |
| | | 21 | | /// again for a full token. |
| | | 22 | | /// </para> |
| | | 23 | | /// <para> |
| | | 24 | | /// <c>403</c> rather than the <c>401</c> <see cref="MfaPendingGuardFilter" /> answers with: |
| | | 25 | | /// the token is genuine and current, and signing in again would only issue another like it |
| | | 26 | | /// while the restriction stands. The caller is authenticated and not permitted here. |
| | | 27 | | /// </para> |
| | | 28 | | /// <para> |
| | | 29 | | /// A request carrying no bearer token resolves to <c>null</c> and is left alone, as the |
| | | 30 | | /// other global filters leave it. |
| | | 31 | | /// </para> |
| | | 32 | | /// </remarks> |
| | | 33 | | public class SubjectRightsOnlyGuardFilter : IAuthorizationFilter |
| | | 34 | | { |
| | | 35 | | /// <summary>The refusal, naming what the token is for so a client can route the subject there.</summary> |
| | | 36 | | public const string SubjectRightsOnly = |
| | | 37 | | "Processing of your identity is restricted. This token only admits exporting your data, requesting its erasure, |
| | | 38 | | |
| | | 39 | | public void OnAuthorization(AuthorizationFilterContext context) |
| | | 40 | | { |
| | 1377 | 41 | | var user = context.HttpContext.GetUser<IdentityUser>(); |
| | | 42 | | |
| | 1377 | 43 | | if (user is not { SubjectRightsOnly: true } || |
| | 1377 | 44 | | context.ActionDescriptor.EndpointMetadata.OfType<DataSubjectRightAttribute>().Any()) |
| | | 45 | | { |
| | 1342 | 46 | | return; |
| | | 47 | | } |
| | | 48 | | |
| | 35 | 49 | | var output = ProcessOutput.New.WithError(SubjectRightsOnly); |
| | | 50 | | |
| | 35 | 51 | | context.Result = new ObjectResult(output) { StatusCode = HttpStatusCodes.Forbidden }; |
| | 35 | 52 | | } |
| | | 53 | | } |