diff --git a/.changeset/few-seas-fail.md b/.changeset/few-seas-fail.md new file mode 100644 index 0000000000..8ace0f3515 --- /dev/null +++ b/.changeset/few-seas-fail.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-permission-common': patch +--- + +Added `PermissionEvaluator`, which will replace the existing `PermissionAuthorizer` interface. This new interface provides stronger type safety and validation by splitting `PermissionAuthorizer.authorize()` into two methods: + +- `authorize()`: Used when the caller requires a definitive decision. +- `query()`: Used when the caller can optimize the evaluation of any conditional decisions. For example, a plugin backend may want to use conditions in a database query instead of evaluating each resource in memory. diff --git a/plugins/permission-common/api-report.md b/plugins/permission-common/api-report.md index ab0c68470d..2f283c0d8d 100644 --- a/plugins/permission-common/api-report.md +++ b/plugins/permission-common/api-report.md @@ -15,6 +15,20 @@ export type AnyOfCriteria = { anyOf: NonEmptyArray>; }; +// @public +export type AuthorizePermissionRequest = + | { + permission: Exclude; + resourceRef?: never; + } + | { + permission: ResourcePermission; + resourceRef: string; + }; + +// @public +export type AuthorizePermissionResponse = DefinitivePolicyDecision; + // @public export type AuthorizeRequestOptions = { token?: string; @@ -78,6 +92,11 @@ export type EvaluatePermissionResponse = PolicyDecision; export type EvaluatePermissionResponseBatch = PermissionMessageBatch; +// @public +export type EvaluatorRequestOptions = { + token?: string; +}; + // @public export type IdentifiedPermissionMessage = T & { id: string; @@ -163,6 +182,18 @@ export type PermissionCriteria = | NotCriteria | TQuery; +// @public +export interface PermissionEvaluator { + authorize( + requests: AuthorizePermissionRequest[], + options?: EvaluatorRequestOptions, + ): Promise; + query( + requests: QueryPermissionRequest[], + options?: EvaluatorRequestOptions, + ): Promise; +} + // @public export type PermissionMessageBatch = { items: IdentifiedPermissionMessage[]; @@ -173,6 +204,15 @@ export type PolicyDecision = | DefinitivePolicyDecision | ConditionalPolicyDecision; +// @public +export type QueryPermissionRequest = { + permission: ResourcePermission; + resourceRef?: never; +}; + +// @public +export type QueryPermissionResponse = PolicyDecision; + // @public export type ResourcePermission = PermissionBase< diff --git a/plugins/permission-common/src/types/api.ts b/plugins/permission-common/src/types/api.ts index 6aaabf978d..1bc9af5ad8 100644 --- a/plugins/permission-common/src/types/api.ts +++ b/plugins/permission-common/src/types/api.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ResourcePermission } from '.'; import { Permission } from './permission'; /** @@ -182,3 +183,72 @@ export type EvaluatePermissionResponse = PolicyDecision; */ export type EvaluatePermissionResponseBatch = PermissionMessageBatch; + +/** + * Request object for {@link PermissionEvaluator.authorize}. If a {@link ResourcePermission} + * is provided, it must include a corresponding `resourceRef`. + * @public + */ +export type AuthorizePermissionRequest = + | { + permission: Exclude; + resourceRef?: never; + } + | { permission: ResourcePermission; resourceRef: string }; + +/** + * Response object for {@link PermissionEvaluator.authorize}. + * @public + */ +export type AuthorizePermissionResponse = DefinitivePolicyDecision; + +/** + * Request object for {@link PermissionEvaluator.query}. + * @public + */ +export type QueryPermissionRequest = { + permission: ResourcePermission; + resourceRef?: never; +}; + +/** + * Response object for {@link PermissionEvaluator.query}. + * @public + */ +export type QueryPermissionResponse = PolicyDecision; + +/** + * A client interacting with the permission backend can implement this evaluator interface. + * + * @public + */ +export interface PermissionEvaluator { + /** + * Evaluates {@link Permission | Permissions} and returns a definitive decision. + */ + authorize( + requests: AuthorizePermissionRequest[], + options?: EvaluatorRequestOptions, + ): Promise; + + /** + * Evaluates {@link ResourcePermission | ResourcePermissions} and returns both definitive and + * conditional decisions, depending on the configured + * {@link @backstage/plugin-permission-node#PermissionPolicy}. This method is useful when the + * caller needs more control over the processing of conditional decisions. For example, a plugin + * backend may want to use {@link PermissionCriteria | conditions} in a database query instead of + * evaluating each resource in memory. + */ + query( + requests: QueryPermissionRequest[], + options?: EvaluatorRequestOptions, + ): Promise; +} + +/** + * Options for {@link PermissionEvaluator} requests. + * @public + */ +export type EvaluatorRequestOptions = { + token?: string; +}; diff --git a/plugins/permission-common/src/types/index.ts b/plugins/permission-common/src/types/index.ts index 1a993a981c..f244a70b1a 100644 --- a/plugins/permission-common/src/types/index.ts +++ b/plugins/permission-common/src/types/index.ts @@ -22,6 +22,12 @@ export type { EvaluatePermissionResponseBatch, IdentifiedPermissionMessage, PermissionMessageBatch, + AuthorizePermissionRequest, + AuthorizePermissionResponse, + QueryPermissionRequest, + QueryPermissionResponse, + EvaluatorRequestOptions, + PermissionEvaluator, ConditionalPolicyDecision, DefinitivePolicyDecision, PolicyDecision,