@backstage/plugin-permission-node 0.2.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # @backstage/plugin-permission-node
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/plugin-auth-backend@0.8.0
9
+ - @backstage/backend-common@0.10.5
10
+
11
+ ## 0.4.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 0ae4f4cc82: **BREAKING**: `PolicyAuthorizeRequest` type has been renamed to `PolicyAuthorizeQuery`.
16
+ **BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common.
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+ - @backstage/plugin-auth-backend@0.7.0
22
+ - @backstage/plugin-permission-common@0.4.0
23
+ - @backstage/backend-common@0.10.4
24
+ - @backstage/config@0.1.13
25
+
26
+ ## 0.4.0-next.0
27
+
28
+ ### Minor Changes
29
+
30
+ - 0ae4f4cc82: **BREAKING**: `PolicyAuthorizeRequest` type has been renamed to `PolicyAuthorizeQuery`.
31
+ **BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common.
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies
36
+ - @backstage/plugin-auth-backend@0.7.0-next.0
37
+ - @backstage/plugin-permission-common@0.4.0-next.0
38
+ - @backstage/backend-common@0.10.4-next.0
39
+ - @backstage/config@0.1.13-next.0
40
+
41
+ ## 0.3.0
42
+
43
+ ### Minor Changes
44
+
45
+ - 419ca637c0: Optimizations to the integration between the permission backend and plugin-backends using createPermissionIntegrationRouter:
46
+
47
+ - The permission backend already supported batched requests to authorize, but would make calls to plugin backend to apply conditions serially. Now, after applying the policy for each authorization request, the permission backend makes a single batched /apply-conditions request to each plugin backend referenced in policy decisions.
48
+ - The `getResource` method accepted by `createPermissionIntegrationRouter` has been replaced with `getResources`, to allow consumers to make batch requests to upstream data stores. When /apply-conditions is called with a batch of requests, all required resources are requested in a single invocation of `getResources`.
49
+
50
+ Plugin owners consuming `createPermissionIntegrationRouter` should replace the `getResource` method in the options with a `getResources` method, accepting an array of resourceRefs, and returning an array of the corresponding resources.
51
+
52
+ ### Patch Changes
53
+
54
+ - 9db1b86f32: Add helpers for creating PermissionRules with inferred types
55
+ - Updated dependencies
56
+ - @backstage/config@0.1.12
57
+ - @backstage/backend-common@0.10.3
58
+ - @backstage/plugin-auth-backend@0.6.2
59
+ - @backstage/errors@0.2.0
60
+ - @backstage/plugin-permission-common@0.3.1
61
+
3
62
  ## 0.2.3
4
63
 
5
64
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -4,11 +4,15 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var pluginPermissionCommon = require('@backstage/plugin-permission-common');
6
6
  var express = require('express');
7
+ var Router = require('express-promise-router');
7
8
  var zod = require('zod');
9
+ var errors = require('@backstage/errors');
10
+ var backendCommon = require('@backstage/backend-common');
8
11
 
9
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
13
 
11
14
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
15
+ var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
12
16
 
13
17
  const createConditionFactory = (rule) => (...params) => ({
14
18
  rule: rule.name,
@@ -76,11 +80,17 @@ const permissionCriteriaSchema = zod.z.lazy(() => zod.z.union([
76
80
  })
77
81
  ]));
78
82
  const applyConditionsRequestSchema = zod.z.object({
79
- resourceRef: zod.z.string(),
80
- resourceType: zod.z.string(),
81
- conditions: permissionCriteriaSchema
83
+ items: zod.z.array(zod.z.object({
84
+ id: zod.z.string(),
85
+ resourceRef: zod.z.string(),
86
+ resourceType: zod.z.string(),
87
+ conditions: permissionCriteriaSchema
88
+ }))
82
89
  });
83
90
  const applyConditions = (criteria, resource, getRule) => {
91
+ if (resource === void 0) {
92
+ return false;
93
+ }
84
94
  if (isAndCriteria(criteria)) {
85
95
  return criteria.allOf.every((child) => applyConditions(child, resource, getRule));
86
96
  } else if (isOrCriteria(criteria)) {
@@ -91,29 +101,43 @@ const applyConditions = (criteria, resource, getRule) => {
91
101
  return getRule(criteria.rule).apply(resource, ...criteria.params);
92
102
  };
93
103
  const createPermissionIntegrationRouter = (options) => {
94
- const { resourceType, rules, getResource } = options;
95
- const router = express.Router();
104
+ const { resourceType, rules, getResources } = options;
105
+ const router = Router__default["default"]();
96
106
  const getRule = createGetRule(rules);
97
- router.post("/.well-known/backstage/permissions/apply-conditions", express__default["default"].json(), async (req, res) => {
107
+ const assertValidResourceTypes = (requests) => {
108
+ const invalidResourceTypes = requests.filter((request) => request.resourceType !== resourceType).map((request) => request.resourceType);
109
+ if (invalidResourceTypes.length) {
110
+ throw new errors.InputError(`Unexpected resource types: ${invalidResourceTypes.join(", ")}.`);
111
+ }
112
+ };
113
+ router.use(express__default["default"].json());
114
+ router.post("/.well-known/backstage/permissions/apply-conditions", async (req, res) => {
98
115
  const parseResult = applyConditionsRequestSchema.safeParse(req.body);
99
116
  if (!parseResult.success) {
100
- return res.status(400).send(`Invalid request body.`);
101
- }
102
- const { data: body } = parseResult;
103
- if (body.resourceType !== resourceType) {
104
- return res.status(400).send(`Unexpected resource type: ${body.resourceType}.`);
105
- }
106
- const resource = await getResource(body.resourceRef);
107
- if (!resource) {
108
- return res.status(400).send(`Resource for ref ${body.resourceRef} not found.`);
117
+ throw new errors.InputError(parseResult.error.toString());
109
118
  }
119
+ const body = parseResult.data;
120
+ assertValidResourceTypes(body.items);
121
+ const resourceRefs = Array.from(new Set(body.items.map(({ resourceRef }) => resourceRef)));
122
+ const resourceArray = await getResources(resourceRefs);
123
+ const resources = resourceRefs.reduce((acc, resourceRef, index) => {
124
+ acc[resourceRef] = resourceArray[index];
125
+ return acc;
126
+ }, {});
110
127
  return res.status(200).json({
111
- result: applyConditions(body.conditions, resource, getRule) ? pluginPermissionCommon.AuthorizeResult.ALLOW : pluginPermissionCommon.AuthorizeResult.DENY
128
+ items: body.items.map((request) => ({
129
+ id: request.id,
130
+ result: applyConditions(request.conditions, resources[request.resourceRef], getRule) ? pluginPermissionCommon.AuthorizeResult.ALLOW : pluginPermissionCommon.AuthorizeResult.DENY
131
+ }))
112
132
  });
113
133
  });
134
+ router.use(backendCommon.errorHandler());
114
135
  return router;
115
136
  };
116
137
 
138
+ const createPermissionRule = (rule) => rule;
139
+ const makeCreatePermissionRule = () => (rule) => createPermissionRule(rule);
140
+
117
141
  class ServerPermissionClient {
118
142
  static fromConfig(config, options) {
119
143
  var _a;
@@ -134,11 +158,11 @@ class ServerPermissionClient {
134
158
  this.tokenManager = options.tokenManager;
135
159
  this.permissionEnabled = options.permissionEnabled;
136
160
  }
137
- async authorize(requests, options) {
161
+ async authorize(queries, options) {
138
162
  if (!this.permissionEnabled || await this.isValidServerToken(options == null ? void 0 : options.token)) {
139
- return requests.map((_) => ({ result: pluginPermissionCommon.AuthorizeResult.ALLOW }));
163
+ return queries.map((_) => ({ result: pluginPermissionCommon.AuthorizeResult.ALLOW }));
140
164
  }
141
- return this.permissionClient.authorize(requests, options);
165
+ return this.permissionClient.authorize(queries, options);
142
166
  }
143
167
  async isValidServerToken(token) {
144
168
  if (!token) {
@@ -153,4 +177,6 @@ exports.createConditionExports = createConditionExports;
153
177
  exports.createConditionFactory = createConditionFactory;
154
178
  exports.createConditionTransformer = createConditionTransformer;
155
179
  exports.createPermissionIntegrationRouter = createPermissionIntegrationRouter;
180
+ exports.createPermissionRule = createPermissionRule;
181
+ exports.makeCreatePermissionRule = makeCreatePermissionRule;
156
182
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/integration/createConditionFactory.ts","../src/integration/createConditionExports.ts","../src/integration/util.ts","../src/integration/createConditionTransformer.ts","../src/integration/createPermissionIntegrationRouter.ts","../src/ServerPermissionClient.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { PermissionRule } from '../types';\n\n/**\n * Creates a condition factory function for a given authorization rule and parameter types.\n *\n * @remarks\n *\n * For example, an isEntityOwner rule for catalog entities might take an array of entityRef strings.\n * The rule itself defines _how_ to check a given resource, whereas a condition also includes _what_\n * to verify.\n *\n * Plugin authors should generally use the {@link createConditionExports} in order to efficiently\n * create multiple condition factories. This helper should generally only be used to construct\n * condition factories for third-party rules that aren't part of the backend plugin with which\n * they're intended to integrate.\n *\n * @public\n */\nexport const createConditionFactory =\n <TParams extends any[]>(rule: PermissionRule<unknown, unknown, TParams>) =>\n (...params: TParams) => ({\n rule: rule.name,\n params,\n });\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthorizeResult,\n PermissionCondition,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { ConditionalPolicyDecision } from '../policy';\nimport { PermissionRule } from '../types';\nimport { createConditionFactory } from './createConditionFactory';\n\n/**\n * A utility type for mapping a single {@link PermissionRule} to its\n * corresponding {@link @backstage/plugin-permission-common#PermissionCondition}.\n *\n * @public\n */\nexport type Condition<TRule> = TRule extends PermissionRule<\n any,\n any,\n infer TParams\n>\n ? (...params: TParams) => PermissionCondition<TParams>\n : never;\n\n/**\n * A utility type for mapping {@link PermissionRule}s to their corresponding\n * {@link @backstage/plugin-permission-common#PermissionCondition}s.\n *\n * @public\n */\nexport type Conditions<\n TRules extends Record<string, PermissionRule<any, any>>,\n> = {\n [Name in keyof TRules]: Condition<TRules[Name]>;\n};\n\n/**\n * Creates the recommended condition-related exports for a given plugin based on the built-in\n * {@link PermissionRule}s it supports.\n *\n * @remarks\n *\n * The function returns a `conditions` object containing a\n * {@link @backstage/plugin-permission-common#PermissionCondition} factory for each of the\n * supplied {@link PermissionRule}s, along with a `createConditions` function which builds the\n * wrapper object needed to enclose conditions when authoring {@link PermissionPolicy} implementations.\n *\n * Plugin authors should generally call this method with all the built-in {@link PermissionRule}s\n * the plugin supports, and export the resulting `conditions` object and `createConditions`\n * function so that they can be used by {@link PermissionPolicy} authors.\n *\n * @public\n */\nexport const createConditionExports = <\n TResource,\n TRules extends Record<string, PermissionRule<TResource, any>>,\n>(options: {\n pluginId: string;\n resourceType: string;\n rules: TRules;\n}): {\n conditions: Conditions<TRules>;\n createPolicyDecision: (\n conditions: PermissionCriteria<PermissionCondition>,\n ) => ConditionalPolicyDecision;\n} => {\n const { pluginId, resourceType, rules } = options;\n\n return {\n conditions: Object.entries(rules).reduce(\n (acc, [key, rule]) => ({\n ...acc,\n [key]: createConditionFactory(rule),\n }),\n {} as Conditions<TRules>,\n ),\n createPolicyDecision: (\n conditions: PermissionCriteria<PermissionCondition>,\n ) => ({\n result: AuthorizeResult.CONDITIONAL,\n pluginId,\n resourceType,\n conditions,\n }),\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { PermissionCriteria } from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\n\nexport const isAndCriteria = (\n filter: PermissionCriteria<unknown>,\n): filter is { allOf: PermissionCriteria<unknown>[] } =>\n Object.prototype.hasOwnProperty.call(filter, 'allOf');\n\nexport const isOrCriteria = (\n filter: PermissionCriteria<unknown>,\n): filter is { anyOf: PermissionCriteria<unknown>[] } =>\n Object.prototype.hasOwnProperty.call(filter, 'anyOf');\n\nexport const isNotCriteria = (\n filter: PermissionCriteria<unknown>,\n): filter is { not: PermissionCriteria<unknown> } =>\n Object.prototype.hasOwnProperty.call(filter, 'not');\n\nexport const createGetRule = <TResource, TQuery>(\n rules: PermissionRule<TResource, TQuery>[],\n) => {\n const rulesMap = new Map(Object.values(rules).map(rule => [rule.name, rule]));\n\n return (name: string): PermissionRule<TResource, TQuery> => {\n const rule = rulesMap.get(name);\n\n if (!rule) {\n throw new Error(`Unexpected permission rule: ${name}`);\n }\n\n return rule;\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PermissionCondition,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\nimport {\n createGetRule,\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from './util';\n\nconst mapConditions = <TQuery>(\n criteria: PermissionCriteria<PermissionCondition>,\n getRule: (name: string) => PermissionRule<unknown, TQuery>,\n): PermissionCriteria<TQuery> => {\n if (isAndCriteria(criteria)) {\n return {\n allOf: criteria.allOf.map(child => mapConditions(child, getRule)),\n };\n } else if (isOrCriteria(criteria)) {\n return {\n anyOf: criteria.anyOf.map(child => mapConditions(child, getRule)),\n };\n } else if (isNotCriteria(criteria)) {\n return {\n not: mapConditions(criteria.not, getRule),\n };\n }\n\n return getRule(criteria.rule).toQuery(...criteria.params);\n};\n\n/**\n * A function which accepts {@link @backstage/plugin-permission-common#PermissionCondition}s\n * logically grouped in a {@link @backstage/plugin-permission-common#PermissionCriteria}\n * object, and transforms the {@link @backstage/plugin-permission-common#PermissionCondition}s\n * into plugin specific query fragments while retaining the enclosing criteria shape.\n *\n * @public\n */\nexport type ConditionTransformer<TQuery> = (\n conditions: PermissionCriteria<PermissionCondition>,\n) => PermissionCriteria<TQuery>;\n\n/**\n * A higher-order helper function which accepts an array of\n * {@link PermissionRule}s, and returns a {@link ConditionTransformer}\n * which transforms input conditions into equivalent plugin-specific\n * query fragments using the supplied rules.\n *\n * @public\n */\nexport const createConditionTransformer = <\n TQuery,\n TRules extends PermissionRule<any, TQuery>[],\n>(\n permissionRules: [...TRules],\n): ConditionTransformer<TQuery> => {\n const getRule = createGetRule(permissionRules);\n\n return conditions => mapConditions(conditions, getRule);\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express, { Response, Router } from 'express';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionCondition,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\nimport {\n createGetRule,\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from './util';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z.union([\n z.object({ anyOf: z.array(permissionCriteriaSchema) }),\n z.object({ allOf: z.array(permissionCriteriaSchema) }),\n z.object({ not: permissionCriteriaSchema }),\n z.object({\n rule: z.string(),\n params: z.array(z.unknown()),\n }),\n ]),\n);\n\nconst applyConditionsRequestSchema = z.object({\n resourceRef: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n});\n\n/**\n * A request to load the referenced resource and apply conditions in order to\n * finalize a conditional authorization response.\n *\n * @public\n */\nexport type ApplyConditionsRequest = {\n resourceRef: string;\n resourceType: string;\n conditions: PermissionCriteria<PermissionCondition>;\n};\n\n/**\n * The result of applying the conditions, expressed as a definitive authorize\n * result of ALLOW or DENY.\n *\n * @public\n */\nexport type ApplyConditionsResponse = {\n result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;\n};\n\nconst applyConditions = <TResource>(\n criteria: PermissionCriteria<PermissionCondition>,\n resource: TResource,\n getRule: (name: string) => PermissionRule<TResource, unknown>,\n): boolean => {\n if (isAndCriteria(criteria)) {\n return criteria.allOf.every(child =>\n applyConditions(child, resource, getRule),\n );\n } else if (isOrCriteria(criteria)) {\n return criteria.anyOf.some(child =>\n applyConditions(child, resource, getRule),\n );\n } else if (isNotCriteria(criteria)) {\n return !applyConditions(criteria.not, resource, getRule);\n }\n\n return getRule(criteria.rule).apply(resource, ...criteria.params);\n};\n\n/**\n * Create an express Router which provides an authorization route to allow integration between the\n * permission backend and other Backstage backend plugins. Plugin owners that wish to support\n * conditional authorization for their resources should add the router created by this function\n * to their express app inside their `createRouter` implementation.\n *\n * @remarks\n *\n * To make this concrete, we can use the Backstage software catalog as an example. The catalog has\n * conditional rules around access to specific _entities_ in the catalog. The _type_ of resource is\n * captured here as `resourceType`, a string identifier (`catalog-entity` in this example) that can\n * be provided with permission definitions. This is merely a _type_ to verify that conditions in an\n * authorization policy are constructed correctly, not a reference to a specific resource.\n *\n * The `rules` parameter is an array of {@link PermissionRule}s that introduce conditional\n * filtering logic for resources; for the catalog, these are things like `isEntityOwner` or\n * `hasAnnotation`. Rules describe how to filter a list of resources, and the `conditions` returned\n * allow these rules to be applied with specific parameters (such as 'group:default/team-a', or\n * 'backstage.io/edit-url').\n *\n * The `getResource` argument should load a resource by reference. For the catalog, this is an\n * {@link @backstage/catalog-model#EntityRef}. For other plugins, this can be any serialized format.\n * This is used to construct the `createPermissionIntegrationRouter`, a function to add an\n * authorization route to your backend plugin. This route will be called by the `permission-backend`\n * when authorization conditions relating to this plugin need to be evaluated.\n *\n * @public\n */\nexport const createPermissionIntegrationRouter = <TResource>(options: {\n resourceType: string;\n rules: PermissionRule<TResource, any>[];\n getResource: (resourceRef: string) => Promise<TResource | undefined>;\n}): Router => {\n const { resourceType, rules, getResource } = options;\n const router = Router();\n\n const getRule = createGetRule(rules);\n\n router.post(\n '/.well-known/backstage/permissions/apply-conditions',\n express.json(),\n async (\n req,\n res: Response<\n | {\n result: Omit<AuthorizeResult, AuthorizeResult.CONDITIONAL>;\n }\n | string\n >,\n ) => {\n const parseResult = applyConditionsRequestSchema.safeParse(req.body);\n\n if (!parseResult.success) {\n return res.status(400).send(`Invalid request body.`);\n }\n\n const { data: body } = parseResult;\n\n if (body.resourceType !== resourceType) {\n return res\n .status(400)\n .send(`Unexpected resource type: ${body.resourceType}.`);\n }\n\n const resource = await getResource(body.resourceRef);\n\n if (!resource) {\n return res\n .status(400)\n .send(`Resource for ref ${body.resourceRef} not found.`);\n }\n\n return res.status(200).json({\n result: applyConditions(body.conditions, resource, getRule)\n ? AuthorizeResult.ALLOW\n : AuthorizeResult.DENY,\n });\n },\n );\n\n return router;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n TokenManager,\n PluginEndpointDiscovery,\n} from '@backstage/backend-common';\nimport { Config } from '@backstage/config';\nimport {\n AuthorizeRequest,\n AuthorizeRequestOptions,\n AuthorizeResponse,\n AuthorizeResult,\n PermissionClient,\n PermissionAuthorizer,\n} from '@backstage/plugin-permission-common';\n\n/**\n * A thin wrapper around\n * {@link @backstage/plugin-permission-common#PermissionClient} that allows all\n * backend-to-backend requests.\n * @public\n */\nexport class ServerPermissionClient implements PermissionAuthorizer {\n private readonly permissionClient: PermissionClient;\n private readonly tokenManager: TokenManager;\n private readonly permissionEnabled: boolean;\n\n static fromConfig(\n config: Config,\n options: {\n discovery: PluginEndpointDiscovery;\n tokenManager: TokenManager;\n },\n ) {\n const { discovery, tokenManager } = options;\n const permissionClient = new PermissionClient({ discovery, config });\n const permissionEnabled =\n config.getOptionalBoolean('permission.enabled') ?? false;\n\n if (\n permissionEnabled &&\n (tokenManager as any).isInsecureServerTokenManager\n ) {\n throw new Error(\n 'You must configure at least one key in backend.auth.keys if permissions are enabled.',\n );\n }\n\n return new ServerPermissionClient({\n permissionClient,\n tokenManager,\n permissionEnabled,\n });\n }\n\n private constructor(options: {\n permissionClient: PermissionClient;\n tokenManager: TokenManager;\n permissionEnabled: boolean;\n }) {\n this.permissionClient = options.permissionClient;\n this.tokenManager = options.tokenManager;\n this.permissionEnabled = options.permissionEnabled;\n }\n\n async authorize(\n requests: AuthorizeRequest[],\n options?: AuthorizeRequestOptions,\n ): Promise<AuthorizeResponse[]> {\n // Check if permissions are enabled before validating the server token. That\n // way when permissions are disabled, the noop token manager can be used\n // without fouling up the logic inside the ServerPermissionClient, because\n // the code path won't be reached.\n if (\n !this.permissionEnabled ||\n (await this.isValidServerToken(options?.token))\n ) {\n return requests.map(_ => ({ result: AuthorizeResult.ALLOW }));\n }\n return this.permissionClient.authorize(requests, options);\n }\n\n private async isValidServerToken(\n token: string | undefined,\n ): Promise<boolean> {\n if (!token) {\n return false;\n }\n return this.tokenManager\n .authenticate(token)\n .then(() => true)\n .catch(() => false);\n }\n}\n"],"names":["AuthorizeResult","z","Router","express","PermissionClient"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/integration/createConditionFactory.ts","../src/integration/createConditionExports.ts","../src/integration/util.ts","../src/integration/createConditionTransformer.ts","../src/integration/createPermissionIntegrationRouter.ts","../src/integration/createPermissionRule.ts","../src/ServerPermissionClient.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { PermissionRule } from '../types';\n\n/**\n * Creates a condition factory function for a given authorization rule and parameter types.\n *\n * @remarks\n *\n * For example, an isEntityOwner rule for catalog entities might take an array of entityRef strings.\n * The rule itself defines _how_ to check a given resource, whereas a condition also includes _what_\n * to verify.\n *\n * Plugin authors should generally use the {@link createConditionExports} in order to efficiently\n * create multiple condition factories. This helper should generally only be used to construct\n * condition factories for third-party rules that aren't part of the backend plugin with which\n * they're intended to integrate.\n *\n * @public\n */\nexport const createConditionFactory =\n <TParams extends any[]>(rule: PermissionRule<unknown, unknown, TParams>) =>\n (...params: TParams) => ({\n rule: rule.name,\n params,\n });\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthorizeResult,\n PermissionCondition,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { ConditionalPolicyDecision } from '../policy';\nimport { PermissionRule } from '../types';\nimport { createConditionFactory } from './createConditionFactory';\n\n/**\n * A utility type for mapping a single {@link PermissionRule} to its\n * corresponding {@link @backstage/plugin-permission-common#PermissionCondition}.\n *\n * @public\n */\nexport type Condition<TRule> = TRule extends PermissionRule<\n any,\n any,\n infer TParams\n>\n ? (...params: TParams) => PermissionCondition<TParams>\n : never;\n\n/**\n * A utility type for mapping {@link PermissionRule}s to their corresponding\n * {@link @backstage/plugin-permission-common#PermissionCondition}s.\n *\n * @public\n */\nexport type Conditions<\n TRules extends Record<string, PermissionRule<any, any>>,\n> = {\n [Name in keyof TRules]: Condition<TRules[Name]>;\n};\n\n/**\n * Creates the recommended condition-related exports for a given plugin based on the built-in\n * {@link PermissionRule}s it supports.\n *\n * @remarks\n *\n * The function returns a `conditions` object containing a\n * {@link @backstage/plugin-permission-common#PermissionCondition} factory for each of the\n * supplied {@link PermissionRule}s, along with a `createConditions` function which builds the\n * wrapper object needed to enclose conditions when authoring {@link PermissionPolicy} implementations.\n *\n * Plugin authors should generally call this method with all the built-in {@link PermissionRule}s\n * the plugin supports, and export the resulting `conditions` object and `createConditions`\n * function so that they can be used by {@link PermissionPolicy} authors.\n *\n * @public\n */\nexport const createConditionExports = <\n TResource,\n TRules extends Record<string, PermissionRule<TResource, any>>,\n>(options: {\n pluginId: string;\n resourceType: string;\n rules: TRules;\n}): {\n conditions: Conditions<TRules>;\n createPolicyDecision: (\n conditions: PermissionCriteria<PermissionCondition>,\n ) => ConditionalPolicyDecision;\n} => {\n const { pluginId, resourceType, rules } = options;\n\n return {\n conditions: Object.entries(rules).reduce(\n (acc, [key, rule]) => ({\n ...acc,\n [key]: createConditionFactory(rule),\n }),\n {} as Conditions<TRules>,\n ),\n createPolicyDecision: (\n conditions: PermissionCriteria<PermissionCondition>,\n ) => ({\n result: AuthorizeResult.CONDITIONAL,\n pluginId,\n resourceType,\n conditions,\n }),\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { PermissionCriteria } from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\n\nexport const isAndCriteria = (\n filter: PermissionCriteria<unknown>,\n): filter is { allOf: PermissionCriteria<unknown>[] } =>\n Object.prototype.hasOwnProperty.call(filter, 'allOf');\n\nexport const isOrCriteria = (\n filter: PermissionCriteria<unknown>,\n): filter is { anyOf: PermissionCriteria<unknown>[] } =>\n Object.prototype.hasOwnProperty.call(filter, 'anyOf');\n\nexport const isNotCriteria = (\n filter: PermissionCriteria<unknown>,\n): filter is { not: PermissionCriteria<unknown> } =>\n Object.prototype.hasOwnProperty.call(filter, 'not');\n\nexport const createGetRule = <TResource, TQuery>(\n rules: PermissionRule<TResource, TQuery>[],\n) => {\n const rulesMap = new Map(Object.values(rules).map(rule => [rule.name, rule]));\n\n return (name: string): PermissionRule<TResource, TQuery> => {\n const rule = rulesMap.get(name);\n\n if (!rule) {\n throw new Error(`Unexpected permission rule: ${name}`);\n }\n\n return rule;\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PermissionCondition,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\nimport {\n createGetRule,\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from './util';\n\nconst mapConditions = <TQuery>(\n criteria: PermissionCriteria<PermissionCondition>,\n getRule: (name: string) => PermissionRule<unknown, TQuery>,\n): PermissionCriteria<TQuery> => {\n if (isAndCriteria(criteria)) {\n return {\n allOf: criteria.allOf.map(child => mapConditions(child, getRule)),\n };\n } else if (isOrCriteria(criteria)) {\n return {\n anyOf: criteria.anyOf.map(child => mapConditions(child, getRule)),\n };\n } else if (isNotCriteria(criteria)) {\n return {\n not: mapConditions(criteria.not, getRule),\n };\n }\n\n return getRule(criteria.rule).toQuery(...criteria.params);\n};\n\n/**\n * A function which accepts {@link @backstage/plugin-permission-common#PermissionCondition}s\n * logically grouped in a {@link @backstage/plugin-permission-common#PermissionCriteria}\n * object, and transforms the {@link @backstage/plugin-permission-common#PermissionCondition}s\n * into plugin specific query fragments while retaining the enclosing criteria shape.\n *\n * @public\n */\nexport type ConditionTransformer<TQuery> = (\n conditions: PermissionCriteria<PermissionCondition>,\n) => PermissionCriteria<TQuery>;\n\n/**\n * A higher-order helper function which accepts an array of\n * {@link PermissionRule}s, and returns a {@link ConditionTransformer}\n * which transforms input conditions into equivalent plugin-specific\n * query fragments using the supplied rules.\n *\n * @public\n */\nexport const createConditionTransformer = <\n TQuery,\n TRules extends PermissionRule<any, TQuery>[],\n>(\n permissionRules: [...TRules],\n): ConditionTransformer<TQuery> => {\n const getRule = createGetRule(permissionRules);\n\n return conditions => mapConditions(conditions, getRule);\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express, { Response } from 'express';\nimport Router from 'express-promise-router';\nimport { z } from 'zod';\nimport { InputError } from '@backstage/errors';\nimport { errorHandler } from '@backstage/backend-common';\nimport {\n AuthorizeResult,\n Identified,\n PermissionCondition,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\nimport {\n createGetRule,\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from './util';\nimport { DefinitivePolicyDecision } from '../policy/types';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z.union([\n z.object({ anyOf: z.array(permissionCriteriaSchema) }),\n z.object({ allOf: z.array(permissionCriteriaSchema) }),\n z.object({ not: permissionCriteriaSchema }),\n z.object({\n rule: z.string(),\n params: z.array(z.unknown()),\n }),\n ]),\n);\n\nconst applyConditionsRequestSchema = z.object({\n items: z.array(\n z.object({\n id: z.string(),\n resourceRef: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ),\n});\n\n/**\n * A request to load the referenced resource and apply conditions in order to\n * finalize a conditional authorization response.\n *\n * @public\n */\nexport type ApplyConditionsRequestEntry = Identified<{\n resourceRef: string;\n resourceType: string;\n conditions: PermissionCriteria<PermissionCondition>;\n}>;\n\n/**\n * A batch of {@link ApplyConditionsRequestEntry} objects.\n *\n * @public\n */\nexport type ApplyConditionsRequest = {\n items: ApplyConditionsRequestEntry[];\n};\n\n/**\n * The result of applying the conditions, expressed as a definitive authorize\n * result of ALLOW or DENY.\n *\n * @public\n */\nexport type ApplyConditionsResponseEntry = Identified<DefinitivePolicyDecision>;\n\n/**\n * A batch of {@link ApplyConditionsResponseEntry} objects.\n *\n * @public\n */\nexport type ApplyConditionsResponse = {\n items: ApplyConditionsResponseEntry[];\n};\n\nconst applyConditions = <TResource>(\n criteria: PermissionCriteria<PermissionCondition>,\n resource: TResource | undefined,\n getRule: (name: string) => PermissionRule<TResource, unknown>,\n): boolean => {\n // If resource was not found, deny. This avoids leaking information from the\n // apply-conditions API which would allow a user to differentiate between\n // non-existent resources and resources to which they do not have access.\n if (resource === undefined) {\n return false;\n }\n\n if (isAndCriteria(criteria)) {\n return criteria.allOf.every(child =>\n applyConditions(child, resource, getRule),\n );\n } else if (isOrCriteria(criteria)) {\n return criteria.anyOf.some(child =>\n applyConditions(child, resource, getRule),\n );\n } else if (isNotCriteria(criteria)) {\n return !applyConditions(criteria.not, resource, getRule);\n }\n\n return getRule(criteria.rule).apply(resource, ...criteria.params);\n};\n\n/**\n * Create an express Router which provides an authorization route to allow\n * integration between the permission backend and other Backstage backend\n * plugins. Plugin owners that wish to support conditional authorization for\n * their resources should add the router created by this function to their\n * express app inside their `createRouter` implementation.\n *\n * @remarks\n *\n * To make this concrete, we can use the Backstage software catalog as an\n * example. The catalog has conditional rules around access to specific\n * _entities_ in the catalog. The _type_ of resource is captured here as\n * `resourceType`, a string identifier (`catalog-entity` in this example) that\n * can be provided with permission definitions. This is merely a _type_ to\n * verify that conditions in an authorization policy are constructed correctly,\n * not a reference to a specific resource.\n *\n * The `rules` parameter is an array of {@link PermissionRule}s that introduce\n * conditional filtering logic for resources; for the catalog, these are things\n * like `isEntityOwner` or `hasAnnotation`. Rules describe how to filter a list\n * of resources, and the `conditions` returned allow these rules to be applied\n * with specific parameters (such as 'group:default/team-a', or\n * 'backstage.io/edit-url').\n *\n * The `getResources` argument should load resources based on a reference\n * identifier. For the catalog, this is an\n * {@link @backstage/catalog-model#EntityRef}. For other plugins, this can be\n * any serialized format. This is used to construct the\n * `createPermissionIntegrationRouter`, a function to add an authorization route\n * to your backend plugin. This function will be called by the\n * `permission-backend` when authorization conditions relating to this plugin\n * need to be evaluated.\n *\n * @public\n */\nexport const createPermissionIntegrationRouter = <TResource>(options: {\n resourceType: string;\n rules: PermissionRule<TResource, any>[];\n getResources: (\n resourceRefs: string[],\n ) => Promise<Array<TResource | undefined>>;\n}): express.Router => {\n const { resourceType, rules, getResources } = options;\n const router = Router();\n\n const getRule = createGetRule(rules);\n\n const assertValidResourceTypes = (\n requests: ApplyConditionsRequestEntry[],\n ) => {\n const invalidResourceTypes = requests\n .filter(request => request.resourceType !== resourceType)\n .map(request => request.resourceType);\n\n if (invalidResourceTypes.length) {\n throw new InputError(\n `Unexpected resource types: ${invalidResourceTypes.join(', ')}.`,\n );\n }\n };\n\n router.use(express.json());\n\n router.post(\n '/.well-known/backstage/permissions/apply-conditions',\n async (req, res: Response<ApplyConditionsResponse | string>) => {\n const parseResult = applyConditionsRequestSchema.safeParse(req.body);\n\n if (!parseResult.success) {\n throw new InputError(parseResult.error.toString());\n }\n\n const body = parseResult.data;\n\n assertValidResourceTypes(body.items);\n\n const resourceRefs = Array.from(\n new Set(body.items.map(({ resourceRef }) => resourceRef)),\n );\n const resourceArray = await getResources(resourceRefs);\n const resources = resourceRefs.reduce((acc, resourceRef, index) => {\n acc[resourceRef] = resourceArray[index];\n\n return acc;\n }, {} as Record<string, TResource | undefined>);\n\n return res.status(200).json({\n items: body.items.map(request => ({\n id: request.id,\n result: applyConditions(\n request.conditions,\n resources[request.resourceRef],\n getRule,\n )\n ? AuthorizeResult.ALLOW\n : AuthorizeResult.DENY,\n })),\n });\n },\n );\n\n router.use(errorHandler());\n\n return router;\n};\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { PermissionRule } from '../types';\n\n/**\n * Helper function to ensure that {@link PermissionRule} definitions are typed correctly.\n *\n * @public\n */\nexport const createPermissionRule = <\n TResource,\n TQuery,\n TParams extends unknown[],\n>(\n rule: PermissionRule<TResource, TQuery, TParams>,\n) => rule;\n\n/**\n * Helper for making plugin-specific createPermissionRule functions, that have\n * the TResource and TQuery type parameters populated but infer the params from\n * the supplied rule. This helps ensure that rules created for this plugin use\n * consistent types for the resource and query.\n *\n * @public\n */\nexport const makeCreatePermissionRule =\n <TResource, TQuery>() =>\n <TParams extends unknown[]>(\n rule: PermissionRule<TResource, TQuery, TParams>,\n ) =>\n createPermissionRule(rule);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n TokenManager,\n PluginEndpointDiscovery,\n} from '@backstage/backend-common';\nimport { Config } from '@backstage/config';\nimport {\n AuthorizeQuery,\n AuthorizeRequestOptions,\n AuthorizeDecision,\n AuthorizeResult,\n PermissionClient,\n PermissionAuthorizer,\n} from '@backstage/plugin-permission-common';\n\n/**\n * A thin wrapper around\n * {@link @backstage/plugin-permission-common#PermissionClient} that allows all\n * backend-to-backend requests.\n * @public\n */\nexport class ServerPermissionClient implements PermissionAuthorizer {\n private readonly permissionClient: PermissionClient;\n private readonly tokenManager: TokenManager;\n private readonly permissionEnabled: boolean;\n\n static fromConfig(\n config: Config,\n options: {\n discovery: PluginEndpointDiscovery;\n tokenManager: TokenManager;\n },\n ) {\n const { discovery, tokenManager } = options;\n const permissionClient = new PermissionClient({ discovery, config });\n const permissionEnabled =\n config.getOptionalBoolean('permission.enabled') ?? false;\n\n if (\n permissionEnabled &&\n (tokenManager as any).isInsecureServerTokenManager\n ) {\n throw new Error(\n 'You must configure at least one key in backend.auth.keys if permissions are enabled.',\n );\n }\n\n return new ServerPermissionClient({\n permissionClient,\n tokenManager,\n permissionEnabled,\n });\n }\n\n private constructor(options: {\n permissionClient: PermissionClient;\n tokenManager: TokenManager;\n permissionEnabled: boolean;\n }) {\n this.permissionClient = options.permissionClient;\n this.tokenManager = options.tokenManager;\n this.permissionEnabled = options.permissionEnabled;\n }\n\n async authorize(\n queries: AuthorizeQuery[],\n options?: AuthorizeRequestOptions,\n ): Promise<AuthorizeDecision[]> {\n // Check if permissions are enabled before validating the server token. That\n // way when permissions are disabled, the noop token manager can be used\n // without fouling up the logic inside the ServerPermissionClient, because\n // the code path won't be reached.\n if (\n !this.permissionEnabled ||\n (await this.isValidServerToken(options?.token))\n ) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW }));\n }\n return this.permissionClient.authorize(queries, options);\n }\n\n private async isValidServerToken(\n token: string | undefined,\n ): Promise<boolean> {\n if (!token) {\n return false;\n }\n return this.tokenManager\n .authenticate(token)\n .then(() => true)\n .catch(() => false);\n }\n}\n"],"names":["AuthorizeResult","z","Router","InputError","express","errorHandler","PermissionClient"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { PermissionCriteria, AuthorizeRequest, AuthorizeResult, PermissionCondition, PermissionAuthorizer, AuthorizeRequestOptions, AuthorizeResponse } from '@backstage/plugin-permission-common';
1
+ import { PermissionCriteria, AuthorizeQuery, AuthorizeResult, PermissionCondition, Identified, PermissionAuthorizer, AuthorizeRequestOptions, AuthorizeDecision } from '@backstage/plugin-permission-common';
2
2
  import { BackstageIdentityResponse } from '@backstage/plugin-auth-backend';
3
- import { Router } from 'express';
3
+ import express from 'express';
4
4
  import { PluginEndpointDiscovery, TokenManager } from '@backstage/backend-common';
5
5
  import { Config } from '@backstage/config';
6
6
 
7
7
  /**
8
8
  * A conditional rule that can be provided in an
9
- * {@link @backstage/permission-common#AuthorizeResult} response to an authorization request.
9
+ * {@link @backstage/permission-common#AuthorizeDecision} response to an authorization request.
10
10
  *
11
11
  * @remarks
12
12
  *
@@ -63,13 +63,25 @@ declare const createConditionFactory: <TParams extends any[]>(rule: PermissionRu
63
63
  *
64
64
  * @remarks
65
65
  *
66
- * This differs from {@link @backstage/permission-common#AuthorizeRequest} in that `resourceRef`
66
+ * This differs from {@link @backstage/permission-common#AuthorizeQuery} in that `resourceRef`
67
67
  * should never be provided. This forces policies to be written in a way that's compatible with
68
68
  * filtering collections of resources at data load time.
69
69
  *
70
70
  * @public
71
71
  */
72
- declare type PolicyAuthorizeRequest = Omit<AuthorizeRequest, 'resourceRef'>;
72
+ declare type PolicyAuthorizeQuery = Omit<AuthorizeQuery, 'resourceRef'>;
73
+ /**
74
+ * A definitive result to an authorization request, returned by the {@link PermissionPolicy}.
75
+ *
76
+ * @remarks
77
+ *
78
+ * This indicates that the policy unconditionally allows (or denies) the request.
79
+ *
80
+ * @public
81
+ */
82
+ declare type DefinitivePolicyDecision = {
83
+ result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;
84
+ };
73
85
  /**
74
86
  * A conditional result to an authorization request, returned by the {@link PermissionPolicy}.
75
87
  *
@@ -79,7 +91,7 @@ declare type PolicyAuthorizeRequest = Omit<AuthorizeRequest, 'resourceRef'>;
79
91
  * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin
80
92
  * which knows about the referenced permission rules.
81
93
  *
82
- * Similar to {@link @backstage/permission-common#AuthorizeResult}, but with the plugin and resource
94
+ * Similar to {@link @backstage/permission-common#AuthorizeDecision}, but with the plugin and resource
83
95
  * identifiers needed to evaluate the returned conditions.
84
96
  * @public
85
97
  */
@@ -94,9 +106,7 @@ declare type ConditionalPolicyDecision = {
94
106
  *
95
107
  * @public
96
108
  */
97
- declare type PolicyDecision = {
98
- result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;
99
- } | ConditionalPolicyDecision;
109
+ declare type PolicyDecision = DefinitivePolicyDecision | ConditionalPolicyDecision;
100
110
  /**
101
111
  * A policy to evaluate authorization requests for any permissioned action performed in Backstage.
102
112
  *
@@ -114,7 +124,7 @@ declare type PolicyDecision = {
114
124
  * @public
115
125
  */
116
126
  interface PermissionPolicy {
117
- handle(request: PolicyAuthorizeRequest, user?: BackstageIdentityResponse): Promise<PolicyDecision>;
127
+ handle(request: PolicyAuthorizeQuery, user?: BackstageIdentityResponse): Promise<PolicyDecision>;
118
128
  }
119
129
 
120
130
  /**
@@ -184,10 +194,18 @@ declare const createConditionTransformer: <TQuery, TRules extends PermissionRule
184
194
  *
185
195
  * @public
186
196
  */
187
- declare type ApplyConditionsRequest = {
197
+ declare type ApplyConditionsRequestEntry = Identified<{
188
198
  resourceRef: string;
189
199
  resourceType: string;
190
200
  conditions: PermissionCriteria<PermissionCondition>;
201
+ }>;
202
+ /**
203
+ * A batch of {@link ApplyConditionsRequestEntry} objects.
204
+ *
205
+ * @public
206
+ */
207
+ declare type ApplyConditionsRequest = {
208
+ items: ApplyConditionsRequestEntry[];
191
209
  };
192
210
  /**
193
211
  * The result of applying the conditions, expressed as a definitive authorize
@@ -195,42 +213,71 @@ declare type ApplyConditionsRequest = {
195
213
  *
196
214
  * @public
197
215
  */
216
+ declare type ApplyConditionsResponseEntry = Identified<DefinitivePolicyDecision>;
217
+ /**
218
+ * A batch of {@link ApplyConditionsResponseEntry} objects.
219
+ *
220
+ * @public
221
+ */
198
222
  declare type ApplyConditionsResponse = {
199
- result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;
223
+ items: ApplyConditionsResponseEntry[];
200
224
  };
201
225
  /**
202
- * Create an express Router which provides an authorization route to allow integration between the
203
- * permission backend and other Backstage backend plugins. Plugin owners that wish to support
204
- * conditional authorization for their resources should add the router created by this function
205
- * to their express app inside their `createRouter` implementation.
226
+ * Create an express Router which provides an authorization route to allow
227
+ * integration between the permission backend and other Backstage backend
228
+ * plugins. Plugin owners that wish to support conditional authorization for
229
+ * their resources should add the router created by this function to their
230
+ * express app inside their `createRouter` implementation.
206
231
  *
207
232
  * @remarks
208
233
  *
209
- * To make this concrete, we can use the Backstage software catalog as an example. The catalog has
210
- * conditional rules around access to specific _entities_ in the catalog. The _type_ of resource is
211
- * captured here as `resourceType`, a string identifier (`catalog-entity` in this example) that can
212
- * be provided with permission definitions. This is merely a _type_ to verify that conditions in an
213
- * authorization policy are constructed correctly, not a reference to a specific resource.
214
- *
215
- * The `rules` parameter is an array of {@link PermissionRule}s that introduce conditional
216
- * filtering logic for resources; for the catalog, these are things like `isEntityOwner` or
217
- * `hasAnnotation`. Rules describe how to filter a list of resources, and the `conditions` returned
218
- * allow these rules to be applied with specific parameters (such as 'group:default/team-a', or
234
+ * To make this concrete, we can use the Backstage software catalog as an
235
+ * example. The catalog has conditional rules around access to specific
236
+ * _entities_ in the catalog. The _type_ of resource is captured here as
237
+ * `resourceType`, a string identifier (`catalog-entity` in this example) that
238
+ * can be provided with permission definitions. This is merely a _type_ to
239
+ * verify that conditions in an authorization policy are constructed correctly,
240
+ * not a reference to a specific resource.
241
+ *
242
+ * The `rules` parameter is an array of {@link PermissionRule}s that introduce
243
+ * conditional filtering logic for resources; for the catalog, these are things
244
+ * like `isEntityOwner` or `hasAnnotation`. Rules describe how to filter a list
245
+ * of resources, and the `conditions` returned allow these rules to be applied
246
+ * with specific parameters (such as 'group:default/team-a', or
219
247
  * 'backstage.io/edit-url').
220
248
  *
221
- * The `getResource` argument should load a resource by reference. For the catalog, this is an
222
- * {@link @backstage/catalog-model#EntityRef}. For other plugins, this can be any serialized format.
223
- * This is used to construct the `createPermissionIntegrationRouter`, a function to add an
224
- * authorization route to your backend plugin. This route will be called by the `permission-backend`
225
- * when authorization conditions relating to this plugin need to be evaluated.
249
+ * The `getResources` argument should load resources based on a reference
250
+ * identifier. For the catalog, this is an
251
+ * {@link @backstage/catalog-model#EntityRef}. For other plugins, this can be
252
+ * any serialized format. This is used to construct the
253
+ * `createPermissionIntegrationRouter`, a function to add an authorization route
254
+ * to your backend plugin. This function will be called by the
255
+ * `permission-backend` when authorization conditions relating to this plugin
256
+ * need to be evaluated.
226
257
  *
227
258
  * @public
228
259
  */
229
260
  declare const createPermissionIntegrationRouter: <TResource>(options: {
230
261
  resourceType: string;
231
262
  rules: PermissionRule<TResource, any, unknown[]>[];
232
- getResource: (resourceRef: string) => Promise<TResource | undefined>;
233
- }) => Router;
263
+ getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>;
264
+ }) => express.Router;
265
+
266
+ /**
267
+ * Helper function to ensure that {@link PermissionRule} definitions are typed correctly.
268
+ *
269
+ * @public
270
+ */
271
+ declare const createPermissionRule: <TResource, TQuery, TParams extends unknown[]>(rule: PermissionRule<TResource, TQuery, TParams>) => PermissionRule<TResource, TQuery, TParams>;
272
+ /**
273
+ * Helper for making plugin-specific createPermissionRule functions, that have
274
+ * the TResource and TQuery type parameters populated but infer the params from
275
+ * the supplied rule. This helps ensure that rules created for this plugin use
276
+ * consistent types for the resource and query.
277
+ *
278
+ * @public
279
+ */
280
+ declare const makeCreatePermissionRule: <TResource, TQuery>() => <TParams extends unknown[]>(rule: PermissionRule<TResource, TQuery, TParams>) => PermissionRule<TResource, TQuery, TParams>;
234
281
 
235
282
  /**
236
283
  * A thin wrapper around
@@ -247,8 +294,8 @@ declare class ServerPermissionClient implements PermissionAuthorizer {
247
294
  tokenManager: TokenManager;
248
295
  }): ServerPermissionClient;
249
296
  private constructor();
250
- authorize(requests: AuthorizeRequest[], options?: AuthorizeRequestOptions): Promise<AuthorizeResponse[]>;
297
+ authorize(queries: AuthorizeQuery[], options?: AuthorizeRequestOptions): Promise<AuthorizeDecision[]>;
251
298
  private isValidServerToken;
252
299
  }
253
300
 
254
- export { ApplyConditionsRequest, ApplyConditionsResponse, Condition, ConditionTransformer, ConditionalPolicyDecision, Conditions, PermissionPolicy, PermissionRule, PolicyAuthorizeRequest, PolicyDecision, ServerPermissionClient, createConditionExports, createConditionFactory, createConditionTransformer, createPermissionIntegrationRouter };
301
+ export { ApplyConditionsRequest, ApplyConditionsRequestEntry, ApplyConditionsResponse, ApplyConditionsResponseEntry, Condition, ConditionTransformer, ConditionalPolicyDecision, Conditions, DefinitivePolicyDecision, PermissionPolicy, PermissionRule, PolicyAuthorizeQuery, PolicyDecision, ServerPermissionClient, createConditionExports, createConditionFactory, createConditionTransformer, createPermissionIntegrationRouter, createPermissionRule, makeCreatePermissionRule };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-permission-node",
3
3
  "description": "Common permission and authorization utilities for backend plugins",
4
- "version": "0.2.3",
4
+ "version": "0.4.1",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -29,16 +29,18 @@
29
29
  "clean": "backstage-cli clean"
30
30
  },
31
31
  "dependencies": {
32
- "@backstage/backend-common": "^0.10.1",
33
- "@backstage/config": "^0.1.11",
34
- "@backstage/plugin-auth-backend": "^0.6.0",
35
- "@backstage/plugin-permission-common": "^0.3.0",
32
+ "@backstage/backend-common": "^0.10.5",
33
+ "@backstage/config": "^0.1.13",
34
+ "@backstage/errors": "^0.2.0",
35
+ "@backstage/plugin-auth-backend": "^0.8.0",
36
+ "@backstage/plugin-permission-common": "^0.4.0",
36
37
  "@types/express": "^4.17.6",
37
38
  "express": "^4.17.1",
39
+ "express-promise-router": "^4.1.0",
38
40
  "zod": "^3.11.6"
39
41
  },
40
42
  "devDependencies": {
41
- "@backstage/cli": "^0.10.4",
43
+ "@backstage/cli": "^0.13.0",
42
44
  "@types/supertest": "^2.0.8",
43
45
  "msw": "^0.35.0",
44
46
  "supertest": "^6.1.3"
@@ -46,5 +48,5 @@
46
48
  "files": [
47
49
  "dist"
48
50
  ],
49
- "gitHead": "4b2a8ed96ff427735c872a72c1864321ef698436"
51
+ "gitHead": "493394603a2c47ea1d141159af9bc7bb84fac9e5"
50
52
  }