< Summary

Information
Class: Elsa.Permissions.PermissionGrantValidationResult
Assembly: Elsa.Api.Common
File(s): /home/runner/work/elsa-core/elsa-core/src/common/Elsa.Api.Common/Permissions/PermissionGrantValidator.cs
Line coverage
100%
Covered lines: 3
Uncovered lines: 0
Coverable lines: 3
Total lines: 79
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Errors()100%11100%
get_IsValid()100%11100%
get_Valid()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/common/Elsa.Api.Common/Permissions/PermissionGrantValidator.cs

#LineLine coverage
 1using Elsa.Authorization;
 2
 3namespace Elsa.Permissions;
 4
 5/// <summary>Why one submitted permission was rejected.</summary>
 6public record PermissionGrantError(string Permission, string Reason);
 7
 8/// <summary>The outcome of validating a set of submitted permissions.</summary>
 429public record PermissionGrantValidationResult(IReadOnlyCollection<PermissionGrantError> Errors)
 10{
 11    /// <summary>Whether every submitted permission was acceptable.</summary>
 2012    public bool IsValid => Errors.Count == 0;
 13
 14    /// <summary>An accepted result.</summary>
 1215    public static PermissionGrantValidationResult Valid { get; } = new([]);
 16}
 17
 18/// <summary>Validates permissions submitted when authoring a role, before they are persisted.</summary>
 19public interface IPermissionGrantValidator
 20{
 21    /// <summary>Validates <paramref name="permissions"/> against the catalog.</summary>
 22    PermissionGrantValidationResult Validate(IEnumerable<string>? permissions);
 23}
 24
 25/// <inheritdoc />
 26/// <remarks>
 27/// Concrete segments are validated against the registry; wildcard segments are validated structurally
 28/// only. A wildcard is deliberately accepted **even when it currently matches nothing** — a grant naming
 29/// a module that is not installed yet must survive, because installing that module later is exactly what
 30/// gives the grant meaning. Validating wildcards against the catalog would reject <c>workflows/*:view</c>,
 31/// which is the grant the hierarchy exists to make possible.
 32/// </remarks>
 33public sealed class PermissionGrantValidator(IPermissionDescriptorRegistry registry) : IPermissionGrantValidator
 34{
 35    /// <inheritdoc />
 36    public PermissionGrantValidationResult Validate(IEnumerable<string>? permissions)
 37    {
 38        if (permissions is null)
 39            return PermissionGrantValidationResult.Valid;
 40
 41        var errors = new List<PermissionGrantError>();
 42
 43        foreach (var value in permissions.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.Ordinal))
 44        {
 45            if (!Permission.TryParse(value, out var permission))
 46            {
 47                errors.Add(new(value, "Not a well-formed permission. Expected '{resource}:{verb}'."));
 48                continue;
 49            }
 50
 51            // An entry like 'workflows*:delete' parses but the matcher never satisfies it, so it would
 52            // be persisted as a grant that silently reaches nothing.
 53            if (!permission.IsValidPattern)
 54            {
 55                errors.Add(new(value, "Places '*' where it has no meaning and would match nothing. A wildcard may only b
 56                continue;
 57            }
 58
 59            if (permission.IsResourceWildcard || permission.IsSubtree)
 60                continue;
 61
 62            var descriptor = registry.Find(permission.Resource);
 63
 64            if (descriptor is null)
 65            {
 66                errors.Add(new(value, $"No module registers the resource '{permission.Resource}'."));
 67                continue;
 68            }
 69
 70            if (permission.IsVerbWildcard)
 71                continue;
 72
 73            if (!descriptor.Supports(permission.Verb))
 74                errors.Add(new(value, $"The resource '{permission.Resource}' does not support the verb '{permission.Verb
 75        }
 76
 77        return errors.Count == 0 ? PermissionGrantValidationResult.Valid : new(errors);
 78    }
 79}