From 9ddfd94528b9d3f9f4d5eb282c3933aa417b1198 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 2 Jan 2025 01:37:15 +0100 Subject: [PATCH] backend-plugin-api: added PermissionsIntegrationsService definition Signed-off-by: Patrik Oldsberg --- .changeset/sharp-vans-protect.md | 5 + packages/backend-plugin-api/package.json | 1 + packages/backend-plugin-api/report.api.md | 29 +++++ .../PermissionIntegrationsService.ts | 116 ++++++++++++++++++ .../src/services/definitions/coreServices.ts | 13 ++ .../src/services/definitions/index.ts | 4 + yarn.lock | 1 + 7 files changed, 169 insertions(+) create mode 100644 .changeset/sharp-vans-protect.md create mode 100644 packages/backend-plugin-api/src/services/definitions/PermissionIntegrationsService.ts diff --git a/.changeset/sharp-vans-protect.md b/.changeset/sharp-vans-protect.md new file mode 100644 index 0000000000..a2f9237a56 --- /dev/null +++ b/.changeset/sharp-vans-protect.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': patch +--- + +Added new `PermissionIntegrationsService` that is used by plugins to register permissions, resource types, and rules into the permission system. This replaces the existing `createPermissionIntegrationRouter` from `@backstage/plugin-permission-node`. diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 7e4fa19c84..8544741dc9 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -58,6 +58,7 @@ "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", "@backstage/types": "workspace:^", "@types/express": "^4.17.6", "@types/luxon": "^3.0.0", diff --git a/packages/backend-plugin-api/report.api.md b/packages/backend-plugin-api/report.api.md index 8416c6c200..2a8300ccec 100644 --- a/packages/backend-plugin-api/report.api.md +++ b/packages/backend-plugin-api/report.api.md @@ -16,8 +16,10 @@ import { isChildPath } from '@backstage/cli-common'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { Knex } from 'knex'; +import { Permission } from '@backstage/plugin-permission-common'; import { PermissionAttributes } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; +import { PermissionRule } from '@backstage/plugin-permission-node'; import { QueryPermissionRequest } from '@backstage/plugin-permission-common'; import { QueryPermissionResponse } from '@backstage/plugin-permission-common'; import { Readable } from 'stream'; @@ -184,6 +186,11 @@ export namespace coreServices { const lifecycle: ServiceRef; const logger: ServiceRef; const permissions: ServiceRef; + const permissionIntegrations: ServiceRef< + PermissionIntegrationsService, + 'plugin', + 'singleton' + >; const pluginMetadata: ServiceRef< PluginMetadataService, 'plugin', @@ -425,6 +432,28 @@ export interface LoggerService { warn(message: string, meta?: Error | JsonObject): void; } +// @public +export interface PermissionIntegrationsService { + addPermissions(permissions: Permission[]): void; + addResourceType( + options: PermissionIntegrationsServiceAddResourceTypeOptions< + TResourceType, + TResource + >, + ): void; +} + +// @public +export type PermissionIntegrationsServiceAddResourceTypeOptions< + TResourceType extends string, + TResource, +> = { + resourceType: TResourceType; + permissions?: Array; + rules: PermissionRule>[]; + getResources?(resourceRefs: string[]): Promise>; +}; + // @public export interface PermissionsService extends PermissionEvaluator { authorize( diff --git a/packages/backend-plugin-api/src/services/definitions/PermissionIntegrationsService.ts b/packages/backend-plugin-api/src/services/definitions/PermissionIntegrationsService.ts new file mode 100644 index 0000000000..fd25a16b1c --- /dev/null +++ b/packages/backend-plugin-api/src/services/definitions/PermissionIntegrationsService.ts @@ -0,0 +1,116 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Permission } from '@backstage/plugin-permission-common'; +import { PermissionRule } from '@backstage/plugin-permission-node'; + +/** + * Prevent use of type parameter from contributing to type inference. + * + * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795 + * @ignore + */ +type NoInfer = T extends infer S ? S : never; + +/** + * Options for adding a resource type to the permission system. + * + * @public + */ +export type PermissionIntegrationsServiceAddResourceTypeOptions< + TResourceType extends string, + TResource, +> = { + /** + * The identifier for the resource type. + */ + resourceType: TResourceType; + + /** + * Permissions that are available for this resource type. + */ + permissions?: Array; + + /** + * Permission rules that are available for this resource type. + */ + rules: PermissionRule>[]; + + /** + * The function used to load associated resources based in the provided + * references. + * + * @remarks + * + * If this function is not provided the permission system will not be able to + * resolve conditional decisions except when requesting resources directly + * form the plugin. + */ + getResources?(resourceRefs: string[]): Promise>; +}; + +/** + * Permission system integration for registering resources and permissions. + * + * See the {@link https://backstage.io/docs/permissions/overview | permissions documentation} + * and the {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | service documentation} + * for more details. + * + * @public + */ +export interface PermissionIntegrationsService { + /** + * Add permissions for this plugin to the permission system. + */ + addPermissions(permissions: Permission[]): void; + + /** + * Add a new resource type that is owned by this plugin to the permission + * system. + * + * @remarks + * + * To make this concrete, we can use the Backstage software catalog as an + * example. The catalog has conditional rules around access to specific + * _entities_ in the catalog. The _type_ of resource is captured here as + * `resourceType`, a string identifier (`catalog-entity` in this example) that + * can be provided with permission definitions. This is merely a _type_ to + * verify that conditions in an authorization policy are constructed + * correctly, not a reference to a specific resource. + * + * The `rules` parameter is an array of + * {@link @backstage/plugin-permission-node#PermissionRule}s that introduce + * conditional filtering logic for resources; for the catalog, these are + * things like `isEntityOwner` or `hasAnnotation`. Rules describe how to + * filter a list of resources, and the `conditions` returned allow these rules + * to be applied with specific parameters (such as 'group:default/team-a', or + * 'backstage.io/edit-url'). + * + * The `getResources` argument should load resources based on a reference + * identifier. For the catalog, this is an + * {@link @backstage/catalog-model#EntityRef}. For other plugins, this can be + * any serialized format. This is used to add a permission integrations API + * via the HTTP router service. This API will be called by the + * `permission-backend` when authorization conditions relating to this plugin + * need to be evaluated. + */ + addResourceType( + options: PermissionIntegrationsServiceAddResourceTypeOptions< + TResourceType, + TResource + >, + ): void; +} diff --git a/packages/backend-plugin-api/src/services/definitions/coreServices.ts b/packages/backend-plugin-api/src/services/definitions/coreServices.ts index f87bff023c..d65b3b85ea 100644 --- a/packages/backend-plugin-api/src/services/definitions/coreServices.ts +++ b/packages/backend-plugin-api/src/services/definitions/coreServices.ts @@ -174,6 +174,19 @@ export namespace coreServices { import('./PermissionsService').PermissionsService >({ id: 'core.permissions' }); + /** + * Permission system integration for registering resources and permissions. + * + * See {@link PermissionIntegrationsService} + * and {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | the service docs} + * for more information. + * + * @public + */ + export const permissionIntegrations = createServiceRef< + import('./PermissionIntegrationsService').PermissionIntegrationsService + >({ id: 'core.permissionIntegrations' }); + /** * Built-in service for accessing metadata about the current plugin. * diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index 1df00bdead..4e82f1e089 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -50,6 +50,10 @@ export type { PermissionsService, PermissionsServiceRequestOptions, } from './PermissionsService'; +export type { + PermissionIntegrationsService, + PermissionIntegrationsServiceAddResourceTypeOptions, +} from './PermissionIntegrationsService'; export type { PluginMetadataService } from './PluginMetadataService'; export type { RootHttpRouterService } from './RootHttpRouterService'; export type { RootLifecycleService } from './RootLifecycleService'; diff --git a/yarn.lock b/yarn.lock index f40ec341af..2ed052002c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3774,6 +3774,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-permission-node": "workspace:^" "@backstage/types": "workspace:^" "@types/express": ^4.17.6 "@types/luxon": ^3.0.0