refactor: rename permission to visibilityPermission

Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
benjdlambert
2026-03-12 15:07:32 +01:00
parent 5f739e1312
commit 97c917ea1c
9 changed files with 24 additions and 24 deletions
+3 -3
View File
@@ -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.
+1 -1
View File
@@ -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.
@@ -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
+1 -1
View File
@@ -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
@@ -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<ActionEntry[]> {
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 },
);
@@ -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({}),
@@ -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;
@@ -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;
@@ -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;