< Summary

Information
Class: Elsa.Workflows.Api.RealTime.Hubs.WorkflowInstanceHub
Assembly: Elsa.Workflows.Api
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs
Line coverage
95%
Covered lines: 22
Uncovered lines: 1
Coverable lines: 23
Total lines: 74
Line coverage: 95.6%
Branch coverage
81%
Covered branches: 13
Total branches: 16
Branch coverage: 81.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
ObserveInstanceAsync()100%44100%
CanReadWorkflowInstances()50%7675%
CanAccessTenant(...)100%66100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs

#LineLine coverage
 1using Elsa.Authorization;
 2using Elsa.Common.Multitenancy;
 3using Elsa.Extensions;
 4using Elsa.Workflows.Api.RealTime.Contracts;
 5using Elsa.Workflows.Management;
 6using Elsa.Workflows.Management.Entities;
 7using Elsa.Workflows.Management.Filters;
 8using FastEndpoints.Security;
 9using JetBrains.Annotations;
 10using Microsoft.AspNetCore.Authorization;
 11using Microsoft.AspNetCore.SignalR;
 12
 13namespace Elsa.Workflows.Api.RealTime.Hubs;
 14
 15/// <summary>
 16/// Represents a SignalR hub for receiving workflow events on the client.
 17/// </summary>
 18[PublicAPI]
 19[Authorize]
 20public class WorkflowInstanceHub : Hub<IWorkflowInstanceClient>
 21{
 122    private static readonly Permission ReadInstances = new(Elsa.Workflows.Api.Permissions.WorkflowPermissions.Instances,
 23    private readonly IWorkflowInstanceStore _workflowInstanceStore;
 24    private readonly ITenantAccessor? _tenantAccessor;
 25
 26    /// <inheritdoc />
 1127    public WorkflowInstanceHub(IWorkflowInstanceStore workflowInstanceStore, ITenantAccessor? tenantAccessor = null)
 28    {
 1129        _workflowInstanceStore = workflowInstanceStore;
 1130        _tenantAccessor = tenantAccessor;
 1131    }
 32
 33    /// <summary>
 34    /// Observes a workflow instance.
 35    /// </summary>
 36    /// <param name="instanceId">The ID of the workflow instance to observe.</param>
 37    public async Task ObserveInstanceAsync(string instanceId)
 38    {
 1039        if (!CanReadWorkflowInstances())
 140            throw new HubException("Access denied.");
 41
 942        var workflowInstance = await _workflowInstanceStore.FindAsync(new WorkflowInstanceFilter { Id = instanceId }, Co
 43
 944        if (!CanAccessTenant(workflowInstance, _tenantAccessor))
 245            throw new HubException("Access denied.");
 46
 47        // Join the user to the workflow instance group.
 748        await Groups.AddToGroupAsync(Context.ConnectionId, instanceId, Context.ConnectionAborted);
 749    }
 50
 51    private bool CanReadWorkflowInstances()
 52    {
 1053        var user = Context.User;
 54
 1055        if (user?.Identity?.IsAuthenticated != true)
 056            return false;
 57
 1058        return PermissionEvaluator.Shared.HasPermission(user, ReadInstances);
 59    }
 60
 61    private static bool CanAccessTenant(WorkflowInstance? workflowInstance, ITenantAccessor? tenantAccessor)
 62    {
 963        if (workflowInstance == null)
 164            return false;
 65
 866        if (tenantAccessor == null)
 167            return true;
 68
 769        var workflowInstanceTenantId = workflowInstance.TenantId.NormalizeTenantId();
 770        var currentTenantId = tenantAccessor.TenantId.NormalizeTenantId();
 71
 772        return workflowInstanceTenantId == Tenant.AgnosticTenantId || workflowInstanceTenantId == currentTenantId;
 73    }
 74}