diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index f18d396114..ea9b0b2277 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -40,6 +40,7 @@ import { PermissionCondition } from '@backstage/plugin-permission-common'; import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { processingResult } from '@backstage/plugin-catalog-node'; @@ -177,37 +178,52 @@ export const catalogConditions: Conditions<{ Entity, EntitiesSearchFilter, 'catalog-entity', - [annotation: string, value?: string | undefined] + { + annotation: string; + value: string | undefined; + } >; hasLabel: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [label: string] + { + label: string; + } >; hasMetadata: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + key: string; + value: string | undefined; + } >; hasSpec: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + key: string; + value: string | undefined; + } >; isEntityKind: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [kinds: string[]] + { + kinds: string[]; + } >; isEntityOwner: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [claims: string[]] + { + claims: string[]; + } >; }>; @@ -221,8 +237,9 @@ export type CatalogEnvironment = { }; // @alpha -export type CatalogPermissionRule = - PermissionRule; +export type CatalogPermissionRule< + TParams extends PermissionRuleParams = PermissionRuleParams, +> = PermissionRule; // @alpha export const catalogPlugin: (options?: undefined) => BackendFeature; @@ -280,12 +297,14 @@ export class CodeOwnersProcessor implements CatalogProcessor { export const createCatalogConditionalDecision: ( permission: ResourcePermission<'catalog-entity'>, conditions: PermissionCriteria< - PermissionCondition<'catalog-entity', unknown[]> + PermissionCondition<'catalog-entity', PermissionRuleParams> >, ) => ConditionalPolicyDecision; // @alpha -export const createCatalogPermissionRule: ( +export const createCatalogPermissionRule: < + TParams extends PermissionRuleParams = PermissionRuleParams, +>( rule: PermissionRule, ) => PermissionRule; @@ -448,37 +467,52 @@ export const permissionRules: { Entity, EntitiesSearchFilter, 'catalog-entity', - [annotation: string, value?: string | undefined] + { + annotation: string; + value: string | undefined; + } >; hasLabel: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [label: string] + { + label: string; + } >; hasMetadata: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + key: string; + value: string | undefined; + } >; hasSpec: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + key: string; + value: string | undefined; + } >; isEntityKind: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [kinds: string[]] + { + kinds: string[]; + } >; isEntityOwner: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [claims: string[]] + { + claims: string[]; + } >; }; diff --git a/plugins/permission-common/api-report.md b/plugins/permission-common/api-report.md index c0c351d6cd..b51b82c907 100644 --- a/plugins/permission-common/api-report.md +++ b/plugins/permission-common/api-report.md @@ -4,6 +4,7 @@ ```ts import { Config } from '@backstage/config'; +import { JsonPrimitive } from '@backstage/types'; // @public export type AllOfCriteria = { @@ -172,7 +173,7 @@ export class PermissionClient implements PermissionEvaluator { // @public export type PermissionCondition< TResourceType extends string = string, - TParams extends unknown[] = unknown[], + TParams extends PermissionRuleParams = PermissionRuleParams, > = { resourceType: TResourceType; rule: string; @@ -203,6 +204,12 @@ export type PermissionMessageBatch = { items: IdentifiedPermissionMessage[]; }; +// @public +export type PermissionRuleParam = undefined | JsonPrimitive | JsonPrimitive[]; + +// @public +export type PermissionRuleParams = Record; + // @public export type PolicyDecision = | DefinitivePolicyDecision diff --git a/plugins/permission-common/src/types/api.ts b/plugins/permission-common/src/types/api.ts index ca915a65da..77d6206c07 100644 --- a/plugins/permission-common/src/types/api.ts +++ b/plugins/permission-common/src/types/api.ts @@ -151,11 +151,15 @@ export type PermissionCriteria = /** * A parameter to a permission rule. + * + * @public */ export type PermissionRuleParam = undefined | JsonPrimitive | JsonPrimitive[]; /** * Types that can be used as parameters to permission rules. + * + * @public */ export type PermissionRuleParams = Record; diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 055325f5a7..5168eefebc 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -19,6 +19,7 @@ import { Permission } from '@backstage/plugin-permission-common'; import { PermissionCondition } from '@backstage/plugin-permission-common'; import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PolicyDecision } from '@backstage/plugin-permission-common'; import { QueryPermissionRequest } from '@backstage/plugin-permission-common'; @@ -54,7 +55,7 @@ export type Condition = TRule extends PermissionRule< infer TResourceType, infer TParams > - ? (...params: TParams) => PermissionCondition + ? (params: TParams) => PermissionCondition : never; // @public @@ -75,7 +76,7 @@ export const createConditionExports: < TResource, TRules extends Record< string, - PermissionRule + PermissionRule >, >(options: { pluginId: string; @@ -86,7 +87,7 @@ export const createConditionExports: < createConditionalDecision: ( permission: ResourcePermission, conditions: PermissionCriteria< - PermissionCondition + PermissionCondition >, ) => ConditionalPolicyDecision; }; @@ -94,15 +95,15 @@ export const createConditionExports: < // @public export const createConditionFactory: < TResourceType extends string, - TParams extends any[], + TParams extends PermissionRuleParams = PermissionRuleParams, >( rule: PermissionRule, -) => (...params: TParams) => PermissionCondition; +) => (params: TParams) => PermissionCondition; // @public export const createConditionTransformer: < TQuery, - TRules extends PermissionRule[], + TRules extends PermissionRule[], >( permissionRules: [...TRules], ) => ConditionTransformer; @@ -114,7 +115,12 @@ export const createPermissionIntegrationRouter: < >(options: { resourceType: TResourceType; permissions?: Permission[] | undefined; - rules: PermissionRule, unknown[]>[]; + rules: PermissionRule< + TResource, + any, + NoInfer, + PermissionRuleParams + >[]; getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>; }) => express.Router; @@ -123,7 +129,7 @@ export const createPermissionRule: < TResource, TQuery, TResourceType extends string, - TParams extends unknown[], + TParams extends PermissionRuleParams = PermissionRuleParams, >( rule: PermissionRule, ) => PermissionRule; @@ -148,7 +154,7 @@ export const makeCreatePermissionRule: < TResource, TQuery, TResourceType extends string, ->() => ( +>() => ( rule: PermissionRule, ) => PermissionRule; @@ -166,16 +172,28 @@ export type PermissionRule< TResource, TQuery, TResourceType extends string, - TParams extends unknown[] = unknown[], + TParams extends PermissionRuleParams = PermissionRuleParams, > = { name: string; description: string; resourceType: TResourceType; - schema: z.ZodSchema; - apply(resource: TResource, ...params: TParams): boolean; - toQuery(...params: TParams): PermissionCriteria; + schema: PermissionRuleSchema; + apply( + resource: TResource, + params: NoInfer>>, + ): boolean; + toQuery( + params: NoInfer>>, + ): PermissionCriteria; }; +// @public +export type PermissionRuleSchema = z.ZodObject<{ + [P in keyof TParams]-?: TParams[P] extends undefined + ? z.ZodOptionalType> + : z.ZodType; +}>; + // @public export type PolicyQuery = { permission: Permission; diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index a01006c194..4f98597e6c 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -28,8 +28,9 @@ import { PermissionCondition, PermissionCriteria, } from '@backstage/plugin-permission-common'; -import { NoInfer, PermissionRule } from '../types'; +import { PermissionRule } from '../types'; import { + NoInfer, createGetRule, isAndCriteria, isNotCriteria, diff --git a/plugins/permission-node/src/integration/util.ts b/plugins/permission-node/src/integration/util.ts index 68b7fff2b8..e448068e17 100644 --- a/plugins/permission-node/src/integration/util.ts +++ b/plugins/permission-node/src/integration/util.ts @@ -22,6 +22,14 @@ import { } from '@backstage/plugin-permission-common'; import { PermissionRule } from '../types'; +/** + * Prevent use of type parameter from contributing to type inference. + * + * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795 + * @ignore + */ +export type NoInfer = T extends infer S ? S : never; + /** * Utility function used to parse a PermissionCriteria * @param criteria - a PermissionCriteria diff --git a/plugins/permission-node/src/types.ts b/plugins/permission-node/src/types.ts index 34ff52391a..b16c0cac32 100644 --- a/plugins/permission-node/src/types.ts +++ b/plugins/permission-node/src/types.ts @@ -19,14 +19,23 @@ import type { PermissionRuleParams, } from '@backstage/plugin-permission-common'; import { z } from 'zod'; +import { NoInfer } from './integration/util'; /** - * Prevent use of type parameter from contributing to type inference. + * A ZodSchema that reflects the structure of the parameters that are passed to + * into a {@link PermissionRule}. * - * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795 - * @ignore + * @public */ -export type NoInfer = T extends infer S ? S : never; +export type PermissionRuleSchema = z.ZodObject<{ + // Parameters can be optional, however we we want to make sure that the + // parameters are always present in the schema, even if they are undefined. + // We remove the optional flag from the schema, and then add it back in + // with an optional zod type. + [P in keyof TParams]-?: TParams[P] extends undefined + ? z.ZodOptionalType> + : z.ZodType; +}>; /** * A conditional rule that can be provided in an @@ -49,15 +58,6 @@ export type PermissionRule< TQuery, TResourceType extends string, TParams extends PermissionRuleParams = PermissionRuleParams, - TSchema extends z.ZodType = z.ZodObject<{ - // Parameters can be optional, however we we want to make sure that the - // parameters are always present in the schema, even if they are undefined. - // We remove the optional flag from the schema, and then add it back in - // with an optional zod type. - [P in keyof TParams]-?: TParams[P] extends undefined - ? z.ZodOptionalType> - : z.ZodType; - }>, > = { name: string; description: string; @@ -66,19 +66,24 @@ export type PermissionRule< /** * A ZodSchema that documents the parameters that this rule accepts. */ - schema: TSchema; + schema: PermissionRuleSchema; /** * Apply this rule to a resource already loaded from a backing data source. The params are * arguments supplied for the rule; for example, a rule could be `isOwner` with entityRefs as the * params. */ - apply(resource: TResource, params: NoInfer>): boolean; + apply( + resource: TResource, + params: NoInfer>>, + ): boolean; /** * Translate this rule to criteria suitable for use in querying a backing data store. The criteria * can be used for loading a collection of resources efficiently with conditional criteria already * applied. */ - toQuery(params: NoInfer>): PermissionCriteria; + toQuery( + params: NoInfer>>, + ): PermissionCriteria; }; diff --git a/plugins/playlist-backend/api-report.md b/plugins/playlist-backend/api-report.md index c7109efbfb..faae97a734 100644 --- a/plugins/playlist-backend/api-report.md +++ b/plugins/playlist-backend/api-report.md @@ -15,6 +15,7 @@ import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionPolicy } from '@backstage/plugin-permission-node'; import { PermissionRule } from '@backstage/plugin-permission-node'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; @@ -26,7 +27,7 @@ import { ResourcePermission } from '@backstage/plugin-permission-common'; export const createPlaylistConditionalDecision: ( permission: ResourcePermission<'playlist-list'>, conditions: PermissionCriteria< - PermissionCondition<'playlist-list', unknown[]> + PermissionCondition<'playlist-list', PermissionRuleParams> >, ) => ConditionalPolicyDecision; @@ -70,13 +71,15 @@ export const playlistConditions: Conditions<{ PlaylistMetadata, ListPlaylistsFilter, 'playlist-list', - [userOwnershipRefs: string[]] + { + owners: string[]; + } >; isPublic: PermissionRule< PlaylistMetadata, ListPlaylistsFilter, 'playlist-list', - [] + PermissionRuleParams >; }>;