< Summary

Information
Class: Elsa.ExternalAuthentication.Services.DefaultPermissionDelegationAuthorizer
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Services/DefaultPermissionGrantResolver.cs
Line coverage
100%
Covered lines: 11
Uncovered lines: 0
Coverable lines: 11
Total lines: 84
Line coverage: 100%
Branch coverage
90%
Covered branches: 9
Total branches: 10
Branch coverage: 90%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
AuthorizeAsync(...)90%1010100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Services/DefaultPermissionGrantResolver.cs

#LineLine coverage
 1using Elsa.ExternalAuthentication.Contracts;
 2using Elsa.ExternalAuthentication.Models;
 3using Elsa.ExternalAuthentication.Options;
 4using Elsa.ExternalAuthentication.Permissions;
 5using Microsoft.Extensions.Options;
 6
 7namespace Elsa.ExternalAuthentication.Services;
 8
 9public sealed class DefaultPermissionGrantResolver(
 10    IEnumerable<IPermissionGrantSource> sources,
 11    IPermissionDescriptorRegistry descriptors,
 12    IOptions<ExternalAuthenticationOptions> options) : IPermissionGrantResolver
 13{
 14    private readonly IReadOnlyDictionary<string, IPermissionGrantSource> _sources = sources.ToDictionary(x => x.Type, St
 15
 16    public async ValueTask<PermissionGrantResult> ResolveAsync(PermissionGrantResolutionContext context, CancellationTok
 17    {
 18        var grants = new List<PermissionGrant>();
 19        var warnings = new List<PermissionGrantWarning>();
 20        var warningKeys = new HashSet<(string Code, string Message)>();
 21        var knownPermissions = descriptors.List().Select(x => x.Name).ToHashSet(StringComparer.Ordinal);
 22        var boundary = new PermissionGrantBoundary(options.Value.PermissionGrants);
 23
 24        foreach (var selection in context.Connection.Connection.PermissionGrantSources.OrderBy(x => x.Order).ThenBy(x =>
 25        {
 26            if (!IsAllowedSource(selection.Type) || !_sources.TryGetValue(selection.Type, out var source))
 27            {
 28                AddWarning(warnings, warningKeys, new PermissionGrantWarning("permission_grant_source_unavailable", $"Th
 29                continue;
 30            }
 31
 32            var result = await source.GetGrantsAsync(new PermissionGrantContext(context.TargetTenantId, context.UserId, 
 33            foreach (var warning in result.Warnings)
 34                AddWarning(warnings, warningKeys, warning);
 35            foreach (var grant in result.Grants)
 36            {
 37                if (!boundary.Allows(grant.Permission))
 38                {
 39                    AddWarning(warnings, warningKeys, new PermissionGrantWarning("permission_denied_by_deployment", $"Th
 40                    continue;
 41                }
 42
 43                if (!knownPermissions.Contains(grant.Permission))
 44                    AddWarning(warnings, warningKeys, new PermissionGrantWarning("unknown_permission_descriptor", $"No m
 45
 46                if (grants.All(x => !string.Equals(x.Permission, grant.Permission, StringComparison.Ordinal)))
 47                    grants.Add(grant);
 48            }
 49        }
 50
 51        return new PermissionGrantResult(grants, warnings);
 52    }
 53
 54    private bool IsAllowedSource(string type) => options.Value.AllowedPermissionGrantSourceTypes.Count == 0 || options.V
 55    private static void AddWarning(ICollection<PermissionGrantWarning> warnings, ISet<(string Code, string Message)> key
 56    {
 57        if (keys.Add((warning.Code, warning.Message)))
 58            warnings.Add(warning);
 59    }
 60}
 61
 462public sealed class DefaultPermissionDelegationAuthorizer(IOptions<ExternalAuthenticationOptions> options) : IPermission
 63{
 64    public ValueTask<PermissionDelegationResult> AuthorizeAsync(System.Security.Claims.ClaimsPrincipal actor, IReadOnlyC
 65    {
 766        cancellationToken.ThrowIfCancellationRequested();
 1867        var actorPermissions = actor.FindAll(PermissionNames.ClaimType).Select(x => x.Value).ToHashSet(StringComparer.Or
 768        var boundary = new PermissionGrantBoundary(options.Value.PermissionGrants);
 1969        var configuredPermissions = selections.SelectMany(x => PermissionGrantMappingSettings.Read(x.Settings)).SelectMa
 970            .Concat(selections.Where(x => string.Equals(x.Type, ClaimPassThroughPermissionGrantSource.SourceType, String
 771            .Distinct(StringComparer.Ordinal).OrderBy(x => x, StringComparer.Ordinal).ToArray();
 772        var unrestricted = actorPermissions.Contains(PermissionNames.All) || actorPermissions.Contains(Permissions.Exter
 773        var mayDelegate = unrestricted || actorPermissions.Contains(Permissions.ExternalAuthenticationPermissions.Permis
 1474        var unauthorized = configuredPermissions.Where(permission => !boundary.Allows(permission) || !mayDelegate || (!u
 775        return ValueTask.FromResult(new PermissionDelegationResult(unauthorized.Length == 0, unauthorized));
 76    }
 77}
 78
 79internal sealed class PermissionGrantBoundary(PermissionGrantOptions options)
 80{
 81    private readonly IReadOnlySet<string> _allowed = options.AllowedPermissions.ToHashSet(StringComparer.Ordinal);
 82    private readonly IReadOnlySet<string> _denied = options.DeniedPermissions.ToHashSet(StringComparer.Ordinal);
 83    public bool Allows(string permission) => !string.IsNullOrWhiteSpace(permission) && !_denied.Contains(permission) && 
 84}