< Summary

Information
Class: Elsa.Identity.Services.RoleAuthorizationService
Assembly: Elsa.Identity
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Identity/Services/RoleAuthorizationService.cs
Line coverage
84%
Covered lines: 22
Uncovered lines: 4
Coverable lines: 26
Total lines: 67
Line coverage: 84.6%
Branch coverage
64%
Covered branches: 9
Total branches: 14
Branch coverage: 64.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
CanAssignRolesAsync()62.5%8888.88%
CanCreateRoleWithPermissions(...)100%210%
CanMutateRole(...)100%22100%
HasAllPermissions(...)50%6680%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Identity/Services/RoleAuthorizationService.cs

#LineLine coverage
 1using System.Security.Claims;
 2using Elsa.Authorization;
 3using Elsa.Extensions;
 4using Elsa.Identity.Contracts;
 5using Elsa.Identity.Entities;
 6
 7namespace Elsa.Identity.Services;
 8
 9/// <inheritdoc />
 2610public class RoleAuthorizationService(IRoleProvider roleProvider, IPermissionEvaluator evaluator) : IRoleAuthorizationSe
 11{
 12    /// <inheritdoc />
 13    public async Task<bool> CanAssignRolesAsync(ClaimsPrincipal user, IEnumerable<string>? roleIds, CancellationToken ca
 14    {
 15        var requestedRoleIds = roleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
 316        if (requestedRoleIds == null || requestedRoleIds.Count == 0)
 017            return true;
 18
 319        var requestedRoleIdSet = requestedRoleIds.ToHashSet(StringComparer.Ordinal);
 320        var roles = (await roleProvider.FindByIdsAsync(requestedRoleIds, cancellationToken))
 221            .Where(x => requestedRoleIdSet.Contains(x.Id))
 322            .ToList();
 23        var resolvedRoleIdSet = roles.Select(x => x.Id).ToHashSet(StringComparer.Ordinal);
 324        if (!resolvedRoleIdSet.SetEquals(requestedRoleIdSet))
 125            return false;
 26
 27        var permissions = roles.SelectMany(x => x.Permissions);
 228        return HasAllPermissions(user, permissions);
 329    }
 30
 31    /// <inheritdoc />
 032    public bool CanCreateRoleWithPermissions(ClaimsPrincipal user, IEnumerable<string>? permissions) => HasAllPermission
 33
 34    /// <inheritdoc />
 35    public bool CanMutateRole(ClaimsPrincipal user, Role role, IEnumerable<string>? replacementPermissions = null)
 36    {
 3137        var permissions = replacementPermissions == null
 3138            ? role.Permissions
 3139            : role.Permissions.Concat(replacementPermissions);
 40
 3141        return HasAllPermissions(user, permissions);
 42    }
 43
 44    /// <remarks>
 45    /// Containment is evaluated through <see cref="IPermissionEvaluator"/> rather than by set membership,
 46    /// so a caller holding <c>workflows/*:view</c> may grant <c>workflows/definitions:view</c>, while a
 47    /// caller holding only the concrete grant may not grant the broader wildcard. Set membership would get
 48    /// the first case wrong and force administrators to hold every concrete grant they wish to delegate.
 49    /// </remarks>
 50    private bool HasAllPermissions(ClaimsPrincipal user, IEnumerable<string>? permissions)
 51    {
 3352        if (permissions is null)
 053            return true;
 54
 3355        var required = permissions
 456            .Where(x => !string.IsNullOrWhiteSpace(x))
 3357            .Distinct(StringComparer.Ordinal)
 458            .Select(x => Permission.TryParse(x, out var permission) ? permission : (Permission?)null)
 3359            .ToArray();
 60
 61        // A permission that does not parse cannot be reasoned about, so it is never delegable.
 3762        if (required.Any(x => x is null))
 063            return false;
 64
 3665        return evaluator.HasAllPermissions(user, required.Select(x => x!.Value));
 66    }
 67}