Merge pull request #28677 from backstage/rugvip/permission-refs

permissions: add resources refs and rule accessor
This commit is contained in:
Patrik Oldsberg
2025-02-04 15:53:34 +01:00
committed by GitHub
32 changed files with 656 additions and 106 deletions
@@ -14,13 +14,12 @@
* limitations under the License.
*/
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { createConditionExports } from '@backstage/plugin-permission-node';
import { permissionRules } from './rules';
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
const { conditions, createConditionalDecision } = createConditionExports({
pluginId: 'catalog',
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
resourceRef: catalogEntityPermissionResourceRef,
rules: permissionRules,
});
@@ -14,16 +14,16 @@
* limitations under the License.
*/
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
import { createPermissionRule } from '@backstage/plugin-permission-node';
import { get } from 'lodash';
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { createCatalogPermissionRule } from './util';
import { z } from 'zod';
export const createPropertyRule = (propertyType: 'metadata' | 'spec') =>
createCatalogPermissionRule({
createPermissionRule({
name: `HAS_${propertyType.toUpperCase()}`,
description: `Allow entities with the specified ${propertyType} subfield`,
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
resourceRef: catalogEntityPermissionResourceRef,
paramsSchema: z.object({
key: z
.string()
@@ -14,9 +14,9 @@
* limitations under the License.
*/
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
import { createPermissionRule } from '@backstage/plugin-permission-node';
import { z } from 'zod';
import { createCatalogPermissionRule } from './util';
/**
* A catalog {@link @backstage/plugin-permission-node#PermissionRule} which
@@ -26,10 +26,10 @@ import { createCatalogPermissionRule } from './util';
*
* @alpha
*/
export const hasAnnotation = createCatalogPermissionRule({
export const hasAnnotation = createPermissionRule({
name: 'HAS_ANNOTATION',
description: 'Allow entities with the specified annotation',
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
resourceRef: catalogEntityPermissionResourceRef,
paramsSchema: z.object({
annotation: z.string().describe('Name of the annotation to match on'),
value: z
@@ -14,19 +14,19 @@
* limitations under the License.
*/
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
import { createPermissionRule } from '@backstage/plugin-permission-node';
import { z } from 'zod';
import { createCatalogPermissionRule } from './util';
/**
* A catalog {@link @backstage/plugin-permission-node#PermissionRule} which
* filters for entities with a specified label in its metadata.
* @alpha
*/
export const hasLabel = createCatalogPermissionRule({
export const hasLabel = createPermissionRule({
name: 'HAS_LABEL',
description: 'Allow entities with the specified label',
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
resourceRef: catalogEntityPermissionResourceRef,
paramsSchema: z.object({
label: z.string().describe('Name of the label to match on'),
}),
@@ -14,19 +14,19 @@
* limitations under the License.
*/
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
import { createPermissionRule } from '@backstage/plugin-permission-node';
import { z } from 'zod';
import { createCatalogPermissionRule } from './util';
/**
* A catalog {@link @backstage/plugin-permission-node#PermissionRule} which
* filters for entities with a specified kind.
* @alpha
*/
export const isEntityKind = createCatalogPermissionRule({
export const isEntityKind = createPermissionRule({
name: 'IS_ENTITY_KIND',
description: 'Allow entities matching a specified kind',
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
resourceRef: catalogEntityPermissionResourceRef,
paramsSchema: z.object({
kinds: z
.array(z.string())
@@ -15,9 +15,9 @@
*/
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { createPermissionRule } from '@backstage/plugin-permission-node';
import { z } from 'zod';
import { createCatalogPermissionRule } from './util';
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
/**
* A catalog {@link @backstage/plugin-permission-node#PermissionRule} which
@@ -25,10 +25,10 @@ import { createCatalogPermissionRule } from './util';
*
* @alpha
*/
export const isEntityOwner = createCatalogPermissionRule({
export const isEntityOwner = createPermissionRule({
name: 'IS_ENTITY_OWNER',
description: 'Allow entities owned by a specified claim',
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
resourceRef: catalogEntityPermissionResourceRef,
paramsSchema: z.object({
claims: z
.array(z.string())
@@ -34,7 +34,7 @@ import {
import { ScmIntegrations } from '@backstage/integration';
import { createHash } from 'crypto';
import { Router } from 'express';
import lodash, { keyBy } from 'lodash';
import lodash from 'lodash';
import {
AuditorService,
@@ -118,6 +118,7 @@ import { DefaultLocationService } from './DefaultLocationService';
import { DefaultRefreshService } from './DefaultRefreshService';
import { basicEntityFilter } from './request';
import { entitiesResponseToObjects } from './response';
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
/**
* This is a duplicate of the alpha `CatalogPermissionRule` type, for use in the stable API.
@@ -558,7 +559,13 @@ export class CatalogBuilder {
const entitiesCatalog = new AuthorizedEntitiesCatalog(
unauthorizedEntitiesCatalog,
permissionsService,
createConditionTransformer(this.permissionRules),
permissionsRegistry
? createConditionTransformer(
permissionsRegistry.getPermissionRuleset(
catalogEntityPermissionResourceRef,
),
)
: createConditionTransformer(this.permissionRules),
);
const catalogPermissionResource = {
@@ -579,9 +586,10 @@ export class CatalogBuilder {
},
});
const entitiesByRef = keyBy(
entitiesResponseToObjects(entities),
stringifyEntityRef,
const entitiesByRef = Object.fromEntries(
entitiesResponseToObjects(entities)
.filter((x): x is Entity => Boolean(x))
.map(entity => [stringifyEntityRef(entity), entity]),
);
return resourceRefs.map(
@@ -597,7 +605,10 @@ export class CatalogBuilder {
| ReturnType<typeof createPermissionIntegrationRouter>
| undefined;
if (permissionsRegistry) {
permissionsRegistry.addResourceType(catalogPermissionResource);
permissionsRegistry.addResourceType({
...catalogPermissionResource,
resourceRef: catalogEntityPermissionResourceRef,
});
} else {
permissionIntegrationRouter = createPermissionIntegrationRouter(
catalogPermissionResource,
+9
View File
@@ -12,6 +12,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-node';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
import { Permission } from '@backstage/plugin-permission-common';
import { PermissionResourceRef } from '@backstage/plugin-permission-node';
import { PermissionRule } from '@backstage/plugin-permission-node';
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
import { PlaceholderResolver } from '@backstage/plugin-catalog-node';
@@ -34,6 +35,14 @@ export interface CatalogAnalysisExtensionPoint {
// @alpha (undocumented)
export const catalogAnalysisExtensionPoint: ExtensionPoint<CatalogAnalysisExtensionPoint>;
// @alpha (undocumented)
export const catalogEntityPermissionResourceRef: PermissionResourceRef<
Entity,
EntitiesSearchFilter,
'catalog-entity',
'catalog'
>;
// @alpha (undocumented)
export interface CatalogLocationsExtensionPoint {
setAllowedLocationTypes(locationTypes: Array<string>): void;
+13
View File
@@ -21,6 +21,19 @@ import {
} from '@backstage/backend-plugin-api';
import { catalogServiceRef as _catalogServiceRef } from './catalogService';
import { CatalogApi, CatalogClient } from '@backstage/catalog-client';
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
import { createPermissionResourceRef } from '@backstage/plugin-permission-node';
import { Entity } from '@backstage/catalog-model';
import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node';
/** @alpha */
export const catalogEntityPermissionResourceRef = createPermissionResourceRef<
Entity,
EntitiesSearchFilter
>().with({
pluginId: 'catalog',
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
});
/**
* @alpha
+102 -11
View File
@@ -78,12 +78,33 @@ export type ConditionTransformer<TQuery> = (
) => PermissionCriteria<TQuery>;
// @public
export const createConditionAuthorizer: <TResource, TQuery>(
export function createConditionAuthorizer<TResource>(
permissionRuleset: PermissionRuleset<TResource>,
): (decision: PolicyDecision, resource: TResource | undefined) => boolean;
// @public @deprecated (undocumented)
export function createConditionAuthorizer<TResource, TQuery>(
rules: PermissionRule<TResource, TQuery, string>[],
) => (decision: PolicyDecision, resource: TResource | undefined) => boolean;
): (decision: PolicyDecision, resource: TResource | undefined) => boolean;
// @public
export const createConditionExports: <
export function createConditionExports<
TResourceType extends string,
TResource,
TRules extends Record<string, PermissionRule<TResource, any, TResourceType>>,
>(options: {
resourceRef: PermissionResourceRef<TResource, any, TResourceType>;
rules: TRules;
}): {
conditions: Conditions<TRules>;
createConditionalDecision: (
permission: ResourcePermission<TResourceType>,
conditions: PermissionCriteria<PermissionCondition<TResourceType>>,
) => ConditionalPolicyDecision;
};
// @public @deprecated (undocumented)
export function createConditionExports<
TResourceType extends string,
TResource,
TRules extends Record<string, PermissionRule<TResource, any, TResourceType>>,
@@ -91,7 +112,7 @@ export const createConditionExports: <
pluginId: string;
resourceType: TResourceType;
rules: TRules;
}) => {
}): {
conditions: Conditions<TRules>;
createConditionalDecision: (
permission: ResourcePermission<TResourceType>,
@@ -108,12 +129,15 @@ export const createConditionFactory: <
) => (params: TParams) => PermissionCondition<TResourceType, TParams>;
// @public
export const createConditionTransformer: <
export function createConditionTransformer<TQuery>(
permissionRuleset: PermissionRuleset<any, TQuery>,
): ConditionTransformer<TQuery>;
// @public @deprecated (undocumented)
export function createConditionTransformer<
TQuery,
TRules extends PermissionRule<any, TQuery, string>[],
>(
permissionRules: [...TRules],
) => ConditionTransformer<TQuery>;
>(permissionRules: [...TRules]): ConditionTransformer<TQuery>;
// @public
export function createPermissionIntegrationRouter<
@@ -149,6 +173,9 @@ export function createPermissionIntegrationRouter<
TResource
>,
): void;
getPermissionRuleset<TResource, TQuery, TResourceType extends string>(
resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>,
): PermissionRuleset<TResource, TQuery, TResourceType>;
};
// @public
@@ -164,15 +191,56 @@ export type CreatePermissionIntegrationRouterResourceOptions<
) => Promise<Array<TResource | undefined>>;
};
// @public (undocumented)
export function createPermissionResourceRef<TResource, TQuery>(): {
with<TPluginId extends string, TResourceType extends string>(options: {
pluginId: TPluginId;
resourceType: TResourceType;
}): PermissionResourceRef<TResource, TQuery, TResourceType, TPluginId>;
};
// @public
export const createPermissionRule: <
export function createPermissionRule<
TResource,
TQuery,
TQueryOutput extends TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = undefined,
>(
rule: CreatePermissionRuleOptions<
TResource,
TQuery,
TQueryOutput,
TResourceType,
TParams
>,
): PermissionRule<TResource, TQuery, TResourceType, TParams>;
// @public @deprecated
export function createPermissionRule<
TResource,
TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = undefined,
>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) => PermissionRule<TResource, TQuery, TResourceType, TParams>;
): PermissionRule<TResource, TQuery, TResourceType, TParams>;
// @public (undocumented)
export type CreatePermissionRuleOptions<
TResource,
TQuery,
TQueryOutput extends TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams,
> = {
name: string;
description: string;
resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>;
paramsSchema?: z.ZodSchema<TParams>;
apply(resource: TResource, params: NoInfer_2<TParams>): boolean;
toQuery(params: NoInfer_2<TParams>): PermissionCriteria<TQueryOutput>;
};
// @public
export const isAndCriteria: <T>(
@@ -189,7 +257,7 @@ export const isOrCriteria: <T>(
criteria: PermissionCriteria<T>,
) => criteria is AnyOfCriteria<T>;
// @public
// @public @deprecated
export const makeCreatePermissionRule: <
TResource,
TQuery,
@@ -253,6 +321,20 @@ export interface PermissionPolicy {
handle(request: PolicyQuery, user?: PolicyQueryUser): Promise<PolicyDecision>;
}
// @public (undocumented)
export type PermissionResourceRef<
TResource = unknown,
TQuery = unknown,
TResourceType extends string = string,
TPluginId extends string = string,
> = {
readonly $$type: '@backstage/PermissionResourceRef';
readonly pluginId: TPluginId;
readonly resourceType: TResourceType;
readonly TQuery: TQuery;
readonly TResource: TResource;
};
// @public
export type PermissionRule<
TResource,
@@ -268,6 +350,15 @@ export type PermissionRule<
toQuery(params: NoInfer_2<TParams>): PermissionCriteria<TQuery>;
};
// @public
export type PermissionRuleset<
TResource = unknown,
TQuery = unknown,
TResourceType extends string = string,
> = {
getRuleByName(name: string): PermissionRule<TResource, TQuery, TResourceType>;
};
// @public
export type PolicyQuery = {
permission: Permission;
@@ -23,6 +23,7 @@ import {
} from '@backstage/plugin-permission-common';
import { PermissionRule } from '../types';
import { createConditionFactory } from './createConditionFactory';
import { PermissionResourceRef } from './createPermissionResourceRef';
/**
* A utility type for mapping a single {@link PermissionRule} to its
@@ -73,7 +74,25 @@ export type Conditions<
*
* @public
*/
export const createConditionExports = <
export function createConditionExports<
TResourceType extends string,
TResource,
TRules extends Record<string, PermissionRule<TResource, any, TResourceType>>,
>(options: {
resourceRef: PermissionResourceRef<TResource, any, TResourceType>;
rules: TRules;
}): {
conditions: Conditions<TRules>;
createConditionalDecision: (
permission: ResourcePermission<TResourceType>,
conditions: PermissionCriteria<PermissionCondition<TResourceType>>,
) => ConditionalPolicyDecision;
};
/**
* @public
* @deprecated Use the version of `createConditionExports` that accepts a `resourceRef` option instead.
*/
export function createConditionExports<
TResourceType extends string,
TResource,
TRules extends Record<string, PermissionRule<TResource, any, TResourceType>>,
@@ -87,8 +106,32 @@ export const createConditionExports = <
permission: ResourcePermission<TResourceType>,
conditions: PermissionCriteria<PermissionCondition<TResourceType>>,
) => ConditionalPolicyDecision;
} => {
const { pluginId, resourceType, rules } = options;
};
export function createConditionExports<
TResourceType extends string,
TResource,
TRules extends Record<string, PermissionRule<TResource, any, TResourceType>>,
>(
options:
| {
resourceRef: PermissionResourceRef<TResource, any, TResourceType>;
rules: TRules;
}
| {
pluginId: string;
resourceType: TResourceType;
rules: TRules;
},
): {
conditions: Conditions<TRules>;
createConditionalDecision: (
permission: ResourcePermission<TResourceType>,
conditions: PermissionCriteria<PermissionCondition<TResourceType>>,
) => ConditionalPolicyDecision;
} {
const { rules } = options;
const { pluginId, resourceType } =
'resourceRef' in options ? options.resourceRef : options;
return {
conditions: Object.entries(rules).reduce(
@@ -108,4 +151,4 @@ export const createConditionExports = <
conditions,
}),
};
};
}
@@ -29,7 +29,7 @@ import { PermissionRule } from '../types';
* 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 createConditionExports} in order to efficiently
* Plugin authors should generally use the {@link (createConditionExports:1)} in order to efficiently
* create multiple condition factories. This helper should generally only be used to construct
* condition factories for third-party rules that aren't part of the backend plugin with which
* they're intended to integrate.
@@ -20,7 +20,7 @@ import {
PermissionCondition,
PermissionCriteria,
} from '@backstage/plugin-permission-common';
import { PermissionRule } from '../types';
import { PermissionRule, PermissionRuleset } from '../types';
import {
createGetRule,
isAndCriteria,
@@ -76,13 +76,26 @@ export type ConditionTransformer<TQuery> = (
*
* @public
*/
export const createConditionTransformer = <
export function createConditionTransformer<TQuery>(
permissionRuleset: PermissionRuleset<any, TQuery>,
): ConditionTransformer<TQuery>;
/**
* @public
* @deprecated Use the version of `createConditionTransformer` that accepts a `PermissionRuleset` instead.
*/
export function createConditionTransformer<
TQuery,
TRules extends PermissionRule<any, TQuery, string>[],
>(
permissionRules: [...TRules],
): ConditionTransformer<TQuery> => {
const getRule = createGetRule(permissionRules);
>(permissionRules: [...TRules]): ConditionTransformer<TQuery>;
export function createConditionTransformer<TQuery>(
permissionRules:
| PermissionRule<any, TQuery, string>[]
| PermissionRuleset<any, TQuery>,
): ConditionTransformer<TQuery> {
const getRule =
'getRuleByName' in permissionRules
? (n: string) => permissionRules.getRuleByName(n)
: createGetRule(permissionRules);
return conditions => mapConditions(conditions, getRule);
};
}
@@ -30,7 +30,7 @@ import {
PermissionCriteria,
PolicyDecision,
} from '@backstage/plugin-permission-common';
import { PermissionRule } from '../types';
import { PermissionRule, PermissionRuleset } from '../types';
import {
NoInfer,
createGetRule,
@@ -39,6 +39,7 @@ import {
isOrCriteria,
} from './util';
import { NotImplementedError } from '@backstage/errors';
import { PermissionResourceRef } from './createPermissionResourceRef';
const permissionCriteriaSchema: z.ZodSchema<
PermissionCriteria<PermissionCondition>
@@ -163,10 +164,25 @@ const applyConditions = <TResourceType extends string, TResource>(
*
* @public
*/
export const createConditionAuthorizer = <TResource, TQuery>(
export function createConditionAuthorizer<TResource>(
permissionRuleset: PermissionRuleset<TResource>,
): (decision: PolicyDecision, resource: TResource | undefined) => boolean;
/**
* @public
* @deprecated Use the version of `createConditionAuthorizer` that accepts a `PermissionRuleset` instead.
*/
export function createConditionAuthorizer<TResource, TQuery>(
rules: PermissionRule<TResource, TQuery, string>[],
) => {
const getRule = createGetRule(rules);
): (decision: PolicyDecision, resource: TResource | undefined) => boolean;
export function createConditionAuthorizer<TResource, TQuery>(
rules:
| PermissionRule<TResource, TQuery, string>[]
| PermissionRuleset<TResource>,
): (decision: PolicyDecision, resource: TResource | undefined) => boolean {
const getRule =
'getRuleByName' in rules
? (n: string) => rules.getRuleByName(n)
: createGetRule(rules);
return (
decision: PolicyDecision,
@@ -178,7 +194,7 @@ export const createConditionAuthorizer = <TResource, TQuery>(
return decision.result === AuthorizeResult.ALLOW;
};
};
}
/**
* Options for creating a permission integration router specific
@@ -426,6 +442,9 @@ export function createPermissionIntegrationRouter<
TResource
>,
): void;
getPermissionRuleset<TResource, TQuery, TResourceType extends string>(
resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>,
): PermissionRuleset<TResource, TQuery, TResourceType>;
} {
const store = new PermissionIntegrationMetadataStore();
@@ -515,5 +534,12 @@ export function createPermissionIntegrationRouter<
) {
store.addResourceType(resource);
},
getPermissionRuleset<TResource, TQuery, TResourceType extends string>(
resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>,
): PermissionRuleset<TResource, TQuery, TResourceType> {
return {
getRuleByName: store.getRuleMapper(resourceRef.resourceType),
} as PermissionRuleset<TResource, TQuery, TResourceType>;
},
});
}
@@ -0,0 +1,56 @@
/*
* Copyright 2025 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.
*/
/**
* @public
*/
export type PermissionResourceRef<
TResource = unknown,
TQuery = unknown,
TResourceType extends string = string,
TPluginId extends string = string,
> = {
readonly $$type: '@backstage/PermissionResourceRef';
readonly pluginId: TPluginId;
readonly resourceType: TResourceType;
readonly TQuery: TQuery;
readonly TResource: TResource;
};
/**
* @public
*/
export function createPermissionResourceRef<TResource, TQuery>(): {
with<TPluginId extends string, TResourceType extends string>(options: {
pluginId: TPluginId;
resourceType: TResourceType;
}): PermissionResourceRef<TResource, TQuery, TResourceType, TPluginId>;
} {
return {
with<TPluginId extends string, TResourceType extends string>(options: {
pluginId: TPluginId;
resourceType: TResourceType;
}): PermissionResourceRef<TResource, TQuery, TResourceType, TPluginId> {
return {
$$type: '@backstage/PermissionResourceRef',
pluginId: options.pluginId,
resourceType: options.resourceType,
TQuery: null as TQuery,
TResource: null as TResource,
};
},
};
}
@@ -14,22 +14,106 @@
* limitations under the License.
*/
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
import {
PermissionCriteria,
PermissionRuleParams,
} from '@backstage/plugin-permission-common';
import { PermissionRule } from '../types';
import { z } from 'zod';
import { PermissionResourceRef } from './createPermissionResourceRef';
import { NoInfer } from './util';
/**
* Helper function to ensure that {@link PermissionRule} definitions are typed correctly.
* @public
*/
export type CreatePermissionRuleOptions<
TResource,
TQuery,
TQueryOutput extends TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams,
> = {
name: string;
description: string;
resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>;
/**
* A ZodSchema that reflects the structure of the parameters that are passed to
*/
paramsSchema?: z.ZodSchema<TParams>;
/**
* 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<TParams>): 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<TParams>): PermissionCriteria<TQueryOutput>;
};
/**
* Helper function to create a {@link PermissionRule} for a specific resource type using a {@link PermissionResourceRef}.
*
* @public
*/
export const createPermissionRule = <
export function createPermissionRule<
TResource,
TQuery,
TQueryOutput extends TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = undefined,
>(
rule: CreatePermissionRuleOptions<
TResource,
TQuery,
TQueryOutput,
TResourceType,
TParams
>,
): PermissionRule<TResource, TQuery, TResourceType, TParams>;
/**
* Helper function to ensure that {@link PermissionRule} definitions are typed correctly.
*
* @deprecated Use the version of `createPermissionRule` that accepts a `resourceRef` option instead.
* @public
*/
export function createPermissionRule<
TResource,
TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = undefined,
>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) => rule;
): PermissionRule<TResource, TQuery, TResourceType, TParams>;
export function createPermissionRule<
TResource,
TQuery,
TQueryOutput extends TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = undefined,
>(
rule:
| PermissionRule<TResource, TQuery, TResourceType, TParams>
| CreatePermissionRuleOptions<
TResource,
TQuery,
TQueryOutput,
TResourceType,
TParams
>,
): PermissionRule<TResource, TQuery, TResourceType, TParams> {
if ('resourceRef' in rule) {
return { ...rule, resourceType: rule.resourceRef.resourceType };
}
return rule;
}
/**
* Helper for making plugin-specific createPermissionRule functions, that have
@@ -38,6 +122,7 @@ export const createPermissionRule = <
* consistent types for the resource and query.
*
* @public
* @deprecated Use {@link (createPermissionRule:1)} directly instead with the resourceRef option.
*/
export const makeCreatePermissionRule =
<TResource, TQuery, TResourceType extends string>() =>
@@ -19,4 +19,8 @@ export * from './createConditionExports';
export * from './createConditionTransformer';
export * from './createPermissionIntegrationRouter';
export * from './createPermissionRule';
export {
createPermissionResourceRef,
type PermissionResourceRef,
} from './createPermissionResourceRef';
export { isAndCriteria, isOrCriteria, isNotCriteria } from './util';
+24
View File
@@ -66,3 +66,27 @@ export type PermissionRule<
*/
toQuery(params: NoInfer<TParams>): PermissionCriteria<TQuery>;
};
/**
* A set of registered rules for a particular resource type.
*
* @remarks
*
* Accessed via {@link @backstage/backend-plugin-api#PermissionsRegistryService.getPermissionRuleset}.
*
* @public
*/
export type PermissionRuleset<
TResource = unknown,
TQuery = unknown,
TResourceType extends string = string,
> = {
/**
* Returns a resource permission rule by name.
*
* @remarks
*
* Will throw an error if a rule with the provided name does not exist.
*/
getRuleByName(name: string): PermissionRule<TResource, TQuery, TResourceType>;
};