< Summary

Information
Class: Elsa.ExternalAuthentication.Services.DefaultPermissionGrantResolver
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Services/DefaultPermissionGrantResolver.cs
Line coverage
90%
Covered lines: 28
Uncovered lines: 3
Coverable lines: 31
Total lines: 84
Line coverage: 90.3%
Branch coverage
75%
Covered branches: 12
Total branches: 16
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ResolveAsync()81.25%171684.21%
IsAllowedSource(...)50%22100%
AddWarning(...)100%22100%

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
 59public sealed class DefaultPermissionGrantResolver(
 510    IEnumerable<IPermissionGrantSource> sources,
 511    IPermissionDescriptorRegistry descriptors,
 512    IOptions<ExternalAuthenticationOptions> options) : IPermissionGrantResolver
 13{
 2514    private readonly IReadOnlyDictionary<string, IPermissionGrantSource> _sources = sources.ToDictionary(x => x.Type, St
 15
 16    public async ValueTask<PermissionGrantResult> ResolveAsync(PermissionGrantResolutionContext context, CancellationTok
 17    {
 718        var grants = new List<PermissionGrant>();
 719        var warnings = new List<PermissionGrantWarning>();
 720        var warningKeys = new HashSet<(string Code, string Message)>();
 721        var knownPermissions = descriptors.List().Select(x => x.Name).ToHashSet(StringComparer.Ordinal);
 722        var boundary = new PermissionGrantBoundary(options.Value.PermissionGrants);
 23
 5424        foreach (var selection in context.Connection.Connection.PermissionGrantSources.OrderBy(x => x.Order).ThenBy(x =>
 25        {
 1026            if (!IsAllowedSource(selection.Type) || !_sources.TryGetValue(selection.Type, out var source))
 27            {
 028                AddWarning(warnings, warningKeys, new PermissionGrantWarning("permission_grant_source_unavailable", $"Th
 029                continue;
 30            }
 31
 1032            var result = await source.GetGrantsAsync(new PermissionGrantContext(context.TargetTenantId, context.UserId, 
 2033            foreach (var warning in result.Warnings)
 034                AddWarning(warnings, warningKeys, warning);
 4635            foreach (var grant in result.Grants)
 36            {
 1337                if (!boundary.Allows(grant.Permission))
 38                {
 339                    AddWarning(warnings, warningKeys, new PermissionGrantWarning("permission_denied_by_deployment", $"Th
 340                    continue;
 41                }
 42
 1043                if (!knownPermissions.Contains(grant.Permission))
 1044                    AddWarning(warnings, warningKeys, new PermissionGrantWarning("unknown_permission_descriptor", $"No m
 45
 1946                if (grants.All(x => !string.Equals(x.Permission, grant.Permission, StringComparison.Ordinal)))
 747                    grants.Add(grant);
 48            }
 49        }
 50
 751        return new PermissionGrantResult(grants, warnings);
 752    }
 53
 1054    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    {
 1357        if (keys.Add((warning.Code, warning.Message)))
 958            warnings.Add(warning);
 1359    }
 60}
 61
 62public sealed class DefaultPermissionDelegationAuthorizer(IOptions<ExternalAuthenticationOptions> options) : IPermission
 63{
 64    public ValueTask<PermissionDelegationResult> AuthorizeAsync(System.Security.Claims.ClaimsPrincipal actor, IReadOnlyC
 65    {
 66        cancellationToken.ThrowIfCancellationRequested();
 67        var actorPermissions = actor.FindAll(PermissionNames.ClaimType).Select(x => x.Value).ToHashSet(StringComparer.Or
 68        var boundary = new PermissionGrantBoundary(options.Value.PermissionGrants);
 69        var configuredPermissions = selections.SelectMany(x => PermissionGrantMappingSettings.Read(x.Settings)).SelectMa
 70            .Concat(selections.Where(x => string.Equals(x.Type, ClaimPassThroughPermissionGrantSource.SourceType, String
 71            .Distinct(StringComparer.Ordinal).OrderBy(x => x, StringComparer.Ordinal).ToArray();
 72        var unrestricted = actorPermissions.Contains(PermissionNames.All) || actorPermissions.Contains(Permissions.Exter
 73        var mayDelegate = unrestricted || actorPermissions.Contains(Permissions.ExternalAuthenticationPermissions.Permis
 74        var unauthorized = configuredPermissions.Where(permission => !boundary.Allows(permission) || !mayDelegate || (!u
 75        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}