< 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: 75
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.Common.Multitenancy;
 2using Elsa.Extensions;
 3using Elsa.Workflows.Api.RealTime.Contracts;
 4using Elsa.Workflows.Management;
 5using Elsa.Workflows.Management.Entities;
 6using Elsa.Workflows.Management.Filters;
 7using FastEndpoints.Security;
 8using JetBrains.Annotations;
 9using Microsoft.AspNetCore.Authorization;
 10using Microsoft.AspNetCore.SignalR;
 11
 12namespace Elsa.Workflows.Api.RealTime.Hubs;
 13
 14/// <summary>
 15/// Represents a SignalR hub for receiving workflow events on the client.
 16/// </summary>
 17[PublicAPI]
 18[Authorize]
 19public class WorkflowInstanceHub : Hub<IWorkflowInstanceClient>
 20{
 21    private const string ReadWorkflowInstancesPermission = "read:workflow-instances";
 22    private const string ReadAllPermission = "read:*";
 123    private static readonly string[] ReadPermissions = [PermissionNames.All, ReadAllPermission, ReadWorkflowInstancesPer
 24    private readonly IWorkflowInstanceStore _workflowInstanceStore;
 25    private readonly ITenantAccessor? _tenantAccessor;
 26
 27    /// <inheritdoc />
 1128    public WorkflowInstanceHub(IWorkflowInstanceStore workflowInstanceStore, ITenantAccessor? tenantAccessor = null)
 29    {
 1130        _workflowInstanceStore = workflowInstanceStore;
 1131        _tenantAccessor = tenantAccessor;
 1132    }
 33
 34    /// <summary>
 35    /// Observes a workflow instance.
 36    /// </summary>
 37    /// <param name="instanceId">The ID of the workflow instance to observe.</param>
 38    public async Task ObserveInstanceAsync(string instanceId)
 39    {
 1040        if (!CanReadWorkflowInstances())
 141            throw new HubException("Access denied.");
 42
 943        var workflowInstance = await _workflowInstanceStore.FindAsync(new WorkflowInstanceFilter { Id = instanceId }, Co
 44
 945        if (!CanAccessTenant(workflowInstance, _tenantAccessor))
 246            throw new HubException("Access denied.");
 47
 48        // Join the user to the workflow instance group.
 749        await Groups.AddToGroupAsync(Context.ConnectionId, instanceId, Context.ConnectionAborted);
 750    }
 51
 52    private bool CanReadWorkflowInstances()
 53    {
 1054        var user = Context.User;
 55
 1056        if (user?.Identity?.IsAuthenticated != true)
 057            return false;
 58
 1059        return ReadPermissions.Any(user.HasPermission);
 60    }
 61
 62    private static bool CanAccessTenant(WorkflowInstance? workflowInstance, ITenantAccessor? tenantAccessor)
 63    {
 964        if (workflowInstance == null)
 165            return false;
 66
 867        if (tenantAccessor == null)
 168            return true;
 69
 770        var workflowInstanceTenantId = workflowInstance.TenantId.NormalizeTenantId();
 771        var currentTenantId = tenantAccessor.TenantId.NormalizeTenantId();
 72
 773        return workflowInstanceTenantId == Tenant.AgnosticTenantId || workflowInstanceTenantId == currentTenantId;
 74    }
 75}