UNPKG

@backstage/plugin-permission-common

Version:

Isomorphic types and client for Backstage permissions and authorization

444 lines (434 loc) • 14.3 kB
import { JsonPrimitive } from '@backstage/types'; import zodToJsonSchema from 'zod-to-json-schema'; import { Config } from '@backstage/config'; /** * The attributes related to a given permission; these should be generic and widely applicable to * all permissions in the system. * @public */ type PermissionAttributes = { action?: 'create' | 'read' | 'update' | 'delete'; }; /** * Generic type for building {@link Permission} types. * @public */ type PermissionBase<TType extends string, TFields extends object> = { /** * The name of the permission. */ name: string; /** * {@link PermissionAttributes} which describe characteristics of the permission, to help * policy authors make consistent decisions for similar permissions without referring to them * all by name. */ attributes: PermissionAttributes; } & { /** * String value indicating the type of the permission (e.g. 'basic', * 'resource'). The allowed authorization flows in the permission system * depend on the type. For example, a `resourceRef` should only be provided * when authorizing permissions of type 'resource'. */ type: TType; } & TFields; /** * A permission that can be checked through authorization. * * @remarks * * Permissions are the "what" part of authorization, the action to be performed. This may be reading * an entity from the catalog, executing a software template, or any other action a plugin author * may wish to protect. * * To evaluate authorization, a permission is paired with a Backstage identity (the "who") and * evaluated using an authorization policy. * @public */ type Permission = BasicPermission | ResourcePermission; /** * A standard {@link Permission} with no additional capabilities or restrictions. * @public */ type BasicPermission = PermissionBase<'basic', {}>; /** * ResourcePermissions are {@link Permission}s that can be authorized based on * characteristics of a resource such a catalog entity. * @public */ type ResourcePermission<TResourceType extends string = string> = PermissionBase<'resource', { /** * Denotes the type of the resource whose resourceRef should be passed when * authorizing. */ resourceType: TResourceType; }>; /** * Options for authorization requests. * @public */ type AuthorizeRequestOptions = { token?: string; }; /** * A request with a UUID identifier, so that batched responses can be matched up with the original * requests. * @public */ type IdentifiedPermissionMessage<T> = T & { id: string; }; /** * A batch of request or response items. * @public */ type PermissionMessageBatch<T> = { items: IdentifiedPermissionMessage<T>[]; }; /** * The result of an authorization request. * @public */ declare const AuthorizeResult: { /** * The authorization request is denied. */ readonly DENY: "DENY"; /** * The authorization request is allowed. */ readonly ALLOW: "ALLOW"; /** * The authorization request is allowed if the provided conditions are met. */ readonly CONDITIONAL: "CONDITIONAL"; }; /** * @public */ type AuthorizeResult = (typeof AuthorizeResult)[keyof typeof AuthorizeResult]; /** * @public */ declare namespace AuthorizeResult { type ALLOW = typeof AuthorizeResult.ALLOW; type DENY = typeof AuthorizeResult.DENY; type CONDITIONAL = typeof AuthorizeResult.CONDITIONAL; } /** * A definitive decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}. * * @remarks * * This indicates that the policy unconditionally allows (or denies) the request. * * @public */ type DefinitivePolicyDecision = { result: AuthorizeResult.ALLOW | AuthorizeResult.DENY; }; /** * A conditional decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}. * * @remarks * * This indicates that the policy allows authorization for the request, given that the returned * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin * which knows about the referenced permission rules. * * @public */ type ConditionalPolicyDecision = { result: AuthorizeResult.CONDITIONAL; pluginId: string; resourceType: string; conditions: PermissionCriteria<PermissionCondition>; }; /** * A decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}. * * @public */ type PolicyDecision = DefinitivePolicyDecision | ConditionalPolicyDecision; /** * A condition returned with a CONDITIONAL authorization response. * * Conditions are a reference to a rule defined by a plugin, and parameters to apply the rule. For * example, a rule might be `isOwner` from the catalog-backend, and params may be a list of entity * claims from a identity token. * @public */ type PermissionCondition<TResourceType extends string = string, TParams extends PermissionRuleParams = PermissionRuleParams> = { resourceType: TResourceType; rule: string; params?: TParams; }; /** * Utility type to represent an array with 1 or more elements. * @ignore */ type NonEmptyArray<T> = [T, ...T[]]; /** * Represents a logical AND for the provided criteria. * @public */ type AllOfCriteria<TQuery> = { allOf: NonEmptyArray<PermissionCriteria<TQuery>>; }; /** * Represents a logical OR for the provided criteria. * @public */ type AnyOfCriteria<TQuery> = { anyOf: NonEmptyArray<PermissionCriteria<TQuery>>; }; /** * Represents a negation of the provided criteria. * @public */ type NotCriteria<TQuery> = { not: PermissionCriteria<TQuery>; }; /** * Composes several {@link PermissionCondition}s as criteria with a nested AND/OR structure. * @public */ type PermissionCriteria<TQuery> = AllOfCriteria<TQuery> | AnyOfCriteria<TQuery> | NotCriteria<TQuery> | TQuery; /** * A parameter to a permission rule. * * @public */ type PermissionRuleParam = undefined | JsonPrimitive | JsonPrimitive[]; /** * Types that can be used as parameters to permission rules. * * @public */ type PermissionRuleParams = undefined | Record<string, PermissionRuleParam>; /** * An individual request sent to the permission backend. * @public */ type EvaluatePermissionRequest = { permission: Permission; resourceRef?: string; }; /** * A batch of requests sent to the permission backend. * @public * @deprecated This type is not used and it will be removed in the future */ type EvaluatePermissionRequestBatch = PermissionMessageBatch<EvaluatePermissionRequest>; /** * An individual response from the permission backend. * * @remarks * * This response type is an alias of {@link PolicyDecision} to maintain separation between the * {@link @backstage/plugin-permission-node#PermissionPolicy} interface and the permission backend * api. They may diverge at some point in the future. The response * * @public */ type EvaluatePermissionResponse = PolicyDecision; /** * A batch of responses from the permission backend. * @public */ type EvaluatePermissionResponseBatch = PermissionMessageBatch<EvaluatePermissionResponse>; /** * Request object for {@link PermissionEvaluator.authorize}. If a {@link ResourcePermission} * is provided, it must include a corresponding `resourceRef`. * @public */ type AuthorizePermissionRequest = { permission: Exclude<Permission, ResourcePermission>; resourceRef?: never; } | { permission: ResourcePermission; resourceRef: string; }; /** * Response object for {@link PermissionEvaluator.authorize}. * @public */ type AuthorizePermissionResponse = DefinitivePolicyDecision; /** * Request object for {@link PermissionEvaluator.authorizeConditional}. * @public */ type QueryPermissionRequest = { permission: ResourcePermission; resourceRef?: never; }; /** * Response object for {@link PermissionEvaluator.authorizeConditional}. * @public */ type QueryPermissionResponse = PolicyDecision; /** * A client interacting with the permission backend can implement this evaluator interface. * * @public */ interface PermissionEvaluator { /** * Evaluates {@link Permission | Permissions} and returns a definitive decision. */ authorize(requests: AuthorizePermissionRequest[], options?: EvaluatorRequestOptions & { _ignored?: never; }): Promise<AuthorizePermissionResponse[]>; /** * 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. */ authorizeConditional(requests: QueryPermissionRequest[], options?: EvaluatorRequestOptions & { _ignored?: never; }): Promise<QueryPermissionResponse[]>; } /** * Options for {@link PermissionEvaluator} requests. * * This is currently empty, as there are no longer any common options for the permission evaluator. * * @public */ interface EvaluatorRequestOptions { } /** * This is a copy of the core DiscoveryApi, to avoid importing core. * * @public */ type DiscoveryApi = { getBaseUrl(pluginId: string): Promise<string>; }; /** * Serialized permission rules, with the paramsSchema * converted from a ZodSchema to a JsonSchema. * * @public */ type MetadataResponseSerializedRule = { name: string; description: string; resourceType: string; paramsSchema?: ReturnType<typeof zodToJsonSchema>; }; /** * Response type for the .metadata endpoint in * {@link @backstage/plugin-permission-node#createPermissionIntegrationRouter} * * @public */ type MetadataResponse = { permissions?: Permission[]; rules: MetadataResponseSerializedRule[]; }; /** * A client interacting with the permission backend can implement this authorizer interface. * @public * @deprecated Use {@link @backstage/plugin-permission-common#PermissionEvaluator} instead */ interface PermissionAuthorizer { authorize(requests: EvaluatePermissionRequest[], options?: AuthorizeRequestOptions): Promise<EvaluatePermissionResponse[]>; } /** * Check if the two parameters are equivalent permissions. * @public */ declare function isPermission<T extends Permission>(permission: Permission, comparedPermission: T): permission is T; /** * Check if a given permission is a {@link ResourcePermission}. When * `resourceType` is supplied as the second parameter, also checks if * the permission has the specified resource type. * @public */ declare function isResourcePermission<T extends string = string>(permission: Permission, resourceType?: T): permission is ResourcePermission<T>; /** * Check if a given permission is related to a create action. * @public */ declare function isCreatePermission(permission: Permission): boolean; /** * Check if a given permission is related to a read action. * @public */ declare function isReadPermission(permission: Permission): boolean; /** * Check if a given permission is related to an update action. * @public */ declare function isUpdatePermission(permission: Permission): boolean; /** * Check if a given permission is related to a delete action. * @public */ declare function isDeletePermission(permission: Permission): boolean; /** * Convert {@link PermissionAuthorizer} to {@link PermissionEvaluator}. * * @public */ declare function toPermissionEvaluator(permissionAuthorizer: PermissionAuthorizer): PermissionEvaluator; /** * Utility function for creating a valid {@link ResourcePermission}, inferring * the appropriate type and resource type parameter. * * @public */ declare function createPermission<TResourceType extends string>(input: { name: string; attributes: PermissionAttributes; resourceType: TResourceType; }): ResourcePermission<TResourceType>; /** * Utility function for creating a valid {@link BasicPermission}. * * @public */ declare function createPermission(input: { name: string; attributes: PermissionAttributes; }): BasicPermission; /** * Options for {@link PermissionClient} requests. * * @public */ type PermissionClientRequestOptions = { token?: string; }; /** * An isomorphic client for requesting authorization for Backstage permissions. * @public */ declare class PermissionClient implements PermissionEvaluator { private readonly enabled; private readonly discovery; private readonly enableBatchedRequests; constructor(options: { discovery: DiscoveryApi; config: Config; }); /** * {@inheritdoc PermissionEvaluator.authorize} */ authorize(requests: AuthorizePermissionRequest[], options?: PermissionClientRequestOptions): Promise<AuthorizePermissionResponse[]>; /** * {@inheritdoc PermissionEvaluator.authorizeConditional} */ authorizeConditional(queries: QueryPermissionRequest[], options?: PermissionClientRequestOptions): Promise<QueryPermissionResponse[]>; private makeRequest; private makeBatchedRequest; private makeRawRequest; private getAuthorizationHeader; } export { AuthorizeResult, PermissionClient, createPermission, isCreatePermission, isDeletePermission, isPermission, isReadPermission, isResourcePermission, isUpdatePermission, toPermissionEvaluator }; export type { AllOfCriteria, AnyOfCriteria, AuthorizePermissionRequest, AuthorizePermissionResponse, AuthorizeRequestOptions, BasicPermission, ConditionalPolicyDecision, DefinitivePolicyDecision, DiscoveryApi, EvaluatePermissionRequest, EvaluatePermissionRequestBatch, EvaluatePermissionResponse, EvaluatePermissionResponseBatch, EvaluatorRequestOptions, IdentifiedPermissionMessage, MetadataResponse, MetadataResponseSerializedRule, NotCriteria, Permission, PermissionAttributes, PermissionAuthorizer, PermissionBase, PermissionClientRequestOptions, PermissionCondition, PermissionCriteria, PermissionEvaluator, PermissionMessageBatch, PermissionRuleParam, PermissionRuleParams, PolicyDecision, QueryPermissionRequest, QueryPermissionResponse, ResourcePermission };