diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 4ee8953d70..a292f6acd2 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -17,8 +17,15 @@ export type ApplyConditionsRequest = { conditions: PermissionCriteria; }; -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-permission-node" does not have an export "AuthorizeResult" -// +// @public +export type Condition = TRule extends PermissionRule< + any, + any, + infer TParams +> + ? (...params: TParams) => PermissionCondition + : never; + // @public export type ConditionalPolicyResult = { result: AuthorizeResult.CONDITIONAL; @@ -29,9 +36,7 @@ export type ConditionalPolicyResult = { }; }; -// Warning: (ae-missing-release-tag) "conditionFor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const conditionFor: ( rule: PermissionRule, ) => (...params: TParams) => { @@ -39,9 +44,14 @@ export const conditionFor: ( params: TParams; }; -// Warning: (ae-missing-release-tag) "createPermissionIntegration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type Conditions< + TRules extends Record>, +> = { + [Name in keyof TRules]: Condition; +}; + +// @public export const createPermissionIntegration: < TResource, TRules extends { @@ -79,8 +89,6 @@ export const createPermissionIntegration: < // @public export interface PermissionPolicy { - // Warning: (ae-forgotten-export) The symbol "PolicyAuthorizeRequest" needs to be exported by the entry point index.d.ts - // // (undocumented) handle( request: PolicyAuthorizeRequest, @@ -88,8 +96,6 @@ export interface PermissionPolicy { ): Promise; } -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-permission-node" does not have an export "AuthorizeResult" -// // @public export type PermissionRule< TResource, @@ -102,6 +108,9 @@ export type PermissionRule< toQuery(...params: TParams): PermissionCriteria; }; +// @public +export type PolicyAuthorizeRequest = Omit; + // @public export type PolicyResult = | { @@ -109,8 +118,11 @@ export type PolicyResult = } | ConditionalPolicyResult; -// Warnings were encountered during analysis: -// -// src/integration/createPermissionIntegration.d.ts:28:5 - (ae-forgotten-export) The symbol "QueryType" needs to be exported by the entry point index.d.ts -// src/integration/createPermissionIntegration.d.ts:29:5 - (ae-forgotten-export) The symbol "Conditions" needs to be exported by the entry point index.d.ts +// @public +export type QueryType = TRules extends Record< + string, + PermissionRule +> + ? TQuery + : never; ``` diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 4e17d7ecfb..830c945158 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -8,6 +8,7 @@ "publishConfig": { "access": "public", "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", "types": "dist/index.d.ts" }, "homepage": "https://backstage.io", diff --git a/plugins/permission-node/src/integration/conditionFor.ts b/plugins/permission-node/src/integration/conditionFor.ts index 3a13a3afe3..7451b35bca 100644 --- a/plugins/permission-node/src/integration/conditionFor.ts +++ b/plugins/permission-node/src/integration/conditionFor.ts @@ -16,6 +16,19 @@ import { PermissionRule } from '../types'; +/** + * Creates a condition function for a given authorization rule and parameter type. + * + * For example, an isEntityOwner rule for catalog entities might take an array of entityRef strings. + * The rule itself defines _how_ to check a given resource, whereas a condition also includes _what_ + * to verify. + * + * Plugin authors should generally use the {@link createPermissionIntegration} helper, which creates + * conditions for the rules supplied. However, a different plugin can also add rules to this + * integration (using the returned `registerPermissionRule` from this function), and create the + * condition to be used in an {@link PermissionPolicy} using this method directly. + * @public + */ export const conditionFor = (rule: PermissionRule) => (...params: TParams) => ({ diff --git a/plugins/permission-node/src/integration/createPermissionIntegration.ts b/plugins/permission-node/src/integration/createPermissionIntegration.ts index 7a7ae0ea4b..72118572e5 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegration.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegration.ts @@ -56,21 +56,65 @@ export type ApplyConditionsRequest = { conditions: PermissionCriteria; }; -type Condition = TRule extends PermissionRule +/** + * An authorization condition, which is a function to evaluate a given {@link PermissionRule} with + * a specific set of parameters. + * @see createPermissionIntegration + * @public + */ +export type Condition = TRule extends PermissionRule< + any, + any, + infer TParams +> ? (...params: TParams) => PermissionCondition : never; -type Conditions>> = { +/** + * A collection of keyed {@link Condition} functions produced from {@link PermissionRule}. + * @see createPermissionIntegration + * @public + */ +export type Conditions< + TRules extends Record>, +> = { [Name in keyof TRules]: Condition; }; -type QueryType = TRules extends Record< +/** + * The plugin-specific type which authorization conditions are translated into, for plugin use in + * filtering resources. + * @public + */ +export type QueryType = TRules extends Record< string, PermissionRule > ? TQuery : never; +/** + * Create a permission and authorization integration for a plugin that supports _conditional + * authorization_ for resources. + * + * 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` are a map of {@link PermissionRule} 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 `getResource` argument should load a resource by reference. For the catalog, this is an + * {@link @backstage/catalog-model#EntityRef}. For other plugins, this can be any serialized format. + * This is used to construct the `createPermissionIntegrationRouter`, a function to add an + * authorization route to your backend plugin. This route will be called by the `permission-backend` + * when authorization conditions relating to this plugin need to be evaluated. + * @public + */ export const createPermissionIntegration = < TResource, TRules extends { [key: string]: PermissionRule }, diff --git a/plugins/permission-node/src/policy/index.ts b/plugins/permission-node/src/policy/index.ts index 226d50df21..f04c2a094c 100644 --- a/plugins/permission-node/src/policy/index.ts +++ b/plugins/permission-node/src/policy/index.ts @@ -17,5 +17,6 @@ export type { ConditionalPolicyResult, PermissionPolicy, + PolicyAuthorizeRequest, PolicyResult, } from './types'; diff --git a/plugins/permission-node/src/policy/types.ts b/plugins/permission-node/src/policy/types.ts index d594d3bdb7..36186d5e57 100644 --- a/plugins/permission-node/src/policy/types.ts +++ b/plugins/permission-node/src/policy/types.ts @@ -25,9 +25,9 @@ import { BackstageIdentity } from '@backstage/plugin-auth-backend'; /** * An authorization request to be evaluated by the {@link PermissionPolicy}. * - * This differs from {@link AuthorizeRequest} in that `resourceRef` should never be provided. This - * forces policies to be written in a way that's compatible with filtering collections of resources - * at data load time. + * This differs from {@link @backstage/permission-common#AuthorizeRequest} in that `resourceRef` + * should never be provided. This forces policies to be written in a way that's compatible with + * filtering collections of resources at data load time. * @public */ export type PolicyAuthorizeRequest = Omit; @@ -39,8 +39,8 @@ export type PolicyAuthorizeRequest = Omit; * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin * which knows about the referenced permission rules. * - * Similar to {@link AuthorizeResult}, but with the plugin and resource identifiers needed to - * evaluate the returned conditions. + * Similar to {@link @backstage/permission-common#AuthorizeResult}, but with the plugin and resource + * identifiers needed to evaluate the returned conditions. * @public */ export type ConditionalPolicyResult = { diff --git a/plugins/permission-node/src/types.ts b/plugins/permission-node/src/types.ts index f983c80ea5..a4e95fbd61 100644 --- a/plugins/permission-node/src/types.ts +++ b/plugins/permission-node/src/types.ts @@ -17,8 +17,8 @@ import type { PermissionCriteria } from '@backstage/plugin-permission-common'; /** - * A conditional rule that can be provided in an {@link AuthorizeResult} response to an - * authorization request. + * A conditional rule that can be provided in an + * {@link @backstage/permission-common#AuthorizeResult} response to an authorization request. * * Rules can either be evaluated against a resource loaded in memory, or used as filters when * loading a collection of resources from a data source. The `apply` and `toQuery` methods implement