fix(management): honor MgmtDeployArtifactsCommand.SiteId + enforce site scope (arch-review C2)
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -760,7 +760,7 @@ scadabridge --url <url> site deploy-artifacts [--site-id <int>]
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--site-id` | no | Target site ID; omit to deploy to all sites |
|
||||
| `--site-id` | no | Target site ID; deploys system-wide artifacts to exactly that site. Omit to deploy fleet-wide to all sites. A site-scoped (non-Administrator) Deployer must supply an in-scope `--site-id`; fleet-wide deployment requires a system-wide Deployer or Administrator (arch-review C2). |
|
||||
|
||||
#### `site area list`
|
||||
|
||||
@@ -840,7 +840,7 @@ scadabridge --url <url> deploy artifacts [--site-id <int>]
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--site-id` | no | Target site ID; omit for all sites |
|
||||
| `--site-id` | no | Target site ID; deploys to exactly that site. Omit to deploy fleet-wide. A site-scoped (non-Administrator) Deployer must supply an in-scope `--site-id`; fleet-wide requires a system-wide Deployer or Administrator (arch-review C2). |
|
||||
|
||||
#### `deploy status`
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ public class ArtifactDeploymentService
|
||||
/// <param name="user">The user initiating the deployment.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A summary of the artifact deployment results for all sites.</returns>
|
||||
public async Task<Result<ArtifactDeploymentSummary>> DeployToAllSitesAsync(
|
||||
public virtual async Task<Result<ArtifactDeploymentSummary>> DeployToAllSitesAsync(
|
||||
string user,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -243,7 +243,7 @@ public class ArtifactDeploymentService
|
||||
/// <param name="user">The user initiating the deployment.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A summary of the artifact deployment result for the single site.</returns>
|
||||
public async Task<Result<ArtifactDeploymentSummary>> DeployToSiteAsync(
|
||||
public virtual async Task<Result<ArtifactDeploymentSummary>> DeployToSiteAsync(
|
||||
int siteId,
|
||||
string user,
|
||||
CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -433,7 +433,7 @@ public class ManagementActor : ReceiveActor
|
||||
DeleteScopeRuleCommand cmd => await HandleDeleteScopeRule(sp, cmd, user.Username),
|
||||
|
||||
// Deployments
|
||||
MgmtDeployArtifactsCommand cmd => await HandleDeployArtifacts(sp, cmd, user.Username),
|
||||
MgmtDeployArtifactsCommand cmd => await HandleDeployArtifacts(sp, cmd, user),
|
||||
QueryDeploymentsCommand cmd => await HandleQueryDeployments(sp, cmd, user),
|
||||
GetDeploymentDiffCommand cmd => await HandleGetDeploymentDiff(sp, cmd, user),
|
||||
|
||||
@@ -2205,10 +2205,24 @@ public class ManagementActor : ReceiveActor
|
||||
// Deployment handlers
|
||||
// ========================================================================
|
||||
|
||||
private static async Task<object?> HandleDeployArtifacts(IServiceProvider sp, MgmtDeployArtifactsCommand cmd, string user)
|
||||
private static async Task<object?> HandleDeployArtifacts(IServiceProvider sp, MgmtDeployArtifactsCommand cmd, AuthenticatedUser user)
|
||||
{
|
||||
// A site-scoped Deployer may not trigger a FLEET-WIDE artifact deployment:
|
||||
// EnforceSiteScope(null) is a no-op by design, so the fleet-wide case needs
|
||||
// its own guard (arch-review C2 authorization gap).
|
||||
if (cmd.SiteId is null
|
||||
&& user.PermittedSiteIds.Length > 0
|
||||
&& !user.Roles.Contains(Roles.Administrator, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new SiteScopeViolationException(
|
||||
"Site-scoped users cannot deploy artifacts fleet-wide; specify a target site id.");
|
||||
}
|
||||
EnforceSiteScope(user, cmd.SiteId);
|
||||
|
||||
var svc = sp.GetRequiredService<ArtifactDeploymentService>();
|
||||
var result = await svc.DeployToAllSitesAsync(user);
|
||||
var result = cmd.SiteId is int siteId
|
||||
? await svc.DeployToSiteAsync(siteId, user.Username)
|
||||
: await svc.DeployToAllSitesAsync(user.Username);
|
||||
return result.IsSuccess
|
||||
? result.Value
|
||||
: throw new ManagementCommandException(result.Error);
|
||||
|
||||
Reference in New Issue
Block a user