diff --git a/.changeset/actions-permissions-api.md b/.changeset/actions-permissions-api.md index 193e84b7a7..c38099a8a9 100644 --- a/.changeset/actions-permissions-api.md +++ b/.changeset/actions-permissions-api.md @@ -2,7 +2,7 @@ '@backstage/backend-plugin-api': minor --- -Added optional `permission` field to `ActionsRegistryActionOptions`, allowing actions to declare a `BasicPermission` that controls visibility and access. +Added optional `visibilityPermission` field to `ActionsRegistryActionOptions`, allowing actions to declare a `BasicPermission` that controls visibility and access. ```typescript import { createPermission } from '@backstage/plugin-permission-common'; @@ -16,7 +16,7 @@ actionsRegistry.register({ name: 'my-action', title: 'My Action', description: 'An action that requires permission', - permission: myPermission, + visibilityPermission: myPermission, schema: { input: z => z.object({ name: z.string() }), output: z => z.object({ ok: z.boolean() }), @@ -27,4 +27,4 @@ actionsRegistry.register({ }); ``` -Actions without a `permission` field continue to work as before. +Actions without a `visibilityPermission` field continue to work as before. diff --git a/.changeset/actions-permissions-defaults.md b/.changeset/actions-permissions-defaults.md index f5fdf0ade0..66d931fa6d 100644 --- a/.changeset/actions-permissions-defaults.md +++ b/.changeset/actions-permissions-defaults.md @@ -2,4 +2,4 @@ '@backstage/backend-defaults': patch --- -Added permissions integration to the actions registry. Actions registered with a `permission` field are now checked against the permissions framework when listing and invoking. Denied actions are filtered from list results, and invoking a denied action returns a `404 Not Found` as if the action does not exist. Permissions are automatically registered with the `PermissionsRegistryService` so they appear in the permission policy system. +Added permissions integration to the actions registry. Actions registered with a `visibilityPermission` field are now checked against the permissions framework when listing and invoking. Denied actions are filtered from list results, and invoking a denied action returns a `404 Not Found` as if the action does not exist. Permissions are automatically registered with the `PermissionsRegistryService` so they appear in the permission policy system. diff --git a/docs/backend-system/core-services/actions-registry.md b/docs/backend-system/core-services/actions-registry.md index 4f817855c1..23e8c2bff8 100644 --- a/docs/backend-system/core-services/actions-registry.md +++ b/docs/backend-system/core-services/actions-registry.md @@ -25,7 +25,7 @@ Each action registered with the service must conform to the `ActionsRegistryActi ### Optional Properties -- **`permission`:** A `BasicPermission` that controls visibility and access to the action through the permissions framework. See [Permissions](#permissions) below. +- **`visibilityPermission`:** A `BasicPermission` that controls visibility and access to the action through the permissions framework. See [Permissions](#permissions) below. - **`attributes`:** Object containing behavioral flags: - **`destructive`:** Boolean indicating if the action modifies or deletes data - **`idempotent`:** Boolean indicating if running the action multiple times produces the same result @@ -160,7 +160,7 @@ export const myPlugin = createBackendPlugin({ ## Permissions -Actions can optionally declare a `permission` to control visibility and access through the Backstage permissions framework. When a permission is set, the action is only visible in listings and accessible by users who are authorized. +Actions can optionally declare a `visibilityPermission` to control visibility and access through the Backstage permissions framework. When a permission is set, the action is only visible in listings and accessible by users who are authorized. When accessed via the Actions Service or the `/.backstage/actions/v1/...` HTTP endpoints, actions that are denied by the permission policy are filtered from list results and return a `404 Not Found` on invocation, as if they don't exist. @@ -181,7 +181,7 @@ actionsRegistry.register({ name: 'delete-entity', title: 'Delete Entity', description: 'Removes an entity from the catalog', - permission: myDeletePermission, + visibilityPermission: myDeletePermission, schema: { input: z => z.object({ entityRef: z.string() }), output: z => z.object({ deleted: z.boolean() }), @@ -193,7 +193,7 @@ actionsRegistry.register({ }); ``` -Actions without a `permission` field remain visible and accessible by all callers, preserving backwards compatibility. +Actions without a `visibilityPermission` field remain visible and accessible by all callers, preserving backwards compatibility. ## Best Practices diff --git a/docs/backend-system/core-services/actions.md b/docs/backend-system/core-services/actions.md index d1680b2380..fc2eabc0f3 100644 --- a/docs/backend-system/core-services/actions.md +++ b/docs/backend-system/core-services/actions.md @@ -67,7 +67,7 @@ backend: ### Permissions -Actions registered with a `permission` field are automatically checked against the permissions framework. When listing actions, any actions denied by the active permission policy are filtered out of the results. When invoking a denied action, a `404 Not Found` error is returned. See the [Actions Registry Permissions](./actions-registry.md#permissions) documentation for how to configure permissions on actions. +Actions registered with a `visibilityPermission` field are automatically checked against the permissions framework. When listing actions, any actions denied by the active permission policy are filtered out of the results. When invoking a denied action, a `404 Not Found` error is returned. See the [Actions Registry Permissions](./actions-registry.md#permissions) documentation for how to configure permissions on actions. ## Using the Service diff --git a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts index 974576dd81..8cda4c675f 100644 --- a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts +++ b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts @@ -148,9 +148,9 @@ export class DefaultActionsRegistryService implements ActionsRegistryService { throw new NotFoundError(`Action "${req.params.actionId}" not found`); } - if (action.permission) { + if (action.visibilityPermission) { const [decision] = await this.permissions.authorize( - [{ permission: action.permission }], + [{ permission: action.visibilityPermission }], { credentials }, ); if (decision.result === AuthorizeResult.DENY) { @@ -204,8 +204,8 @@ export class DefaultActionsRegistryService implements ActionsRegistryService { throw new Error(`Action with id "${id}" is already registered`); } - if (options.permission) { - this.permissionsRegistry.addPermissions([options.permission]); + if (options.visibilityPermission) { + this.permissionsRegistry.addPermissions([options.visibilityPermission]); } this.actions.set(id, options); @@ -216,7 +216,7 @@ export class DefaultActionsRegistryService implements ActionsRegistryService { credentials: BackstageCredentials, ): Promise { const permissionedEntries = entries.filter( - ([_, action]) => action.permission, + ([_, action]) => action.visibilityPermission, ); if (permissionedEntries.length === 0) { @@ -225,7 +225,7 @@ export class DefaultActionsRegistryService implements ActionsRegistryService { const decisions = await this.permissions.authorize( permissionedEntries.map(([_, action]) => ({ - permission: action.permission!, + permission: action.visibilityPermission!, })), { credentials }, ); diff --git a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts index 21123cdca5..56327031ec 100644 --- a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts +++ b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts @@ -592,7 +592,7 @@ describe('actionsRegistryServiceFactory', () => { name: 'protected-action', title: 'Protected Action', description: 'Permission required', - permission: testPermission, + visibilityPermission: testPermission, schema: { input: z => z.object({}), output: z => z.object({}), @@ -640,7 +640,7 @@ describe('actionsRegistryServiceFactory', () => { name: 'protected-action', title: 'Protected Action', description: 'Permission required', - permission: testPermission, + visibilityPermission: testPermission, schema: { input: z => z.object({}), output: z => z.object({}), @@ -688,7 +688,7 @@ describe('actionsRegistryServiceFactory', () => { name: 'protected-action', title: 'Protected Action', description: 'Permission required', - permission: testPermission, + visibilityPermission: testPermission, schema: { input: z => z.object({}), output: z => z.object({}), @@ -741,7 +741,7 @@ describe('actionsRegistryServiceFactory', () => { name: 'protected-action', title: 'Protected Action', description: 'Permission required', - permission: testPermission, + visibilityPermission: testPermission, schema: { input: z => z.object({}), output: z => z.object({ ok: z.boolean() }), @@ -793,7 +793,7 @@ describe('actionsRegistryServiceFactory', () => { name: 'protected-action', title: 'Protected Action', description: 'Permission required', - permission: testPermission, + visibilityPermission: testPermission, schema: { input: z => z.object({}), output: z => z.object({}), @@ -840,7 +840,7 @@ describe('actionsRegistryServiceFactory', () => { name: 'protected-action', title: 'Protected Action', description: 'Permission required', - permission: testPermission, + visibilityPermission: testPermission, schema: { input: z => z.object({}), output: z => z.object({}), diff --git a/packages/backend-plugin-api/report-alpha.api.md b/packages/backend-plugin-api/report-alpha.api.md index 58acb5338b..729c56b2d5 100644 --- a/packages/backend-plugin-api/report-alpha.api.md +++ b/packages/backend-plugin-api/report-alpha.api.md @@ -32,7 +32,7 @@ export type ActionsRegistryActionOptions< input: (zod: typeof z) => TInputSchema; output: (zod: typeof z) => TOutputSchema; }; - permission?: BasicPermission; + visibilityPermission?: BasicPermission; attributes?: { destructive?: boolean; idempotent?: boolean; diff --git a/packages/backend-plugin-api/src/alpha/ActionsRegistryService.ts b/packages/backend-plugin-api/src/alpha/ActionsRegistryService.ts index cd8a271322..a707e350e9 100644 --- a/packages/backend-plugin-api/src/alpha/ActionsRegistryService.ts +++ b/packages/backend-plugin-api/src/alpha/ActionsRegistryService.ts @@ -43,7 +43,7 @@ export type ActionsRegistryActionOptions< input: (zod: typeof z) => TInputSchema; output: (zod: typeof z) => TOutputSchema; }; - permission?: BasicPermission; + visibilityPermission?: BasicPermission; attributes?: { destructive?: boolean; idempotent?: boolean; diff --git a/packages/backend-plugin-api/src/services/definitions/ActionsRegistryService.ts b/packages/backend-plugin-api/src/services/definitions/ActionsRegistryService.ts index dc81bb3c28..a17c82c6f2 100644 --- a/packages/backend-plugin-api/src/services/definitions/ActionsRegistryService.ts +++ b/packages/backend-plugin-api/src/services/definitions/ActionsRegistryService.ts @@ -41,7 +41,7 @@ export type ActionsRegistryActionOptions< input: (zod: typeof z) => TInputSchema; output: (zod: typeof z) => TOutputSchema; }; - permission?: BasicPermission; + visibilityPermission?: BasicPermission; attributes?: { destructive?: boolean; idempotent?: boolean;