< Summary

Information
Class: Elsa.Workflows.Api.Security.WorkflowDefinitionScriptAuthorizationResult
Assembly: Elsa.Workflows.Api
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Api/Security/WorkflowDefinitionScriptAuthorizationService.cs
Line coverage
50%
Covered lines: 2
Uncovered lines: 2
Coverable lines: 4
Total lines: 106
Line coverage: 50%
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_Succeeded()100%11100%
Allowed()100%11100%
HostDisabled(...)100%210%
MissingPermission()100%210%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Api/Security/WorkflowDefinitionScriptAuthorizationService.cs

#LineLine coverage
 1using System.Security.Claims;
 2using Elsa.Expressions.Contracts;
 3using Elsa.Extensions;
 4using Elsa.Workflows.Activities;
 5using Elsa.Workflows.Management.Models;
 6
 7namespace Elsa.Workflows.Api.Security;
 8
 9internal class WorkflowDefinitionScriptAuthorizationService(
 10    IActivityVisitor activityVisitor,
 11    IExpressionDescriptorRegistry expressionDescriptorRegistry)
 12{
 13    private static readonly ScriptPolicy[] ScriptPolicies =
 14    [
 15        new(
 16            "CSharp",
 17            WorkflowScriptActivityTypeNames.RunCSharp,
 18            PermissionNames.ExecuteCSharpExpressions,
 19            "C# workflow expression execution is disabled by the host. Set CSharpOptions.AllowHostCodeExecution to true 
 20        new(
 21            "Python",
 22            WorkflowScriptActivityTypeNames.RunPython,
 23            PermissionNames.ExecutePythonExpressions,
 24            "Python.NET workflow expression execution is disabled by the host. Set PythonOptions.AllowHostCodeExecution 
 25    ];
 26
 27    public async Task<WorkflowDefinitionScriptAuthorizationResult> AuthorizeAsync(WorkflowDefinitionModel model, ClaimsP
 28    {
 29        if (model.Root == null)
 30            return WorkflowDefinitionScriptAuthorizationResult.Allowed();
 31
 32        return await AuthorizeAsync(model.Root, user, cancellationToken);
 33    }
 34
 35    public async Task<WorkflowDefinitionScriptAuthorizationResult> AuthorizeAsync(IActivity root, ClaimsPrincipal user, 
 36    {
 37        var scriptUsages = await GetUsedScriptPoliciesAsync(root, cancellationToken);
 38
 39        var failure = scriptUsages
 40            .Select(policy => AuthorizeScriptUsage(policy, user))
 41            .FirstOrDefault(result => result is { Succeeded: false });
 42
 43        if (failure.FailureReason.HasValue)
 44            return failure;
 45
 46        return WorkflowDefinitionScriptAuthorizationResult.Allowed();
 47    }
 48
 49    public async Task<WorkflowDefinitionScriptAuthorizationResult> AuthorizeAsync(Workflow workflow, ClaimsPrincipal use
 50    {
 51        return await AuthorizeAsync((IActivity)workflow, user, cancellationToken);
 52    }
 53
 54    private WorkflowDefinitionScriptAuthorizationResult AuthorizeScriptUsage(ScriptPolicy policy, ClaimsPrincipal user)
 55    {
 56        // Language-specific options live in optional modules. Workflows.Api observes the descriptor state projected by 
 57        if (expressionDescriptorRegistry.Find(policy.ExpressionType)?.IsBrowsable != true)
 58            return WorkflowDefinitionScriptAuthorizationResult.HostDisabled(policy.HostDisabledMessage);
 59
 60        return HasPermission(user, policy.Permission)
 61            ? WorkflowDefinitionScriptAuthorizationResult.Allowed()
 62            : WorkflowDefinitionScriptAuthorizationResult.MissingPermission();
 63    }
 64
 65    private async Task<IEnumerable<ScriptPolicy>> GetUsedScriptPoliciesAsync(IActivity root, CancellationToken cancellat
 66    {
 67        var graph = await activityVisitor.VisitAsync(root, cancellationToken);
 68        var nodes = new[] { graph }.Concat(graph.Descendants()).ToList();
 69        var policies = ScriptPolicies
 70            .Where(policy => nodes.Any(x => IsRunActivity(x.Activity, policy) || HasExpression(x.Activity, policy)))
 71            .ToList();
 72
 73        return policies;
 74    }
 75
 76    private static bool IsRunActivity(IActivity activity, ScriptPolicy policy) =>
 77        string.Equals(activity.Type, policy.RunActivityType, StringComparison.Ordinal);
 78
 79    private static bool HasExpression(IActivity activity, ScriptPolicy policy) =>
 80        activity.GetInputs().Any(x => string.Equals(x.Expression?.Type, policy.ExpressionType, StringComparison.Ordinal)
 81
 82    private static bool HasPermission(ClaimsPrincipal user, string permission)
 83    {
 84        return user.Claims.Any(x =>
 85            x.Type == PermissionNames.ClaimType &&
 86            (string.Equals(x.Value, PermissionNames.All, StringComparison.Ordinal) ||
 87             string.Equals(x.Value, permission, StringComparison.Ordinal)));
 88    }
 89
 90    private sealed record ScriptPolicy(string ExpressionType, string RunActivityType, string Permission, string HostDisa
 91}
 92
 2593internal readonly record struct WorkflowDefinitionScriptAuthorizationResult(bool Succeeded, WorkflowDefinitionScriptAuth
 94{
 1495    public static WorkflowDefinitionScriptAuthorizationResult Allowed() => new(true, null, null);
 96
 097    public static WorkflowDefinitionScriptAuthorizationResult HostDisabled(string message) => new(false, WorkflowDefinit
 98
 099    public static WorkflowDefinitionScriptAuthorizationResult MissingPermission() => new(false, WorkflowDefinitionScript
 100}
 101
 102internal enum WorkflowDefinitionScriptAuthorizationFailureReason
 103{
 104    HostDisabled,
 105    MissingPermission
 106}