fix(management): reconcile area role gate to Designer|Deployer across actor and all three docs (arch-review C6)

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 05:23:57 -04:00
parent 75bf14a35a
commit 51cff07753
5 changed files with 50 additions and 6 deletions
+2 -1
View File
@@ -103,7 +103,8 @@ Central cluster only. Sites have no user interface.
- `Export Bundle` (`/design/transport/export`) — multi-step wizard for exporting a `.scadabundle` artifact containing templates, shared scripts, external systems, and central-only configuration. Visible to users with `RequireDesign`.
- `Import Bundle` (`/design/transport/import`) — multi-step wizard for uploading and applying a `.scadabundle` into the current environment, with per-artifact diff review and conflict resolution. Visible to users with `RequireAdmin`.
### Area Management (Admin Role)
### Area Management (Designer or Deployer Role)
- Areas are authored inside the Deployment Topology workflow (`Topology.razor`, `RequireDeployment`); managing them is gated **any-of [Designer, Deployer]**, not Administrator (arch-review C6).
- Define hierarchical area structures per site.
- Parent-child area relationships.
- Assign areas when managing instances.
@@ -216,9 +216,10 @@ The two-person authorization workflow for writes through the MxAccess Gateway. B
Every incoming message carries the authenticated user's identity and roles. The ManagementActor enforces the same role-based authorization rules as the Central UI:
- **Admin** role required for: site management, area management, API key management, role mapping management, scope rule management, system configuration.
- **Admin** role required for: site management, API key management, role mapping management, scope rule management, system configuration.
- **Design** role required for: template authoring (including template member management: attributes, alarms, native alarm sources, scripts, compositions), shared scripts, external system definitions, database connection definitions, notification lists, inbound API method definitions.
- **Deployment** role required for: instance management (including instance alarm overrides and native alarm source overrides), deployments, debug view, debug snapshot, parked message queries, site event log queries. Site scoping is enforced for site-scoped Deployment users.
- **Any of Design / Deployment** required for: area management (`CreateAreaCommand` / `UpdateAreaCommand` / `DeleteAreaCommand`). Areas are authored both in the Designer tooling and inside the Central UI Deployment Topology workflow (`Topology.razor` is `RequireDeployment`), so either role qualifies — enforced as an any-of gate (arch-review C6). Administrator is not implicitly included.
- **Operator** role required for: submitting a secured write (`SubmitSecuredWriteCommand`).
- **Verifier** role required for: approving / rejecting a secured write (`ApproveSecuredWriteCommand` / `RejectSecuredWriteCommand`). The no-self-approval rule (`Operator ≠ Verifier`) is enforced in the handler, independent of the role check.
- **Any of Operator / Verifier / Administrator** required for: listing / querying secured writes (`ListSecuredWritesCommand`). Read-only, but the history exposes process-sensitive tag values, so it is gated any-of rather than open to every authenticated user (arch-review UA1). Enforced as an any-of gate (`GetRequiredRoles` returns the permitted set; the caller must hold at least one).
+4 -1
View File
@@ -85,7 +85,6 @@ Set in a local or docker-dev environment via the environment variable `ScadaBrid
- **Permissions**:
- Manage site definitions.
- Manage site-level data connections (define and assign to sites).
- Manage area definitions per site.
- Manage LDAP group-to-role mappings.
- Manage API keys (create, enable/disable, delete).
- System-level configuration.
@@ -100,12 +99,14 @@ Set in a local or docker-dev environment via the environment variable `ScadaBrid
- Manage database connection definitions.
- Manage notification lists and SMTP configuration.
- Manage inbound API method definitions.
- Manage area definitions per site (any-of with Deployer — see note below).
- Run on-demand validation (template flattening, script compilation).
### Deployer
- **Scope**: System-wide or site-scoped.
- **Permissions**:
- Create and manage instances (overrides, connection bindings, area assignment).
- Manage area definitions per site (any-of with Designer — see note below).
- Disable, enable, and delete instances.
- Deploy configurations to instances.
- Deploy system-wide artifacts (shared scripts, external system definitions, DB connections, data connections) to all sites.
@@ -116,6 +117,8 @@ Set in a local or docker-dev environment via the environment variable `ScadaBrid
- View site event logs.
- **Site scoping**: A user with site-scoped Deployer role can only perform these actions for instances at their permitted sites.
> **Area management (arch-review C6)**: Managing area definitions per site is gated **any-of [Designer, Deployer]**, not Administrator. Areas are authored both in the Designer tooling and inside the Central UI Deployment Topology workflow (`Topology.razor` is `RequireDeployment`), so either role qualifies; Administrator is not implicitly included in the actor gate.
### Viewer
- **Scope**: System-wide (always).
- **Permissions**:
@@ -167,6 +167,15 @@ public class ManagementActor : ReceiveActor
private static readonly string[] OperatorOnly = [Roles.Operator];
private static readonly string[] VerifierOnly = [Roles.Verifier];
// arch-review C6: area management is reconciled to any-of [Designer, Deployer].
// Areas are authored on both real surfaces — the Designer template/topology
// tooling AND the Central UI Deployment Topology workflow (Topology.razor is
// RequireDeployment) — so a pure Deployer must be able to create/update/delete
// them. The three requirements docs' historical "Administrator" assignment was
// stale; Administrator is not implicitly included here, matching the actor's
// any-of treatment of every other Designer/Deployer command.
private static readonly string[] AreaManagers = [Roles.Designer, Roles.Deployer];
// arch-review UA1: the Secured Writes history table exposes
// process-sensitive tag values, so reading it is restricted to the
// two-person Secured Write roles plus Administrator (any-of) — NOT any
@@ -205,9 +214,13 @@ public class ManagementActor : ReceiveActor
or UpdateSmtpConfigCommand
or UpdateSmsConfigCommand => AdminOnly,
// Area management — any-of [Designer, Deployer] (arch-review C6).
// Exposed both in the Designer tooling and inside the Central UI
// Deployment Topology workflow (RequireDeployment), so both roles qualify.
CreateAreaCommand or UpdateAreaCommand or DeleteAreaCommand => AreaManagers,
// Designer operations
CreateAreaCommand or DeleteAreaCommand
or CreateTemplateCommand or UpdateTemplateCommand or DeleteTemplateCommand
CreateTemplateCommand or UpdateTemplateCommand or DeleteTemplateCommand
or ValidateTemplateCommand
or CreateExternalSystemCommand or UpdateExternalSystemCommand
or DeleteExternalSystemCommand
@@ -227,7 +240,6 @@ public class ManagementActor : ReceiveActor
or CreateSharedSchemaCommand or UpdateSharedSchemaCommand or DeleteSharedSchemaCommand
or CreateDatabaseConnectionDefCommand or UpdateDatabaseConnectionDefCommand or DeleteDatabaseConnectionDefCommand
or CreateApiMethodCommand or UpdateApiMethodCommand or DeleteApiMethodCommand
or UpdateAreaCommand
or CreateTemplateFolderCommand or RenameTemplateFolderCommand
or MoveTemplateFolderCommand or ReorderTemplateFolderCommand or DeleteTemplateFolderCommand
or MoveTemplateToFolderCommand
@@ -3125,4 +3125,31 @@ public class ManagementActorTests : TestKit, IDisposable
Arg.Is<Commons.Entities.ExternalSystems.ExternalSystemDefinition>(d => d.TimeoutSeconds == 42),
Arg.Any<CancellationToken>());
}
// ========================================================================
// Area management role gate (arch-review C6): reconciled to any-of
// [Designer, Deployer]. The Central UI creates areas inside the Deployment
// Topology workflow (RequireDeployment), so a pure-Deployer must be allowed.
// ========================================================================
[Theory]
[InlineData("Designer")]
[InlineData("Deployer")]
public void CreateArea_DesignerOrDeployer_Allowed(string role)
{
var actor = CreateActor();
actor.Tell(Envelope(new CreateAreaCommand(1, "Area1", null), role));
// The gate must let both roles through. Dispatch may still fail (no
// AreaService wired), but it must NOT be an authorization rejection.
var resp = ExpectMsg<object>(TimeSpan.FromSeconds(5));
Assert.IsNotType<ManagementUnauthorized>(resp);
}
[Fact]
public void CreateArea_ViewerOnly_Unauthorized()
{
var actor = CreateActor();
actor.Tell(Envelope(new CreateAreaCommand(1, "Area1", null), "Viewer"));
ExpectMsg<ManagementUnauthorized>(TimeSpan.FromSeconds(5));
}
}