|
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
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'; |
18 | 26 | 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'; |
20 | 33 | import { randomUUID } from 'crypto'; |
21 | 34 | 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'; |
25 | 43 |
|
26 | 44 | type StartRunValidatedInput = { |
27 | 45 | readonly agentId: string; |
@@ -58,6 +76,35 @@ StartRunValidatedInput, |
58 | 76 | this.credentials = credentials; |
59 | 77 | } |
60 | 78 |
|
| 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 | + |
61 | 108 | protected verifyInfrastructureDependencies(): void { |
62 | 109 | // Enforce rigid boot-readiness invariants for storage adapters |
63 | 110 | if (!this.runStore) { |
@@ -154,36 +201,6 @@ StartRunValidatedInput, |
154 | 201 | }; |
155 | 202 | } |
156 | 203 |
|
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 | | - |
187 | 204 | protected async handle(input: StartRunValidatedInput, context: CommandContext): Promise<{ readonly runId: string; readonly status: string }> { |
188 | 205 | const runId = randomUUID(); |
189 | 206 |
|
|
0 commit comments