@backstage/plugin-permission-common 0.6.0-next.0 → 0.6.1-next.0

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,50 @@
1
1
  # @backstage/plugin-permission-common
2
2
 
3
+ ## 0.6.1-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/config@1.0.1-next.0
9
+
10
+ ## 0.6.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 8012ac46a0: Add `resourceType` property to `PermissionCondition` type to allow matching them with `ResourcePermission` instances.
15
+ - c98d271466: Refactor api types into more specific, decoupled names.
16
+
17
+ - **BREAKING:**
18
+ - Renamed `AuthorizeDecision` to `EvaluatePermissionResponse`
19
+ - Renamed `AuthorizeQuery` to `EvaluatePermissionRequest`
20
+ - Renamed `AuthorizeRequest` to `EvaluatePermissionRequestBatch`
21
+ - Renamed `AuthorizeResponse` to `EvaluatePermissionResponseBatch`
22
+ - Renamed `Identified` to `IdentifiedPermissionMessage`
23
+ - Add `PermissionMessageBatch` helper type
24
+ - Add `ConditionalPolicyDecision`, `DefinitivePolicyDecision`, and `PolicyDecision` types from `@backstage/plugin-permission-node`
25
+
26
+ ### Patch Changes
27
+
28
+ - 90754d4fa9: Removed [strict](https://github.com/colinhacks/zod#strict) validation from `PermissionCriteria` schemas to support backward-compatible changes.
29
+ - 2b07063d77: Added `PermissionEvaluator`, which will replace the existing `PermissionAuthorizer` interface. This new interface provides stronger type safety and validation by splitting `PermissionAuthorizer.authorize()` into two methods:
30
+
31
+ - `authorize()`: Used when the caller requires a definitive decision.
32
+ - `authorizeConditional()`: Used when the caller can optimize the evaluation of any conditional decisions. For example, a plugin backend may want to use conditions in a database query instead of evaluating each resource in memory.
33
+
34
+ - 8012ac46a0: Add `isPermission` helper method.
35
+ - 95284162d6: - Add more specific `Permission` types.
36
+ - Add `createPermission` helper to infer the appropriate type for some permission input.
37
+ - Add `isResourcePermission` helper to refine Permissions to ResourcePermissions.
38
+
39
+ ## 0.6.0-next.1
40
+
41
+ ### Patch Changes
42
+
43
+ - 2b07063d77: Added `PermissionEvaluator`, which will replace the existing `PermissionAuthorizer` interface. This new interface provides stronger type safety and validation by splitting `PermissionAuthorizer.authorize()` into two methods:
44
+
45
+ - `authorize()`: Used when the caller requires a definitive decision.
46
+ - `authorizeConditional()`: Used when the caller can optimize the evaluation of any conditional decisions. For example, a plugin backend may want to use conditions in a database query instead of evaluating each resource in memory.
47
+
3
48
  ## 0.6.0-next.0
4
49
 
5
50
  ### Minor Changes
package/dist/index.cjs.js CHANGED
@@ -58,6 +58,18 @@ function isUpdatePermission(permission) {
58
58
  function isDeletePermission(permission) {
59
59
  return permission.attributes.action === "delete";
60
60
  }
61
+ function toPermissionEvaluator(permissionAuthorizer) {
62
+ return {
63
+ authorize: async (requests, options) => {
64
+ const response = await permissionAuthorizer.authorize(requests, options);
65
+ return response;
66
+ },
67
+ authorizeConditional(requests, options) {
68
+ const parsedRequests = requests;
69
+ return permissionAuthorizer.authorize(parsedRequests, options);
70
+ }
71
+ };
72
+ }
61
73
 
62
74
  function createPermission({
63
75
  name,
@@ -83,16 +95,27 @@ const permissionCriteriaSchema = zod.z.lazy(() => zod.z.object({
83
95
  rule: zod.z.string(),
84
96
  resourceType: zod.z.string(),
85
97
  params: zod.z.array(zod.z.unknown())
86
- }).strict().or(zod.z.object({ anyOf: zod.z.array(permissionCriteriaSchema).nonempty() }).strict()).or(zod.z.object({ allOf: zod.z.array(permissionCriteriaSchema).nonempty() }).strict()).or(zod.z.object({ not: permissionCriteriaSchema }).strict()));
87
- const responseSchema = zod.z.object({
88
- items: zod.z.array(zod.z.object({
89
- id: zod.z.string(),
98
+ }).or(zod.z.object({ anyOf: zod.z.array(permissionCriteriaSchema).nonempty() })).or(zod.z.object({ allOf: zod.z.array(permissionCriteriaSchema).nonempty() })).or(zod.z.object({ not: permissionCriteriaSchema })));
99
+ const authorizePermissionResponseSchema = zod.z.object({
100
+ result: zod.z.literal(AuthorizeResult.ALLOW).or(zod.z.literal(AuthorizeResult.DENY))
101
+ });
102
+ const queryPermissionResponseSchema = zod.z.union([
103
+ zod.z.object({
90
104
  result: zod.z.literal(AuthorizeResult.ALLOW).or(zod.z.literal(AuthorizeResult.DENY))
91
- }).or(zod.z.object({
92
- id: zod.z.string(),
105
+ }),
106
+ zod.z.object({
93
107
  result: zod.z.literal(AuthorizeResult.CONDITIONAL),
108
+ pluginId: zod.z.string(),
109
+ resourceType: zod.z.string(),
94
110
  conditions: permissionCriteriaSchema
95
- })))
111
+ })
112
+ ]);
113
+ const responseSchema = (itemSchema, ids) => zod.z.object({
114
+ items: zod.z.array(zod.z.intersection(zod.z.object({
115
+ id: zod.z.string()
116
+ }), itemSchema)).refine((items) => items.length === ids.size && items.every(({ id }) => ids.has(id)), {
117
+ message: "Items in response do not match request"
118
+ })
96
119
  });
97
120
  class PermissionClient {
98
121
  constructor(options) {
@@ -100,7 +123,13 @@ class PermissionClient {
100
123
  this.discovery = options.discovery;
101
124
  this.enabled = (_a = options.config.getOptionalBoolean("permission.enabled")) != null ? _a : false;
102
125
  }
103
- async authorize(queries, options) {
126
+ async authorize(requests, options) {
127
+ return this.makeRequest(requests, authorizePermissionResponseSchema, options);
128
+ }
129
+ async authorizeConditional(queries, options) {
130
+ return this.makeRequest(queries, queryPermissionResponseSchema, options);
131
+ }
132
+ async makeRequest(queries, itemSchema, options) {
104
133
  if (!this.enabled) {
105
134
  return queries.map((_) => ({ result: AuthorizeResult.ALLOW }));
106
135
  }
@@ -123,8 +152,8 @@ class PermissionClient {
123
152
  throw await errors.ResponseError.fromResponse(response);
124
153
  }
125
154
  const responseBody = await response.json();
126
- this.assertValidResponse(request, responseBody);
127
- const responsesById = responseBody.items.reduce((acc, r) => {
155
+ const parsedResponse = responseSchema(itemSchema, new Set(request.items.map(({ id }) => id))).parse(responseBody);
156
+ const responsesById = parsedResponse.items.reduce((acc, r) => {
128
157
  acc[r.id] = r;
129
158
  return acc;
130
159
  }, {});
@@ -133,14 +162,6 @@ class PermissionClient {
133
162
  getAuthorizationHeader(token) {
134
163
  return token ? { Authorization: `Bearer ${token}` } : {};
135
164
  }
136
- assertValidResponse(request, json) {
137
- const authorizedResponses = responseSchema.parse(json);
138
- const responseIds = authorizedResponses.items.map((r) => r.id);
139
- const hasAllRequestIds = request.items.every((r) => responseIds.includes(r.id));
140
- if (!hasAllRequestIds) {
141
- throw new Error("Unexpected authorization response from permission-backend");
142
- }
143
- }
144
165
  }
145
166
 
146
167
  exports.AuthorizeResult = AuthorizeResult;
@@ -152,4 +173,5 @@ exports.isPermission = isPermission;
152
173
  exports.isReadPermission = isReadPermission;
153
174
  exports.isResourcePermission = isResourcePermission;
154
175
  exports.isUpdatePermission = isUpdatePermission;
176
+ exports.toPermissionEvaluator = toPermissionEvaluator;
155
177
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/types/api.ts","../src/permissions/util.ts","../src/permissions/createPermission.ts","../src/PermissionClient.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 { Permission } from './permission';\n\n/**\n * A request with a UUID identifier, so that batched responses can be matched up with the original\n * requests.\n * @public\n */\nexport type IdentifiedPermissionMessage<T> = T & { id: string };\n\n/**\n * A batch of request or response items.\n * @public\n */\nexport type PermissionMessageBatch<T> = {\n items: IdentifiedPermissionMessage<T>[];\n};\n\n/**\n * The result of an authorization request.\n * @public\n */\nexport enum AuthorizeResult {\n /**\n * The authorization request is denied.\n */\n DENY = 'DENY',\n /**\n * The authorization request is allowed.\n */\n ALLOW = 'ALLOW',\n /**\n * The authorization request is allowed if the provided conditions are met.\n */\n CONDITIONAL = 'CONDITIONAL',\n}\n\n/**\n * A definitive decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy unconditionally allows (or denies) the request.\n *\n * @public\n */\nexport type DefinitivePolicyDecision = {\n result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;\n};\n\n/**\n * A conditional decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy allows authorization for the request, given that the returned\n * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin\n * which knows about the referenced permission rules.\n *\n * @public\n */\nexport type ConditionalPolicyDecision = {\n result: AuthorizeResult.CONDITIONAL;\n pluginId: string;\n resourceType: string;\n conditions: PermissionCriteria<PermissionCondition>;\n};\n\n/**\n * A decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @public\n */\nexport type PolicyDecision =\n | DefinitivePolicyDecision\n | ConditionalPolicyDecision;\n\n/**\n * A condition returned with a CONDITIONAL authorization response.\n *\n * Conditions are a reference to a rule defined by a plugin, and parameters to apply the rule. For\n * example, a rule might be `isOwner` from the catalog-backend, and params may be a list of entity\n * claims from a identity token.\n * @public\n */\nexport type PermissionCondition<\n TResourceType extends string = string,\n TParams extends unknown[] = unknown[],\n> = {\n resourceType: TResourceType;\n rule: string;\n params: TParams;\n};\n\n/**\n * Utility type to represent an array with 1 or more elements.\n * @ignore\n */\ntype NonEmptyArray<T> = [T, ...T[]];\n\n/**\n * Represents a logical AND for the provided criteria.\n * @public\n */\nexport type AllOfCriteria<TQuery> = {\n allOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a logical OR for the provided criteria.\n * @public\n */\nexport type AnyOfCriteria<TQuery> = {\n anyOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a negation of the provided criteria.\n * @public\n */\nexport type NotCriteria<TQuery> = {\n not: PermissionCriteria<TQuery>;\n};\n\n/**\n * Composes several {@link PermissionCondition}s as criteria with a nested AND/OR structure.\n * @public\n */\nexport type PermissionCriteria<TQuery> =\n | AllOfCriteria<TQuery>\n | AnyOfCriteria<TQuery>\n | NotCriteria<TQuery>\n | TQuery;\n\n/**\n * An individual request sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequest = {\n permission: Permission;\n resourceRef?: string;\n};\n\n/**\n * A batch of requests sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequestBatch =\n PermissionMessageBatch<EvaluatePermissionRequest>;\n\n/**\n * An individual response from the permission backend.\n *\n * @remarks\n *\n * This response type is an alias of {@link PolicyDecision} to maintain separation between the\n * {@link @backstage/plugin-permission-node#PermissionPolicy} interface and the permission backend\n * api. They may diverge at some point in the future. The response\n *\n * @public\n */\nexport type EvaluatePermissionResponse = PolicyDecision;\n\n/**\n * A batch of responses from the permission backend.\n * @public\n */\nexport type EvaluatePermissionResponseBatch =\n PermissionMessageBatch<EvaluatePermissionResponse>;\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 { Permission, ResourcePermission } from '../types';\n\n/**\n * Check if the two parameters are equivalent permissions.\n * @public\n */\nexport function isPermission<T extends Permission>(\n permission: Permission,\n comparedPermission: T,\n): permission is T {\n return permission.name === comparedPermission.name;\n}\n\n/**\n * Check if a given permission is a {@link ResourcePermission}. When\n * `resourceType` is supplied as the second parameter, also checks if\n * the permission has the specified resource type.\n * @public\n */\nexport function isResourcePermission<T extends string = string>(\n permission: Permission,\n resourceType?: T,\n): permission is ResourcePermission<T> {\n if (!('resourceType' in permission)) {\n return false;\n }\n\n return !resourceType || permission.resourceType === resourceType;\n}\n\n/**\n * Check if a given permission is related to a create action.\n * @public\n */\nexport function isCreatePermission(permission: Permission) {\n return permission.attributes.action === 'create';\n}\n\n/**\n * Check if a given permission is related to a read action.\n * @public\n */\nexport function isReadPermission(permission: Permission) {\n return permission.attributes.action === 'read';\n}\n\n/**\n * Check if a given permission is related to an update action.\n * @public\n */\nexport function isUpdatePermission(permission: Permission) {\n return permission.attributes.action === 'update';\n}\n\n/**\n * Check if a given permission is related to a delete action.\n * @public\n */\nexport function isDeletePermission(permission: Permission) {\n return permission.attributes.action === 'delete';\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 {\n BasicPermission,\n Permission,\n PermissionAttributes,\n ResourcePermission,\n} from '../types';\n\n/**\n * Utility function for creating a valid {@link ResourcePermission}, inferring\n * the appropriate type and resource type parameter.\n *\n * @public\n */\nexport function createPermission<TResourceType extends string>(input: {\n name: string;\n attributes: PermissionAttributes;\n resourceType: TResourceType;\n}): ResourcePermission<TResourceType>;\n/**\n * Utility function for creating a valid {@link BasicPermission}.\n *\n * @public\n */\nexport function createPermission(input: {\n name: string;\n attributes: PermissionAttributes;\n}): BasicPermission;\nexport function createPermission({\n name,\n attributes,\n resourceType,\n}: {\n name: string;\n attributes: PermissionAttributes;\n resourceType?: string;\n}): Permission {\n if (resourceType) {\n return {\n type: 'resource',\n name,\n attributes,\n resourceType,\n };\n }\n\n return {\n type: 'basic',\n name,\n attributes,\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 { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n EvaluatePermissionRequest,\n EvaluatePermissionResponse,\n IdentifiedPermissionMessage,\n PermissionCriteria,\n PermissionCondition,\n EvaluatePermissionResponseBatch,\n EvaluatePermissionRequestBatch,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport {\n PermissionAuthorizer,\n AuthorizeRequestOptions,\n} from './types/permission';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.array(z.unknown()),\n })\n .strict()\n .or(\n z\n .object({ anyOf: z.array(permissionCriteriaSchema).nonempty() })\n .strict(),\n )\n .or(\n z\n .object({ allOf: z.array(permissionCriteriaSchema).nonempty() })\n .strict(),\n )\n .or(z.object({ not: permissionCriteriaSchema }).strict()),\n);\n\nconst responseSchema = z.object({\n items: z.array(\n z\n .object({\n id: z.string(),\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n })\n .or(\n z.object({\n id: z.string(),\n result: z.literal(AuthorizeResult.CONDITIONAL),\n conditions: permissionCriteriaSchema,\n }),\n ),\n ),\n});\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionAuthorizer {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n }\n\n /**\n * Request authorization from the permission-backend for the given set of permissions.\n *\n * Authorization requests check that a given Backstage user can perform a protected operation,\n * potentially for a specific resource (such as a catalog entity). The Backstage identity token\n * should be included in the `options` if available.\n *\n * Permissions can be imported from plugins exposing them, such as `catalogEntityReadPermission`.\n *\n * The response will be either ALLOW or DENY when either the permission has no resourceType, or a\n * resourceRef is provided in the request. For permissions with a resourceType, CONDITIONAL may be\n * returned if no resourceRef is provided in the request. Conditional responses are intended only\n * for backends which have access to the data source for permissioned resources, so that filters\n * can be applied when loading collections of resources.\n * @public\n */\n async authorize(\n queries: EvaluatePermissionRequest[],\n options?: AuthorizeRequestOptions,\n ): Promise<EvaluatePermissionResponse[]> {\n // TODO(permissions): it would be great to provide some kind of typing guarantee that\n // conditional responses will only ever be returned for requests containing a resourceType\n // but no resourceRef. That way clients who aren't prepared to handle filtering according\n // to conditions can be guaranteed that they won't unexpectedly get a CONDITIONAL response.\n\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW }));\n }\n\n const request: EvaluatePermissionRequestBatch = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n this.assertValidResponse(request, responseBody);\n\n const responsesById = responseBody.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, IdentifiedPermissionMessage<EvaluatePermissionResponse>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n private assertValidResponse(\n request: EvaluatePermissionRequestBatch,\n json: any,\n ): asserts json is EvaluatePermissionResponseBatch {\n const authorizedResponses = responseSchema.parse(json);\n const responseIds = authorizedResponses.items.map(r => r.id);\n const hasAllRequestIds = request.items.every(r =>\n responseIds.includes(r.id),\n );\n if (!hasAllRequestIds) {\n throw new Error(\n 'Unexpected authorization response from permission-backend',\n );\n }\n }\n}\n"],"names":["z","uuid","fetch","ResponseError"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/types/api.ts","../src/permissions/util.ts","../src/permissions/createPermission.ts","../src/PermissionClient.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 { ResourcePermission } from '.';\nimport { Permission } from './permission';\n\n/**\n * A request with a UUID identifier, so that batched responses can be matched up with the original\n * requests.\n * @public\n */\nexport type IdentifiedPermissionMessage<T> = T & { id: string };\n\n/**\n * A batch of request or response items.\n * @public\n */\nexport type PermissionMessageBatch<T> = {\n items: IdentifiedPermissionMessage<T>[];\n};\n\n/**\n * The result of an authorization request.\n * @public\n */\nexport enum AuthorizeResult {\n /**\n * The authorization request is denied.\n */\n DENY = 'DENY',\n /**\n * The authorization request is allowed.\n */\n ALLOW = 'ALLOW',\n /**\n * The authorization request is allowed if the provided conditions are met.\n */\n CONDITIONAL = 'CONDITIONAL',\n}\n\n/**\n * A definitive decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy unconditionally allows (or denies) the request.\n *\n * @public\n */\nexport type DefinitivePolicyDecision = {\n result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;\n};\n\n/**\n * A conditional decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy allows authorization for the request, given that the returned\n * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin\n * which knows about the referenced permission rules.\n *\n * @public\n */\nexport type ConditionalPolicyDecision = {\n result: AuthorizeResult.CONDITIONAL;\n pluginId: string;\n resourceType: string;\n conditions: PermissionCriteria<PermissionCondition>;\n};\n\n/**\n * A decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @public\n */\nexport type PolicyDecision =\n | DefinitivePolicyDecision\n | ConditionalPolicyDecision;\n\n/**\n * A condition returned with a CONDITIONAL authorization response.\n *\n * Conditions are a reference to a rule defined by a plugin, and parameters to apply the rule. For\n * example, a rule might be `isOwner` from the catalog-backend, and params may be a list of entity\n * claims from a identity token.\n * @public\n */\nexport type PermissionCondition<\n TResourceType extends string = string,\n TParams extends unknown[] = unknown[],\n> = {\n resourceType: TResourceType;\n rule: string;\n params: TParams;\n};\n\n/**\n * Utility type to represent an array with 1 or more elements.\n * @ignore\n */\ntype NonEmptyArray<T> = [T, ...T[]];\n\n/**\n * Represents a logical AND for the provided criteria.\n * @public\n */\nexport type AllOfCriteria<TQuery> = {\n allOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a logical OR for the provided criteria.\n * @public\n */\nexport type AnyOfCriteria<TQuery> = {\n anyOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a negation of the provided criteria.\n * @public\n */\nexport type NotCriteria<TQuery> = {\n not: PermissionCriteria<TQuery>;\n};\n\n/**\n * Composes several {@link PermissionCondition}s as criteria with a nested AND/OR structure.\n * @public\n */\nexport type PermissionCriteria<TQuery> =\n | AllOfCriteria<TQuery>\n | AnyOfCriteria<TQuery>\n | NotCriteria<TQuery>\n | TQuery;\n\n/**\n * An individual request sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequest = {\n permission: Permission;\n resourceRef?: string;\n};\n\n/**\n * A batch of requests sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequestBatch =\n PermissionMessageBatch<EvaluatePermissionRequest>;\n\n/**\n * An individual response from the permission backend.\n *\n * @remarks\n *\n * This response type is an alias of {@link PolicyDecision} to maintain separation between the\n * {@link @backstage/plugin-permission-node#PermissionPolicy} interface and the permission backend\n * api. They may diverge at some point in the future. The response\n *\n * @public\n */\nexport type EvaluatePermissionResponse = PolicyDecision;\n\n/**\n * A batch of responses from the permission backend.\n * @public\n */\nexport type EvaluatePermissionResponseBatch =\n PermissionMessageBatch<EvaluatePermissionResponse>;\n\n/**\n * Request object for {@link PermissionEvaluator.authorize}. If a {@link ResourcePermission}\n * is provided, it must include a corresponding `resourceRef`.\n * @public\n */\nexport type AuthorizePermissionRequest =\n | {\n permission: Exclude<Permission, ResourcePermission>;\n resourceRef?: never;\n }\n | { permission: ResourcePermission; resourceRef: string };\n\n/**\n * Response object for {@link PermissionEvaluator.authorize}.\n * @public\n */\nexport type AuthorizePermissionResponse = DefinitivePolicyDecision;\n\n/**\n * Request object for {@link PermissionEvaluator.authorizeConditional}.\n * @public\n */\nexport type QueryPermissionRequest = {\n permission: ResourcePermission;\n resourceRef?: never;\n};\n\n/**\n * Response object for {@link PermissionEvaluator.authorizeConditional}.\n * @public\n */\nexport type QueryPermissionResponse = PolicyDecision;\n\n/**\n * A client interacting with the permission backend can implement this evaluator interface.\n *\n * @public\n */\nexport interface PermissionEvaluator {\n /**\n * Evaluates {@link Permission | Permissions} and returns a definitive decision.\n */\n authorize(\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]>;\n\n /**\n * Evaluates {@link ResourcePermission | ResourcePermissions} and returns both definitive and\n * conditional decisions, depending on the configured\n * {@link @backstage/plugin-permission-node#PermissionPolicy}. This method is useful when the\n * caller needs more control over the processing of conditional decisions. For example, a plugin\n * backend may want to use {@link PermissionCriteria | conditions} in a database query instead of\n * evaluating each resource in memory.\n */\n authorizeConditional(\n requests: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<QueryPermissionResponse[]>;\n}\n\n/**\n * Options for {@link PermissionEvaluator} requests.\n * The Backstage identity token should be defined if available.\n * @public\n */\nexport type EvaluatorRequestOptions = {\n token?: string;\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 AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n DefinitivePolicyDecision,\n EvaluatorRequestOptions,\n Permission,\n PermissionAuthorizer,\n PermissionEvaluator,\n QueryPermissionRequest,\n QueryPermissionResponse,\n ResourcePermission,\n} from '../types';\n\n/**\n * Check if the two parameters are equivalent permissions.\n * @public\n */\nexport function isPermission<T extends Permission>(\n permission: Permission,\n comparedPermission: T,\n): permission is T {\n return permission.name === comparedPermission.name;\n}\n\n/**\n * Check if a given permission is a {@link ResourcePermission}. When\n * `resourceType` is supplied as the second parameter, also checks if\n * the permission has the specified resource type.\n * @public\n */\nexport function isResourcePermission<T extends string = string>(\n permission: Permission,\n resourceType?: T,\n): permission is ResourcePermission<T> {\n if (!('resourceType' in permission)) {\n return false;\n }\n\n return !resourceType || permission.resourceType === resourceType;\n}\n\n/**\n * Check if a given permission is related to a create action.\n * @public\n */\nexport function isCreatePermission(permission: Permission) {\n return permission.attributes.action === 'create';\n}\n\n/**\n * Check if a given permission is related to a read action.\n * @public\n */\nexport function isReadPermission(permission: Permission) {\n return permission.attributes.action === 'read';\n}\n\n/**\n * Check if a given permission is related to an update action.\n * @public\n */\nexport function isUpdatePermission(permission: Permission) {\n return permission.attributes.action === 'update';\n}\n\n/**\n * Check if a given permission is related to a delete action.\n * @public\n */\nexport function isDeletePermission(permission: Permission) {\n return permission.attributes.action === 'delete';\n}\n\n/**\n * Convert {@link PermissionAuthorizer} to {@link PermissionEvaluator}.\n *\n * @public\n */\nexport function toPermissionEvaluator(\n permissionAuthorizer: PermissionAuthorizer,\n): PermissionEvaluator {\n return {\n authorize: async (\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> => {\n const response = await permissionAuthorizer.authorize(requests, options);\n\n return response as DefinitivePolicyDecision[];\n },\n authorizeConditional(\n requests: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n const parsedRequests =\n requests as unknown as AuthorizePermissionRequest[];\n return permissionAuthorizer.authorize(parsedRequests, options);\n },\n };\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 {\n BasicPermission,\n Permission,\n PermissionAttributes,\n ResourcePermission,\n} from '../types';\n\n/**\n * Utility function for creating a valid {@link ResourcePermission}, inferring\n * the appropriate type and resource type parameter.\n *\n * @public\n */\nexport function createPermission<TResourceType extends string>(input: {\n name: string;\n attributes: PermissionAttributes;\n resourceType: TResourceType;\n}): ResourcePermission<TResourceType>;\n/**\n * Utility function for creating a valid {@link BasicPermission}.\n *\n * @public\n */\nexport function createPermission(input: {\n name: string;\n attributes: PermissionAttributes;\n}): BasicPermission;\nexport function createPermission({\n name,\n attributes,\n resourceType,\n}: {\n name: string;\n attributes: PermissionAttributes;\n resourceType?: string;\n}): Permission {\n if (resourceType) {\n return {\n type: 'resource',\n name,\n attributes,\n resourceType,\n };\n }\n\n return {\n type: 'basic',\n name,\n attributes,\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 { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionMessageBatch,\n PermissionCriteria,\n PermissionCondition,\n PermissionEvaluator,\n QueryPermissionRequest,\n AuthorizePermissionRequest,\n EvaluatorRequestOptions,\n AuthorizePermissionResponse,\n QueryPermissionResponse,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport { AuthorizeRequestOptions } from './types/permission';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.array(z.unknown()),\n })\n .or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ not: permissionCriteriaSchema })),\n);\n\nconst authorizePermissionResponseSchema: z.ZodSchema<AuthorizePermissionResponse> =\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n });\n\nconst queryPermissionResponseSchema: z.ZodSchema<QueryPermissionResponse> =\n z.union([\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n z.object({\n result: z.literal(AuthorizeResult.CONDITIONAL),\n pluginId: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ]);\n\nconst responseSchema = <T>(\n itemSchema: z.ZodSchema<T>,\n ids: Set<string>,\n): z.ZodSchema<PermissionMessageBatch<T>> =>\n z.object({\n items: z\n .array(\n z.intersection(\n z.object({\n id: z.string(),\n }),\n itemSchema,\n ),\n )\n .refine(\n items =>\n items.length === ids.size && items.every(({ id }) => ids.has(id)),\n {\n message: 'Items in response do not match request',\n },\n ),\n });\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionEvaluator {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorize}\n */\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n return this.makeRequest(\n requests,\n authorizePermissionResponseSchema,\n options,\n );\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorizeConditional}\n */\n async authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n return this.makeRequest(queries, queryPermissionResponseSchema, options);\n }\n\n private async makeRequest<TQuery, TResult>(\n queries: TQuery[],\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n const request: PermissionMessageBatch<TQuery> = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n\n const parsedResponse = responseSchema(\n itemSchema,\n new Set(request.items.map(({ id }) => id)),\n ).parse(responseBody);\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, z.infer<typeof itemSchema>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n}\n"],"names":["z","uuid","fetch","ResponseError"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -66,6 +66,7 @@ declare type ResourcePermission<TResourceType extends string = string> = Permiss
66
66
  /**
67
67
  * A client interacting with the permission backend can implement this authorizer interface.
68
68
  * @public
69
+ * @deprecated Use {@link @backstage/plugin-permission-common#PermissionEvaluator} instead
69
70
  */
70
71
  interface PermissionAuthorizer {
71
72
  authorize(requests: EvaluatePermissionRequest[], options?: AuthorizeRequestOptions): Promise<EvaluatePermissionResponse[]>;
@@ -220,6 +221,64 @@ declare type EvaluatePermissionResponse = PolicyDecision;
220
221
  * @public
221
222
  */
222
223
  declare type EvaluatePermissionResponseBatch = PermissionMessageBatch<EvaluatePermissionResponse>;
224
+ /**
225
+ * Request object for {@link PermissionEvaluator.authorize}. If a {@link ResourcePermission}
226
+ * is provided, it must include a corresponding `resourceRef`.
227
+ * @public
228
+ */
229
+ declare type AuthorizePermissionRequest = {
230
+ permission: Exclude<Permission, ResourcePermission>;
231
+ resourceRef?: never;
232
+ } | {
233
+ permission: ResourcePermission;
234
+ resourceRef: string;
235
+ };
236
+ /**
237
+ * Response object for {@link PermissionEvaluator.authorize}.
238
+ * @public
239
+ */
240
+ declare type AuthorizePermissionResponse = DefinitivePolicyDecision;
241
+ /**
242
+ * Request object for {@link PermissionEvaluator.authorizeConditional}.
243
+ * @public
244
+ */
245
+ declare type QueryPermissionRequest = {
246
+ permission: ResourcePermission;
247
+ resourceRef?: never;
248
+ };
249
+ /**
250
+ * Response object for {@link PermissionEvaluator.authorizeConditional}.
251
+ * @public
252
+ */
253
+ declare type QueryPermissionResponse = PolicyDecision;
254
+ /**
255
+ * A client interacting with the permission backend can implement this evaluator interface.
256
+ *
257
+ * @public
258
+ */
259
+ interface PermissionEvaluator {
260
+ /**
261
+ * Evaluates {@link Permission | Permissions} and returns a definitive decision.
262
+ */
263
+ authorize(requests: AuthorizePermissionRequest[], options?: EvaluatorRequestOptions): Promise<AuthorizePermissionResponse[]>;
264
+ /**
265
+ * Evaluates {@link ResourcePermission | ResourcePermissions} and returns both definitive and
266
+ * conditional decisions, depending on the configured
267
+ * {@link @backstage/plugin-permission-node#PermissionPolicy}. This method is useful when the
268
+ * caller needs more control over the processing of conditional decisions. For example, a plugin
269
+ * backend may want to use {@link PermissionCriteria | conditions} in a database query instead of
270
+ * evaluating each resource in memory.
271
+ */
272
+ authorizeConditional(requests: QueryPermissionRequest[], options?: EvaluatorRequestOptions): Promise<QueryPermissionResponse[]>;
273
+ }
274
+ /**
275
+ * Options for {@link PermissionEvaluator} requests.
276
+ * The Backstage identity token should be defined if available.
277
+ * @public
278
+ */
279
+ declare type EvaluatorRequestOptions = {
280
+ token?: string;
281
+ };
223
282
 
224
283
  /**
225
284
  * This is a copy of the core DiscoveryApi, to avoid importing core.
@@ -262,6 +321,12 @@ declare function isUpdatePermission(permission: Permission): boolean;
262
321
  * @public
263
322
  */
264
323
  declare function isDeletePermission(permission: Permission): boolean;
324
+ /**
325
+ * Convert {@link PermissionAuthorizer} to {@link PermissionEvaluator}.
326
+ *
327
+ * @public
328
+ */
329
+ declare function toPermissionEvaluator(permissionAuthorizer: PermissionAuthorizer): PermissionEvaluator;
265
330
 
266
331
  /**
267
332
  * Utility function for creating a valid {@link ResourcePermission}, inferring
@@ -288,7 +353,7 @@ declare function createPermission(input: {
288
353
  * An isomorphic client for requesting authorization for Backstage permissions.
289
354
  * @public
290
355
  */
291
- declare class PermissionClient implements PermissionAuthorizer {
356
+ declare class PermissionClient implements PermissionEvaluator {
292
357
  private readonly enabled;
293
358
  private readonly discovery;
294
359
  constructor(options: {
@@ -296,24 +361,15 @@ declare class PermissionClient implements PermissionAuthorizer {
296
361
  config: Config;
297
362
  });
298
363
  /**
299
- * Request authorization from the permission-backend for the given set of permissions.
300
- *
301
- * Authorization requests check that a given Backstage user can perform a protected operation,
302
- * potentially for a specific resource (such as a catalog entity). The Backstage identity token
303
- * should be included in the `options` if available.
304
- *
305
- * Permissions can be imported from plugins exposing them, such as `catalogEntityReadPermission`.
306
- *
307
- * The response will be either ALLOW or DENY when either the permission has no resourceType, or a
308
- * resourceRef is provided in the request. For permissions with a resourceType, CONDITIONAL may be
309
- * returned if no resourceRef is provided in the request. Conditional responses are intended only
310
- * for backends which have access to the data source for permissioned resources, so that filters
311
- * can be applied when loading collections of resources.
312
- * @public
364
+ * {@inheritdoc PermissionEvaluator.authorize}
365
+ */
366
+ authorize(requests: AuthorizePermissionRequest[], options?: EvaluatorRequestOptions): Promise<AuthorizePermissionResponse[]>;
367
+ /**
368
+ * {@inheritdoc PermissionEvaluator.authorizeConditional}
313
369
  */
314
- authorize(queries: EvaluatePermissionRequest[], options?: AuthorizeRequestOptions): Promise<EvaluatePermissionResponse[]>;
370
+ authorizeConditional(queries: QueryPermissionRequest[], options?: EvaluatorRequestOptions): Promise<QueryPermissionResponse[]>;
371
+ private makeRequest;
315
372
  private getAuthorizationHeader;
316
- private assertValidResponse;
317
373
  }
318
374
 
319
- export { AllOfCriteria, AnyOfCriteria, AuthorizeRequestOptions, AuthorizeResult, BasicPermission, ConditionalPolicyDecision, DefinitivePolicyDecision, DiscoveryApi, EvaluatePermissionRequest, EvaluatePermissionRequestBatch, EvaluatePermissionResponse, EvaluatePermissionResponseBatch, IdentifiedPermissionMessage, NotCriteria, Permission, PermissionAttributes, PermissionAuthorizer, PermissionBase, PermissionClient, PermissionCondition, PermissionCriteria, PermissionMessageBatch, PolicyDecision, ResourcePermission, createPermission, isCreatePermission, isDeletePermission, isPermission, isReadPermission, isResourcePermission, isUpdatePermission };
375
+ export { AllOfCriteria, AnyOfCriteria, AuthorizePermissionRequest, AuthorizePermissionResponse, AuthorizeRequestOptions, AuthorizeResult, BasicPermission, ConditionalPolicyDecision, DefinitivePolicyDecision, DiscoveryApi, EvaluatePermissionRequest, EvaluatePermissionRequestBatch, EvaluatePermissionResponse, EvaluatePermissionResponseBatch, EvaluatorRequestOptions, IdentifiedPermissionMessage, NotCriteria, Permission, PermissionAttributes, PermissionAuthorizer, PermissionBase, PermissionClient, PermissionCondition, PermissionCriteria, PermissionEvaluator, PermissionMessageBatch, PolicyDecision, QueryPermissionRequest, QueryPermissionResponse, ResourcePermission, createPermission, isCreatePermission, isDeletePermission, isPermission, isReadPermission, isResourcePermission, isUpdatePermission, toPermissionEvaluator };
package/dist/index.esm.js CHANGED
@@ -31,6 +31,18 @@ function isUpdatePermission(permission) {
31
31
  function isDeletePermission(permission) {
32
32
  return permission.attributes.action === "delete";
33
33
  }
34
+ function toPermissionEvaluator(permissionAuthorizer) {
35
+ return {
36
+ authorize: async (requests, options) => {
37
+ const response = await permissionAuthorizer.authorize(requests, options);
38
+ return response;
39
+ },
40
+ authorizeConditional(requests, options) {
41
+ const parsedRequests = requests;
42
+ return permissionAuthorizer.authorize(parsedRequests, options);
43
+ }
44
+ };
45
+ }
34
46
 
35
47
  function createPermission({
36
48
  name,
@@ -56,16 +68,27 @@ const permissionCriteriaSchema = z.lazy(() => z.object({
56
68
  rule: z.string(),
57
69
  resourceType: z.string(),
58
70
  params: z.array(z.unknown())
59
- }).strict().or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }).strict()).or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }).strict()).or(z.object({ not: permissionCriteriaSchema }).strict()));
60
- const responseSchema = z.object({
61
- items: z.array(z.object({
62
- id: z.string(),
71
+ }).or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() })).or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() })).or(z.object({ not: permissionCriteriaSchema })));
72
+ const authorizePermissionResponseSchema = z.object({
73
+ result: z.literal(AuthorizeResult.ALLOW).or(z.literal(AuthorizeResult.DENY))
74
+ });
75
+ const queryPermissionResponseSchema = z.union([
76
+ z.object({
63
77
  result: z.literal(AuthorizeResult.ALLOW).or(z.literal(AuthorizeResult.DENY))
64
- }).or(z.object({
65
- id: z.string(),
78
+ }),
79
+ z.object({
66
80
  result: z.literal(AuthorizeResult.CONDITIONAL),
81
+ pluginId: z.string(),
82
+ resourceType: z.string(),
67
83
  conditions: permissionCriteriaSchema
68
- })))
84
+ })
85
+ ]);
86
+ const responseSchema = (itemSchema, ids) => z.object({
87
+ items: z.array(z.intersection(z.object({
88
+ id: z.string()
89
+ }), itemSchema)).refine((items) => items.length === ids.size && items.every(({ id }) => ids.has(id)), {
90
+ message: "Items in response do not match request"
91
+ })
69
92
  });
70
93
  class PermissionClient {
71
94
  constructor(options) {
@@ -73,7 +96,13 @@ class PermissionClient {
73
96
  this.discovery = options.discovery;
74
97
  this.enabled = (_a = options.config.getOptionalBoolean("permission.enabled")) != null ? _a : false;
75
98
  }
76
- async authorize(queries, options) {
99
+ async authorize(requests, options) {
100
+ return this.makeRequest(requests, authorizePermissionResponseSchema, options);
101
+ }
102
+ async authorizeConditional(queries, options) {
103
+ return this.makeRequest(queries, queryPermissionResponseSchema, options);
104
+ }
105
+ async makeRequest(queries, itemSchema, options) {
77
106
  if (!this.enabled) {
78
107
  return queries.map((_) => ({ result: AuthorizeResult.ALLOW }));
79
108
  }
@@ -96,8 +125,8 @@ class PermissionClient {
96
125
  throw await ResponseError.fromResponse(response);
97
126
  }
98
127
  const responseBody = await response.json();
99
- this.assertValidResponse(request, responseBody);
100
- const responsesById = responseBody.items.reduce((acc, r) => {
128
+ const parsedResponse = responseSchema(itemSchema, new Set(request.items.map(({ id }) => id))).parse(responseBody);
129
+ const responsesById = parsedResponse.items.reduce((acc, r) => {
101
130
  acc[r.id] = r;
102
131
  return acc;
103
132
  }, {});
@@ -106,15 +135,7 @@ class PermissionClient {
106
135
  getAuthorizationHeader(token) {
107
136
  return token ? { Authorization: `Bearer ${token}` } : {};
108
137
  }
109
- assertValidResponse(request, json) {
110
- const authorizedResponses = responseSchema.parse(json);
111
- const responseIds = authorizedResponses.items.map((r) => r.id);
112
- const hasAllRequestIds = request.items.every((r) => responseIds.includes(r.id));
113
- if (!hasAllRequestIds) {
114
- throw new Error("Unexpected authorization response from permission-backend");
115
- }
116
- }
117
138
  }
118
139
 
119
- export { AuthorizeResult, PermissionClient, createPermission, isCreatePermission, isDeletePermission, isPermission, isReadPermission, isResourcePermission, isUpdatePermission };
140
+ export { AuthorizeResult, PermissionClient, createPermission, isCreatePermission, isDeletePermission, isPermission, isReadPermission, isResourcePermission, isUpdatePermission, toPermissionEvaluator };
120
141
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/types/api.ts","../src/permissions/util.ts","../src/permissions/createPermission.ts","../src/PermissionClient.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 { Permission } from './permission';\n\n/**\n * A request with a UUID identifier, so that batched responses can be matched up with the original\n * requests.\n * @public\n */\nexport type IdentifiedPermissionMessage<T> = T & { id: string };\n\n/**\n * A batch of request or response items.\n * @public\n */\nexport type PermissionMessageBatch<T> = {\n items: IdentifiedPermissionMessage<T>[];\n};\n\n/**\n * The result of an authorization request.\n * @public\n */\nexport enum AuthorizeResult {\n /**\n * The authorization request is denied.\n */\n DENY = 'DENY',\n /**\n * The authorization request is allowed.\n */\n ALLOW = 'ALLOW',\n /**\n * The authorization request is allowed if the provided conditions are met.\n */\n CONDITIONAL = 'CONDITIONAL',\n}\n\n/**\n * A definitive decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy unconditionally allows (or denies) the request.\n *\n * @public\n */\nexport type DefinitivePolicyDecision = {\n result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;\n};\n\n/**\n * A conditional decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy allows authorization for the request, given that the returned\n * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin\n * which knows about the referenced permission rules.\n *\n * @public\n */\nexport type ConditionalPolicyDecision = {\n result: AuthorizeResult.CONDITIONAL;\n pluginId: string;\n resourceType: string;\n conditions: PermissionCriteria<PermissionCondition>;\n};\n\n/**\n * A decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @public\n */\nexport type PolicyDecision =\n | DefinitivePolicyDecision\n | ConditionalPolicyDecision;\n\n/**\n * A condition returned with a CONDITIONAL authorization response.\n *\n * Conditions are a reference to a rule defined by a plugin, and parameters to apply the rule. For\n * example, a rule might be `isOwner` from the catalog-backend, and params may be a list of entity\n * claims from a identity token.\n * @public\n */\nexport type PermissionCondition<\n TResourceType extends string = string,\n TParams extends unknown[] = unknown[],\n> = {\n resourceType: TResourceType;\n rule: string;\n params: TParams;\n};\n\n/**\n * Utility type to represent an array with 1 or more elements.\n * @ignore\n */\ntype NonEmptyArray<T> = [T, ...T[]];\n\n/**\n * Represents a logical AND for the provided criteria.\n * @public\n */\nexport type AllOfCriteria<TQuery> = {\n allOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a logical OR for the provided criteria.\n * @public\n */\nexport type AnyOfCriteria<TQuery> = {\n anyOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a negation of the provided criteria.\n * @public\n */\nexport type NotCriteria<TQuery> = {\n not: PermissionCriteria<TQuery>;\n};\n\n/**\n * Composes several {@link PermissionCondition}s as criteria with a nested AND/OR structure.\n * @public\n */\nexport type PermissionCriteria<TQuery> =\n | AllOfCriteria<TQuery>\n | AnyOfCriteria<TQuery>\n | NotCriteria<TQuery>\n | TQuery;\n\n/**\n * An individual request sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequest = {\n permission: Permission;\n resourceRef?: string;\n};\n\n/**\n * A batch of requests sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequestBatch =\n PermissionMessageBatch<EvaluatePermissionRequest>;\n\n/**\n * An individual response from the permission backend.\n *\n * @remarks\n *\n * This response type is an alias of {@link PolicyDecision} to maintain separation between the\n * {@link @backstage/plugin-permission-node#PermissionPolicy} interface and the permission backend\n * api. They may diverge at some point in the future. The response\n *\n * @public\n */\nexport type EvaluatePermissionResponse = PolicyDecision;\n\n/**\n * A batch of responses from the permission backend.\n * @public\n */\nexport type EvaluatePermissionResponseBatch =\n PermissionMessageBatch<EvaluatePermissionResponse>;\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 { Permission, ResourcePermission } from '../types';\n\n/**\n * Check if the two parameters are equivalent permissions.\n * @public\n */\nexport function isPermission<T extends Permission>(\n permission: Permission,\n comparedPermission: T,\n): permission is T {\n return permission.name === comparedPermission.name;\n}\n\n/**\n * Check if a given permission is a {@link ResourcePermission}. When\n * `resourceType` is supplied as the second parameter, also checks if\n * the permission has the specified resource type.\n * @public\n */\nexport function isResourcePermission<T extends string = string>(\n permission: Permission,\n resourceType?: T,\n): permission is ResourcePermission<T> {\n if (!('resourceType' in permission)) {\n return false;\n }\n\n return !resourceType || permission.resourceType === resourceType;\n}\n\n/**\n * Check if a given permission is related to a create action.\n * @public\n */\nexport function isCreatePermission(permission: Permission) {\n return permission.attributes.action === 'create';\n}\n\n/**\n * Check if a given permission is related to a read action.\n * @public\n */\nexport function isReadPermission(permission: Permission) {\n return permission.attributes.action === 'read';\n}\n\n/**\n * Check if a given permission is related to an update action.\n * @public\n */\nexport function isUpdatePermission(permission: Permission) {\n return permission.attributes.action === 'update';\n}\n\n/**\n * Check if a given permission is related to a delete action.\n * @public\n */\nexport function isDeletePermission(permission: Permission) {\n return permission.attributes.action === 'delete';\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 {\n BasicPermission,\n Permission,\n PermissionAttributes,\n ResourcePermission,\n} from '../types';\n\n/**\n * Utility function for creating a valid {@link ResourcePermission}, inferring\n * the appropriate type and resource type parameter.\n *\n * @public\n */\nexport function createPermission<TResourceType extends string>(input: {\n name: string;\n attributes: PermissionAttributes;\n resourceType: TResourceType;\n}): ResourcePermission<TResourceType>;\n/**\n * Utility function for creating a valid {@link BasicPermission}.\n *\n * @public\n */\nexport function createPermission(input: {\n name: string;\n attributes: PermissionAttributes;\n}): BasicPermission;\nexport function createPermission({\n name,\n attributes,\n resourceType,\n}: {\n name: string;\n attributes: PermissionAttributes;\n resourceType?: string;\n}): Permission {\n if (resourceType) {\n return {\n type: 'resource',\n name,\n attributes,\n resourceType,\n };\n }\n\n return {\n type: 'basic',\n name,\n attributes,\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 { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n EvaluatePermissionRequest,\n EvaluatePermissionResponse,\n IdentifiedPermissionMessage,\n PermissionCriteria,\n PermissionCondition,\n EvaluatePermissionResponseBatch,\n EvaluatePermissionRequestBatch,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport {\n PermissionAuthorizer,\n AuthorizeRequestOptions,\n} from './types/permission';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.array(z.unknown()),\n })\n .strict()\n .or(\n z\n .object({ anyOf: z.array(permissionCriteriaSchema).nonempty() })\n .strict(),\n )\n .or(\n z\n .object({ allOf: z.array(permissionCriteriaSchema).nonempty() })\n .strict(),\n )\n .or(z.object({ not: permissionCriteriaSchema }).strict()),\n);\n\nconst responseSchema = z.object({\n items: z.array(\n z\n .object({\n id: z.string(),\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n })\n .or(\n z.object({\n id: z.string(),\n result: z.literal(AuthorizeResult.CONDITIONAL),\n conditions: permissionCriteriaSchema,\n }),\n ),\n ),\n});\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionAuthorizer {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n }\n\n /**\n * Request authorization from the permission-backend for the given set of permissions.\n *\n * Authorization requests check that a given Backstage user can perform a protected operation,\n * potentially for a specific resource (such as a catalog entity). The Backstage identity token\n * should be included in the `options` if available.\n *\n * Permissions can be imported from plugins exposing them, such as `catalogEntityReadPermission`.\n *\n * The response will be either ALLOW or DENY when either the permission has no resourceType, or a\n * resourceRef is provided in the request. For permissions with a resourceType, CONDITIONAL may be\n * returned if no resourceRef is provided in the request. Conditional responses are intended only\n * for backends which have access to the data source for permissioned resources, so that filters\n * can be applied when loading collections of resources.\n * @public\n */\n async authorize(\n queries: EvaluatePermissionRequest[],\n options?: AuthorizeRequestOptions,\n ): Promise<EvaluatePermissionResponse[]> {\n // TODO(permissions): it would be great to provide some kind of typing guarantee that\n // conditional responses will only ever be returned for requests containing a resourceType\n // but no resourceRef. That way clients who aren't prepared to handle filtering according\n // to conditions can be guaranteed that they won't unexpectedly get a CONDITIONAL response.\n\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW }));\n }\n\n const request: EvaluatePermissionRequestBatch = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n this.assertValidResponse(request, responseBody);\n\n const responsesById = responseBody.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, IdentifiedPermissionMessage<EvaluatePermissionResponse>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n private assertValidResponse(\n request: EvaluatePermissionRequestBatch,\n json: any,\n ): asserts json is EvaluatePermissionResponseBatch {\n const authorizedResponses = responseSchema.parse(json);\n const responseIds = authorizedResponses.items.map(r => r.id);\n const hasAllRequestIds = request.items.every(r =>\n responseIds.includes(r.id),\n );\n if (!hasAllRequestIds) {\n throw new Error(\n 'Unexpected authorization response from permission-backend',\n );\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/types/api.ts","../src/permissions/util.ts","../src/permissions/createPermission.ts","../src/PermissionClient.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 { ResourcePermission } from '.';\nimport { Permission } from './permission';\n\n/**\n * A request with a UUID identifier, so that batched responses can be matched up with the original\n * requests.\n * @public\n */\nexport type IdentifiedPermissionMessage<T> = T & { id: string };\n\n/**\n * A batch of request or response items.\n * @public\n */\nexport type PermissionMessageBatch<T> = {\n items: IdentifiedPermissionMessage<T>[];\n};\n\n/**\n * The result of an authorization request.\n * @public\n */\nexport enum AuthorizeResult {\n /**\n * The authorization request is denied.\n */\n DENY = 'DENY',\n /**\n * The authorization request is allowed.\n */\n ALLOW = 'ALLOW',\n /**\n * The authorization request is allowed if the provided conditions are met.\n */\n CONDITIONAL = 'CONDITIONAL',\n}\n\n/**\n * A definitive decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy unconditionally allows (or denies) the request.\n *\n * @public\n */\nexport type DefinitivePolicyDecision = {\n result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;\n};\n\n/**\n * A conditional decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @remarks\n *\n * This indicates that the policy allows authorization for the request, given that the returned\n * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin\n * which knows about the referenced permission rules.\n *\n * @public\n */\nexport type ConditionalPolicyDecision = {\n result: AuthorizeResult.CONDITIONAL;\n pluginId: string;\n resourceType: string;\n conditions: PermissionCriteria<PermissionCondition>;\n};\n\n/**\n * A decision returned by the {@link @backstage/plugin-permission-node#PermissionPolicy}.\n *\n * @public\n */\nexport type PolicyDecision =\n | DefinitivePolicyDecision\n | ConditionalPolicyDecision;\n\n/**\n * A condition returned with a CONDITIONAL authorization response.\n *\n * Conditions are a reference to a rule defined by a plugin, and parameters to apply the rule. For\n * example, a rule might be `isOwner` from the catalog-backend, and params may be a list of entity\n * claims from a identity token.\n * @public\n */\nexport type PermissionCondition<\n TResourceType extends string = string,\n TParams extends unknown[] = unknown[],\n> = {\n resourceType: TResourceType;\n rule: string;\n params: TParams;\n};\n\n/**\n * Utility type to represent an array with 1 or more elements.\n * @ignore\n */\ntype NonEmptyArray<T> = [T, ...T[]];\n\n/**\n * Represents a logical AND for the provided criteria.\n * @public\n */\nexport type AllOfCriteria<TQuery> = {\n allOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a logical OR for the provided criteria.\n * @public\n */\nexport type AnyOfCriteria<TQuery> = {\n anyOf: NonEmptyArray<PermissionCriteria<TQuery>>;\n};\n\n/**\n * Represents a negation of the provided criteria.\n * @public\n */\nexport type NotCriteria<TQuery> = {\n not: PermissionCriteria<TQuery>;\n};\n\n/**\n * Composes several {@link PermissionCondition}s as criteria with a nested AND/OR structure.\n * @public\n */\nexport type PermissionCriteria<TQuery> =\n | AllOfCriteria<TQuery>\n | AnyOfCriteria<TQuery>\n | NotCriteria<TQuery>\n | TQuery;\n\n/**\n * An individual request sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequest = {\n permission: Permission;\n resourceRef?: string;\n};\n\n/**\n * A batch of requests sent to the permission backend.\n * @public\n */\nexport type EvaluatePermissionRequestBatch =\n PermissionMessageBatch<EvaluatePermissionRequest>;\n\n/**\n * An individual response from the permission backend.\n *\n * @remarks\n *\n * This response type is an alias of {@link PolicyDecision} to maintain separation between the\n * {@link @backstage/plugin-permission-node#PermissionPolicy} interface and the permission backend\n * api. They may diverge at some point in the future. The response\n *\n * @public\n */\nexport type EvaluatePermissionResponse = PolicyDecision;\n\n/**\n * A batch of responses from the permission backend.\n * @public\n */\nexport type EvaluatePermissionResponseBatch =\n PermissionMessageBatch<EvaluatePermissionResponse>;\n\n/**\n * Request object for {@link PermissionEvaluator.authorize}. If a {@link ResourcePermission}\n * is provided, it must include a corresponding `resourceRef`.\n * @public\n */\nexport type AuthorizePermissionRequest =\n | {\n permission: Exclude<Permission, ResourcePermission>;\n resourceRef?: never;\n }\n | { permission: ResourcePermission; resourceRef: string };\n\n/**\n * Response object for {@link PermissionEvaluator.authorize}.\n * @public\n */\nexport type AuthorizePermissionResponse = DefinitivePolicyDecision;\n\n/**\n * Request object for {@link PermissionEvaluator.authorizeConditional}.\n * @public\n */\nexport type QueryPermissionRequest = {\n permission: ResourcePermission;\n resourceRef?: never;\n};\n\n/**\n * Response object for {@link PermissionEvaluator.authorizeConditional}.\n * @public\n */\nexport type QueryPermissionResponse = PolicyDecision;\n\n/**\n * A client interacting with the permission backend can implement this evaluator interface.\n *\n * @public\n */\nexport interface PermissionEvaluator {\n /**\n * Evaluates {@link Permission | Permissions} and returns a definitive decision.\n */\n authorize(\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]>;\n\n /**\n * Evaluates {@link ResourcePermission | ResourcePermissions} and returns both definitive and\n * conditional decisions, depending on the configured\n * {@link @backstage/plugin-permission-node#PermissionPolicy}. This method is useful when the\n * caller needs more control over the processing of conditional decisions. For example, a plugin\n * backend may want to use {@link PermissionCriteria | conditions} in a database query instead of\n * evaluating each resource in memory.\n */\n authorizeConditional(\n requests: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<QueryPermissionResponse[]>;\n}\n\n/**\n * Options for {@link PermissionEvaluator} requests.\n * The Backstage identity token should be defined if available.\n * @public\n */\nexport type EvaluatorRequestOptions = {\n token?: string;\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 AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n DefinitivePolicyDecision,\n EvaluatorRequestOptions,\n Permission,\n PermissionAuthorizer,\n PermissionEvaluator,\n QueryPermissionRequest,\n QueryPermissionResponse,\n ResourcePermission,\n} from '../types';\n\n/**\n * Check if the two parameters are equivalent permissions.\n * @public\n */\nexport function isPermission<T extends Permission>(\n permission: Permission,\n comparedPermission: T,\n): permission is T {\n return permission.name === comparedPermission.name;\n}\n\n/**\n * Check if a given permission is a {@link ResourcePermission}. When\n * `resourceType` is supplied as the second parameter, also checks if\n * the permission has the specified resource type.\n * @public\n */\nexport function isResourcePermission<T extends string = string>(\n permission: Permission,\n resourceType?: T,\n): permission is ResourcePermission<T> {\n if (!('resourceType' in permission)) {\n return false;\n }\n\n return !resourceType || permission.resourceType === resourceType;\n}\n\n/**\n * Check if a given permission is related to a create action.\n * @public\n */\nexport function isCreatePermission(permission: Permission) {\n return permission.attributes.action === 'create';\n}\n\n/**\n * Check if a given permission is related to a read action.\n * @public\n */\nexport function isReadPermission(permission: Permission) {\n return permission.attributes.action === 'read';\n}\n\n/**\n * Check if a given permission is related to an update action.\n * @public\n */\nexport function isUpdatePermission(permission: Permission) {\n return permission.attributes.action === 'update';\n}\n\n/**\n * Check if a given permission is related to a delete action.\n * @public\n */\nexport function isDeletePermission(permission: Permission) {\n return permission.attributes.action === 'delete';\n}\n\n/**\n * Convert {@link PermissionAuthorizer} to {@link PermissionEvaluator}.\n *\n * @public\n */\nexport function toPermissionEvaluator(\n permissionAuthorizer: PermissionAuthorizer,\n): PermissionEvaluator {\n return {\n authorize: async (\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> => {\n const response = await permissionAuthorizer.authorize(requests, options);\n\n return response as DefinitivePolicyDecision[];\n },\n authorizeConditional(\n requests: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n const parsedRequests =\n requests as unknown as AuthorizePermissionRequest[];\n return permissionAuthorizer.authorize(parsedRequests, options);\n },\n };\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 {\n BasicPermission,\n Permission,\n PermissionAttributes,\n ResourcePermission,\n} from '../types';\n\n/**\n * Utility function for creating a valid {@link ResourcePermission}, inferring\n * the appropriate type and resource type parameter.\n *\n * @public\n */\nexport function createPermission<TResourceType extends string>(input: {\n name: string;\n attributes: PermissionAttributes;\n resourceType: TResourceType;\n}): ResourcePermission<TResourceType>;\n/**\n * Utility function for creating a valid {@link BasicPermission}.\n *\n * @public\n */\nexport function createPermission(input: {\n name: string;\n attributes: PermissionAttributes;\n}): BasicPermission;\nexport function createPermission({\n name,\n attributes,\n resourceType,\n}: {\n name: string;\n attributes: PermissionAttributes;\n resourceType?: string;\n}): Permission {\n if (resourceType) {\n return {\n type: 'resource',\n name,\n attributes,\n resourceType,\n };\n }\n\n return {\n type: 'basic',\n name,\n attributes,\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 { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionMessageBatch,\n PermissionCriteria,\n PermissionCondition,\n PermissionEvaluator,\n QueryPermissionRequest,\n AuthorizePermissionRequest,\n EvaluatorRequestOptions,\n AuthorizePermissionResponse,\n QueryPermissionResponse,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport { AuthorizeRequestOptions } from './types/permission';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.array(z.unknown()),\n })\n .or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ not: permissionCriteriaSchema })),\n);\n\nconst authorizePermissionResponseSchema: z.ZodSchema<AuthorizePermissionResponse> =\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n });\n\nconst queryPermissionResponseSchema: z.ZodSchema<QueryPermissionResponse> =\n z.union([\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n z.object({\n result: z.literal(AuthorizeResult.CONDITIONAL),\n pluginId: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ]);\n\nconst responseSchema = <T>(\n itemSchema: z.ZodSchema<T>,\n ids: Set<string>,\n): z.ZodSchema<PermissionMessageBatch<T>> =>\n z.object({\n items: z\n .array(\n z.intersection(\n z.object({\n id: z.string(),\n }),\n itemSchema,\n ),\n )\n .refine(\n items =>\n items.length === ids.size && items.every(({ id }) => ids.has(id)),\n {\n message: 'Items in response do not match request',\n },\n ),\n });\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionEvaluator {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorize}\n */\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n return this.makeRequest(\n requests,\n authorizePermissionResponseSchema,\n options,\n );\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorizeConditional}\n */\n async authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n return this.makeRequest(queries, queryPermissionResponseSchema, options);\n }\n\n private async makeRequest<TQuery, TResult>(\n queries: TQuery[],\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n const request: PermissionMessageBatch<TQuery> = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n\n const parsedResponse = responseSchema(\n itemSchema,\n new Set(request.items.map(({ id }) => id)),\n ).parse(responseBody);\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, z.infer<typeof itemSchema>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-permission-common",
3
3
  "description": "Isomorphic types and client for Backstage permissions and authorization",
4
- "version": "0.6.0-next.0",
4
+ "version": "0.6.1-next.0",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "publishConfig": {
@@ -41,17 +41,17 @@
41
41
  "url": "https://github.com/backstage/backstage/issues"
42
42
  },
43
43
  "dependencies": {
44
- "@backstage/config": "^1.0.0",
44
+ "@backstage/config": "^1.0.1-next.0",
45
45
  "@backstage/errors": "^1.0.0",
46
46
  "cross-fetch": "^3.1.5",
47
47
  "uuid": "^8.0.0",
48
48
  "zod": "^3.11.6"
49
49
  },
50
50
  "devDependencies": {
51
- "@backstage/cli": "^0.17.0-next.1",
51
+ "@backstage/cli": "^0.17.1-next.2",
52
52
  "@types/jest": "^26.0.7",
53
53
  "msw": "^0.35.0"
54
54
  },
55
- "gitHead": "57d12dcc35aeb6c33b09e51d1efc3408c574f109",
55
+ "gitHead": "cfbf5762d7d91eee18999306b21d63840400ee29",
56
56
  "module": "dist/index.esm.js"
57
57
  }