Skip to content

Commit 0c26ed4

Browse files
committed
Updates to auth routines in new CQRS system, move permission.ts to kernel/node
1 parent 64dd5aa commit 0c26ed4

7 files changed

Lines changed: 228 additions & 190 deletions

File tree

‎plugins/kernel/backend/src/api/commands/ApproveRunCommand.ts‎

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,19 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { NotAllowedError, NotFoundError, ConflictError, NotImplementedError } from '@backstage/errors';
17-
import { PermissionsService, BackstageCredentials } from '@backstage/backend-plugin-api';
16+
import {
17+
ConflictError,
18+
NotAllowedError,
19+
NotFoundError,
20+
NotImplementedError,
21+
} from '@backstage/errors';
22+
import {
23+
BackstageCredentials,
24+
PermissionsService,
25+
} from '@backstage/backend-plugin-api';
1826
import { ResourcePermission } from '@backstage/plugin-permission-common';
1927
import {
28+
aiPermissions,
2029
ApprovalDecision,
2130
ArtifactSink,
2231
AuditLogSink,
@@ -27,9 +36,14 @@ import {
2736
ToolRegistry,
2837
} from '@ai-crew-suite/plugin-kernel-node';
2938
import { BaseKernelCommand } from './BaseKernelCommand';
30-
import { CommandContext, PackedRequestInput } from './types';
31-
import { aiPermissions } from '../permissions';
32-
import { ApproveRunBodySchema, ApproveRunParamsSchema } from '../controller/schemas';
39+
import {
40+
CommandContext,
41+
PackedRequestInput,
42+
} from './types';
43+
import {
44+
ApproveRunBodySchema,
45+
ApproveRunParamsSchema,
46+
} from '../controller/schemas';
3347
import { AgentRuntime } from '../../runtime/AgentRuntime';
3448

3549
type ApproveRunValidatedInput = {
@@ -41,7 +55,10 @@ type ApproveRunValidatedInput = {
4155
/**
4256
* Concrete CQRS Command handling workflow supervisor approvals.
4357
*/
44-
export class ApproveRunCommand extends BaseKernelCommand<ApproveRunValidatedInput, { success: boolean; status: string }> {
58+
export class ApproveRunCommand extends BaseKernelCommand<
59+
ApproveRunValidatedInput,
60+
{ success: boolean; status: string }
61+
> {
4562
private readonly credentials: BackstageCredentials;
4663

4764
public constructor(
@@ -67,28 +84,6 @@ export class ApproveRunCommand extends BaseKernelCommand<ApproveRunValidatedInpu
6784
this.credentials = credentials;
6885
}
6986

70-
protected verifyInfrastructureDependencies(): void {
71-
if (!this.runStore) {
72-
throw new NotImplementedError(
73-
'Run persistence store is not configured on this AI backend kernel node.'
74-
);
75-
}
76-
}
77-
78-
protected validate(input: PackedRequestInput, _context: CommandContext): ApproveRunValidatedInput {
79-
const { paramsData, bodyData } = this.parseCombinedSchemas(
80-
ApproveRunParamsSchema,
81-
ApproveRunBodySchema,
82-
input
83-
);
84-
85-
return {
86-
runId: paramsData.id,
87-
status: bodyData.status,
88-
note: bodyData.note ? bodyData.note.trim() : undefined,
89-
};
90-
}
91-
9287
protected async authorize(input: ApproveRunValidatedInput, context: CommandContext): Promise<void> {
9388
const targetPermission = aiPermissions.agentApprove as ResourcePermission<string>;
9489

@@ -111,6 +106,27 @@ export class ApproveRunCommand extends BaseKernelCommand<ApproveRunValidatedInpu
111106
}
112107
}
113108

109+
protected validate(input: PackedRequestInput, _context: CommandContext): ApproveRunValidatedInput {
110+
const { paramsData, bodyData } = this.parseCombinedSchemas(
111+
ApproveRunParamsSchema,
112+
ApproveRunBodySchema,
113+
input
114+
);
115+
116+
return {
117+
runId: paramsData.id,
118+
status: bodyData.status,
119+
note: bodyData.note ? bodyData.note.trim() : undefined,
120+
};
121+
}
122+
123+
protected verifyInfrastructureDependencies(): void {
124+
if (!this.runStore) {
125+
throw new NotImplementedError(
126+
'Run persistence store is not configured on this AI backend kernel node.'
127+
);
128+
}
129+
}
114130

115131
protected async handle(
116132
input: ApproveRunValidatedInput,

‎plugins/kernel/backend/src/api/commands/StartRunCommand.ts‎

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,33 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { InputError, NotAllowedError, ConflictError, NotImplementedError } from '@backstage/errors';
17-
import { PermissionsService, BackstageCredentials } from '@backstage/backend-plugin-api';
16+
import {
17+
ConflictError,
18+
InputError,
19+
NotAllowedError,
20+
NotImplementedError,
21+
} from '@backstage/errors';
22+
import {
23+
BackstageCredentials,
24+
PermissionsService,
25+
} from '@backstage/backend-plugin-api';
1826
import { ResourcePermission } from '@backstage/plugin-permission-common';
19-
import { RunStore, HardeningOptions, RunRecord } from '@ai-crew-suite/plugin-kernel-node';
27+
import {
28+
aiPermissions,
29+
HardeningOptions,
30+
RunRecord,
31+
RunStore,
32+
} from '@ai-crew-suite/plugin-kernel-node';
2033
import { randomUUID } from 'crypto';
2134
import { BaseKernelCommand } from './BaseKernelCommand';
22-
import { CommandContext, PackedRequestInput } from './types';
23-
import { aiPermissions } from '../permissions';
24-
import { StartRunBodySchema, StartRunParamsSchema } from '../controller/schemas';
35+
import {
36+
CommandContext,
37+
PackedRequestInput,
38+
} from './types';
39+
import {
40+
StartRunBodySchema,
41+
StartRunParamsSchema,
42+
} from '../controller/schemas';
2543

2644
type StartRunValidatedInput = {
2745
readonly agentId: string;
@@ -58,6 +76,35 @@ StartRunValidatedInput,
5876
this.credentials = credentials;
5977
}
6078

79+
protected async authorize(input: StartRunValidatedInput, context: CommandContext): Promise<void> {
80+
const targetPermission = aiPermissions.agentRun as ResourcePermission<string>;
81+
const decisions = await this.permissions.authorize(
82+
[{ permission: targetPermission, resourceRef: input.agentId }],
83+
{ credentials: this.credentials }
84+
);
85+
86+
const [mainDecision] = decisions;
87+
88+
// Parity Check: Reject if array payload is completely empty
89+
if (!mainDecision) {
90+
context.logger.error('RBAC critical evaluation failure: Authorization response payload was completely empty');
91+
92+
throw new Error('Internal authorization parsing failure encountered');
93+
}
94+
95+
if (mainDecision.result === 'DENY') {
96+
context.logger.warn(
97+
`RBAC violation intercepted: UserRef [${context.actorIdentity}] denied access to permission [${aiPermissions.agentRun.name}]`
98+
);
99+
100+
throw new NotAllowedError(`Access Denied: Actor lacks required scope: ${aiPermissions.agentRun.name}`);
101+
}
102+
103+
if (!this.consumeRateLimit(input.agentId)) {
104+
throw new ConflictError('Rate limit exceeded for agent. Core capacity thresholds exhausted.');
105+
}
106+
}
107+
61108
protected verifyInfrastructureDependencies(): void {
62109
// Enforce rigid boot-readiness invariants for storage adapters
63110
if (!this.runStore) {
@@ -154,36 +201,6 @@ StartRunValidatedInput,
154201
};
155202
}
156203

157-
protected async authorize(input: StartRunValidatedInput, context: CommandContext): Promise<void> {
158-
const targetPermission = aiPermissions.agentRun as ResourcePermission<string>;
159-
const decisions = await this.permissions.authorize(
160-
[{ permission: targetPermission, resourceRef: input.agentId }],
161-
{ credentials: this.credentials }
162-
);
163-
164-
const [mainDecision] = decisions;
165-
166-
// Parity Check: Reject if array payload is completely empty
167-
if (!mainDecision) {
168-
context.logger.error('RBAC critical evaluation failure: Authorization response payload was completely empty');
169-
170-
throw new Error('Internal authorization parsing failure encountered');
171-
}
172-
173-
if (mainDecision.result === 'DENY') {
174-
context.logger.warn(
175-
`RBAC violation intercepted: UserRef [${context.actorIdentity}] denied access to permission [${aiPermissions.agentRun.name}]`
176-
);
177-
178-
throw new NotAllowedError(`Access Denied: Actor lacks required scope: ${aiPermissions.agentRun.name}`);
179-
}
180-
181-
if (!this.consumeRateLimit(input.agentId)) {
182-
throw new ConflictError('Rate limit exceeded for agent. Core capacity thresholds exhausted.');
183-
}
184-
}
185-
186-
187204
protected async handle(input: StartRunValidatedInput, context: CommandContext): Promise<{ readonly runId: string; readonly status: string }> {
188205
const runId = randomUUID();
189206

0 commit comments

Comments
 (0)