#nullable enable using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Repositories; namespace Bit.Api.AdminConsole.Authorization; public static class HttpContextExtensions { public const string NoOrgIdError = "A route decorated with with '[Authorize]' should include a route value named 'orgId' either through the [Controller] attribute or through a '[Http*]' attribute."; /// /// Returns the result of the callback, caching it in HttpContext.Features for the lifetime of the request. /// Subsequent calls will retrieve the cached value. /// Results are stored by type and therefore must be of a unique type. /// public static async Task WithFeaturesCacheAsync(this HttpContext httpContext, Func> callback) { var cachedResult = httpContext.Features.Get(); if (cachedResult != null) { return cachedResult; } var result = await callback(); httpContext.Features.Set(result); return result; } /// /// Returns true if the user is a ProviderUser for a Provider which manages the specified organization, otherwise false. /// This data is fetched from the database and cached as a HttpContext Feature for the lifetime of the request. /// public static async Task IsProviderUserForOrgAsync( this HttpContext httpContext, IProviderUserRepository providerUserRepository, Guid userId, Guid organizationId) { var organizations = await httpContext.GetProviderUserOrganizationsAsync(providerUserRepository, userId); return organizations.Any(o => o.OrganizationId == organizationId); } /// /// Returns the ProviderUserOrganizations for a user. These are the organizations the ProviderUser manages via their Provider, if any. /// This data is fetched from the database and cached as a HttpContext Feature for the lifetime of the request. /// /// /// /// /// private static async Task> GetProviderUserOrganizationsAsync( this HttpContext httpContext, IProviderUserRepository providerUserRepository, Guid userId) => await httpContext.WithFeaturesCacheAsync(() => providerUserRepository.GetManyOrganizationDetailsByUserAsync(userId, ProviderUserStatusType.Confirmed)); /// /// Parses the {orgId} route parameter into a Guid, or throws if the {orgId} is not present or not a valid guid. /// /// /// /// public static Guid GetOrganizationId(this HttpContext httpContext) { httpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam); if (orgIdParam == null || !Guid.TryParse(orgIdParam.ToString(), out var orgId)) { throw new InvalidOperationException(NoOrgIdError); } return orgId; } }