@backstage/plugin-permission-node 0.5.6-next.0 → 0.6.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 +168 -0
- package/dist/index.cjs.js +15 -10
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +74 -100
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,173 @@
|
|
|
1
1
|
# @backstage/plugin-permission-node
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8012ac46a0: **BREAKING**: Stronger typing in `PermissionPolicy` 🎉.
|
|
8
|
+
|
|
9
|
+
Previously, it was entirely the responsibility of the `PermissionPolicy` author to only return `CONDITIONAL` decisions for permissions that are associated with a resource, and to return the correct kind of `PermissionCondition` instances inside the decision. Now, the policy authoring helpers provided in this package now ensure that the decision and permission match.
|
|
10
|
+
|
|
11
|
+
**For policy authors**: rename and adjust api of `createConditionExports`. Previously, the function returned a factory for creating conditional decisions named `createPolicyDecision`, which had a couple of drawbacks:
|
|
12
|
+
|
|
13
|
+
1. The function always creates a _conditional_ policy decision, but this was not reflected in the name.
|
|
14
|
+
2. Conditional decisions should only ever be returned from `PermissionPolicy#handle` for resource permissions, but there was nothing in the API that encoded this constraint.
|
|
15
|
+
|
|
16
|
+
This change addresses the drawbacks above by making the following changes for policy authors:
|
|
17
|
+
|
|
18
|
+
- The `createPolicyDecision` method has been renamed to `createConditionalDecision`.
|
|
19
|
+
- Along with conditions, the method now accepts a permission, which must be a `ResourcePermission`. This is expected to be the handled permission in `PermissionPolicy#handle`, whose type must first be narrowed using methods like `isPermission` and `isResourcePermission`:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
class TestPermissionPolicy implements PermissionPolicy {
|
|
23
|
+
async handle(
|
|
24
|
+
request: PolicyQuery<Permission>,
|
|
25
|
+
_user?: BackstageIdentityResponse,
|
|
26
|
+
): Promise<PolicyDecision> {
|
|
27
|
+
if (
|
|
28
|
+
// Narrow type of `request.permission` to `ResourcePermission<'catalog-entity'>
|
|
29
|
+
isResourcePermission(request.permission, RESOURCE_TYPE_CATALOG_ENTITY)
|
|
30
|
+
) {
|
|
31
|
+
return createCatalogConditionalDecision(
|
|
32
|
+
request.permission,
|
|
33
|
+
catalogConditions.isEntityOwner(
|
|
34
|
+
_user?.identity.ownershipEntityRefs ?? [],
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
result: AuthorizeResult.ALLOW,
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**BREAKING**: when creating `PermissionRule`s, provide a `resourceType`.
|
|
45
|
+
|
|
46
|
+
```diff
|
|
47
|
+
export const isEntityOwner = createCatalogPermissionRule({
|
|
48
|
+
name: 'IS_ENTITY_OWNER',
|
|
49
|
+
description: 'Allow entities owned by the current user',
|
|
50
|
+
+ resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
|
51
|
+
apply: (resource: Entity, claims: string[]) => {
|
|
52
|
+
if (!resource.relations) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return resource.relations
|
|
57
|
+
.filter(relation => relation.type === RELATION_OWNED_BY)
|
|
58
|
+
.some(relation => claims.includes(relation.targetRef));
|
|
59
|
+
},
|
|
60
|
+
toQuery: (claims: string[]) => ({
|
|
61
|
+
key: 'relations.ownedBy',
|
|
62
|
+
values: claims,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- c98d271466: **BREAKING:**
|
|
68
|
+
|
|
69
|
+
- Rename `PolicyAuthorizeQuery` to `PolicyQuery`
|
|
70
|
+
- Remove `PolicyDecision`, `DefinitivePolicyDecision`, and `ConditionalPolicyDecision`. These types are now exported from `@backstage/plugin-permission-common`
|
|
71
|
+
|
|
72
|
+
- 322b69e46a: **BREAKING:** `ServerPermissionClient` now implements `PermissionEvaluator`, which moves out the capabilities for evaluating conditional decisions from `authorize()` to `authorizeConditional()` method.
|
|
73
|
+
|
|
74
|
+
### Patch Changes
|
|
75
|
+
|
|
76
|
+
- 90754d4fa9: Removed [strict](https://github.com/colinhacks/zod#strict) validation from `PermissionCriteria` schemas to support backward-compatible changes.
|
|
77
|
+
- 8012ac46a0: Fix signature of permission rule in test suites
|
|
78
|
+
- Updated dependencies
|
|
79
|
+
- @backstage/plugin-permission-common@0.6.0
|
|
80
|
+
- @backstage/plugin-auth-node@0.2.0
|
|
81
|
+
- @backstage/backend-common@0.13.2
|
|
82
|
+
|
|
83
|
+
## 0.6.0-next.2
|
|
84
|
+
|
|
85
|
+
### Minor Changes
|
|
86
|
+
|
|
87
|
+
- 322b69e46a: **BREAKING:** `ServerPermissionClient` now implements `PermissionEvaluator`, which moves out the capabilities for evaluating conditional decisions from `authorize()` to `authorizeConditional()` method.
|
|
88
|
+
|
|
89
|
+
### Patch Changes
|
|
90
|
+
|
|
91
|
+
- Updated dependencies
|
|
92
|
+
- @backstage/plugin-permission-common@0.6.0-next.1
|
|
93
|
+
- @backstage/backend-common@0.13.2-next.2
|
|
94
|
+
|
|
95
|
+
## 0.6.0-next.1
|
|
96
|
+
|
|
97
|
+
### Minor Changes
|
|
98
|
+
|
|
99
|
+
- 8012ac46a0: **BREAKING**: Stronger typing in `PermissionPolicy` 🎉.
|
|
100
|
+
|
|
101
|
+
Previously, it was entirely the responsibility of the `PermissionPolicy` author to only return `CONDITIONAL` decisions for permissions that are associated with a resource, and to return the correct kind of `PermissionCondition` instances inside the decision. Now, the policy authoring helpers provided in this package now ensure that the decision and permission match.
|
|
102
|
+
|
|
103
|
+
**For policy authors**: rename and adjust api of `createConditionExports`. Previously, the function returned a factory for creating conditional decisions named `createPolicyDecision`, which had a couple of drawbacks:
|
|
104
|
+
|
|
105
|
+
1. The function always creates a _conditional_ policy decision, but this was not reflected in the name.
|
|
106
|
+
2. Conditional decisions should only ever be returned from `PermissionPolicy#handle` for resource permissions, but there was nothing in the API that encoded this constraint.
|
|
107
|
+
|
|
108
|
+
This change addresses the drawbacks above by making the following changes for policy authors:
|
|
109
|
+
|
|
110
|
+
- The `createPolicyDecision` method has been renamed to `createConditionalDecision`.
|
|
111
|
+
- Along with conditions, the method now accepts a permission, which must be a `ResourcePermission`. This is expected to be the handled permission in `PermissionPolicy#handle`, whose type must first be narrowed using methods like `isPermission` and `isResourcePermission`:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
class TestPermissionPolicy implements PermissionPolicy {
|
|
115
|
+
async handle(
|
|
116
|
+
request: PolicyQuery<Permission>,
|
|
117
|
+
_user?: BackstageIdentityResponse,
|
|
118
|
+
): Promise<PolicyDecision> {
|
|
119
|
+
if (
|
|
120
|
+
// Narrow type of `request.permission` to `ResourcePermission<'catalog-entity'>
|
|
121
|
+
isResourcePermission(request.permission, RESOURCE_TYPE_CATALOG_ENTITY)
|
|
122
|
+
) {
|
|
123
|
+
return createCatalogConditionalDecision(
|
|
124
|
+
request.permission,
|
|
125
|
+
catalogConditions.isEntityOwner(
|
|
126
|
+
_user?.identity.ownershipEntityRefs ?? [],
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
result: AuthorizeResult.ALLOW,
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**BREAKING**: when creating `PermissionRule`s, provide a `resourceType`.
|
|
137
|
+
|
|
138
|
+
```diff
|
|
139
|
+
export const isEntityOwner = createCatalogPermissionRule({
|
|
140
|
+
name: 'IS_ENTITY_OWNER',
|
|
141
|
+
description: 'Allow entities owned by the current user',
|
|
142
|
+
+ resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
|
143
|
+
apply: (resource: Entity, claims: string[]) => {
|
|
144
|
+
if (!resource.relations) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return resource.relations
|
|
149
|
+
.filter(relation => relation.type === RELATION_OWNED_BY)
|
|
150
|
+
.some(relation => claims.includes(relation.targetRef));
|
|
151
|
+
},
|
|
152
|
+
toQuery: (claims: string[]) => ({
|
|
153
|
+
key: 'relations.ownedBy',
|
|
154
|
+
values: claims,
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- c98d271466: **BREAKING:**
|
|
160
|
+
|
|
161
|
+
- Rename `PolicyAuthorizeQuery` to `PolicyQuery`
|
|
162
|
+
- Remove `PolicyDecision`, `DefinitivePolicyDecision`, and `ConditionalPolicyDecision`. These types are now exported from `@backstage/plugin-permission-common`
|
|
163
|
+
|
|
164
|
+
### Patch Changes
|
|
165
|
+
|
|
166
|
+
- 8012ac46a0: Fix signature of permission rule in test suites
|
|
167
|
+
- Updated dependencies
|
|
168
|
+
- @backstage/plugin-permission-common@0.6.0-next.0
|
|
169
|
+
- @backstage/backend-common@0.13.2-next.1
|
|
170
|
+
|
|
3
171
|
## 0.5.6-next.0
|
|
4
172
|
|
|
5
173
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -16,6 +16,7 @@ var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
|
16
16
|
|
|
17
17
|
const createConditionFactory = (rule) => (...params) => ({
|
|
18
18
|
rule: rule.name,
|
|
19
|
+
resourceType: rule.resourceType,
|
|
19
20
|
params
|
|
20
21
|
});
|
|
21
22
|
|
|
@@ -26,7 +27,7 @@ const createConditionExports = (options) => {
|
|
|
26
27
|
...acc,
|
|
27
28
|
[key]: createConditionFactory(rule)
|
|
28
29
|
}), {}),
|
|
29
|
-
|
|
30
|
+
createConditionalDecision: (_permission, conditions) => ({
|
|
30
31
|
result: pluginPermissionCommon.AuthorizeResult.CONDITIONAL,
|
|
31
32
|
pluginId,
|
|
32
33
|
resourceType,
|
|
@@ -71,13 +72,14 @@ const createConditionTransformer = (permissionRules) => {
|
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
const permissionCriteriaSchema = zod.z.lazy(() => zod.z.union([
|
|
74
|
-
zod.z.object({ anyOf: zod.z.array(permissionCriteriaSchema).nonempty() })
|
|
75
|
-
zod.z.object({ allOf: zod.z.array(permissionCriteriaSchema).nonempty() })
|
|
76
|
-
zod.z.object({ not: permissionCriteriaSchema })
|
|
75
|
+
zod.z.object({ anyOf: zod.z.array(permissionCriteriaSchema).nonempty() }),
|
|
76
|
+
zod.z.object({ allOf: zod.z.array(permissionCriteriaSchema).nonempty() }),
|
|
77
|
+
zod.z.object({ not: permissionCriteriaSchema }),
|
|
77
78
|
zod.z.object({
|
|
78
79
|
rule: zod.z.string(),
|
|
80
|
+
resourceType: zod.z.string(),
|
|
79
81
|
params: zod.z.array(zod.z.unknown())
|
|
80
|
-
})
|
|
82
|
+
})
|
|
81
83
|
]));
|
|
82
84
|
const applyConditionsRequestSchema = zod.z.object({
|
|
83
85
|
items: zod.z.array(zod.z.object({
|
|
@@ -158,11 +160,11 @@ class ServerPermissionClient {
|
|
|
158
160
|
this.tokenManager = options.tokenManager;
|
|
159
161
|
this.permissionEnabled = options.permissionEnabled;
|
|
160
162
|
}
|
|
161
|
-
async
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return this.permissionClient.authorize(
|
|
163
|
+
async authorizeConditional(queries, options) {
|
|
164
|
+
return await this.isEnabled(options == null ? void 0 : options.token) ? this.permissionClient.authorizeConditional(queries, options) : queries.map((_) => ({ result: pluginPermissionCommon.AuthorizeResult.ALLOW }));
|
|
165
|
+
}
|
|
166
|
+
async authorize(requests, options) {
|
|
167
|
+
return await this.isEnabled(options == null ? void 0 : options.token) ? this.permissionClient.authorize(requests, options) : requests.map((_) => ({ result: pluginPermissionCommon.AuthorizeResult.ALLOW }));
|
|
166
168
|
}
|
|
167
169
|
async isValidServerToken(token) {
|
|
168
170
|
if (!token) {
|
|
@@ -170,6 +172,9 @@ class ServerPermissionClient {
|
|
|
170
172
|
}
|
|
171
173
|
return this.tokenManager.authenticate(token).then(() => true).catch(() => false);
|
|
172
174
|
}
|
|
175
|
+
async isEnabled(token) {
|
|
176
|
+
return this.permissionEnabled && !await this.isValidServerToken(token);
|
|
177
|
+
}
|
|
173
178
|
}
|
|
174
179
|
|
|
175
180
|
exports.ServerPermissionClient = ServerPermissionClient;
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -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/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 {\n AllOfCriteria,\n AnyOfCriteria,\n NotCriteria,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\n\n/**\n * Utility function used to parse a PermissionCriteria\n * @param criteria - a PermissionCriteria\n * @alpha\n *\n * @returns `true` if the permission criteria is of type allOf,\n * narrowing down `criteria` to the specific type.\n */\nexport const isAndCriteria = <T>(\n criteria: PermissionCriteria<T>,\n): criteria is AllOfCriteria<T> =>\n Object.prototype.hasOwnProperty.call(criteria, 'allOf');\n\n/**\n * Utility function used to parse a PermissionCriteria of type\n * @param criteria - a PermissionCriteria\n * @alpha\n *\n * @returns `true` if the permission criteria is of type anyOf,\n * narrowing down `criteria` to the specific type.\n */\nexport const isOrCriteria = <T>(\n criteria: PermissionCriteria<T>,\n): criteria is AnyOfCriteria<T> =>\n Object.prototype.hasOwnProperty.call(criteria, 'anyOf');\n\n/**\n * Utility function used to parse a PermissionCriteria\n * @param criteria - a PermissionCriteria\n * @alpha\n *\n * @returns `true` if the permission criteria is of type not,\n * narrowing down `criteria` to the specific type.\n */\nexport const isNotCriteria = <T>(\n criteria: PermissionCriteria<T>,\n): criteria is NotCriteria<T> =>\n Object.prototype.hasOwnProperty.call(criteria, '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 AllOfCriteria,\n AnyOfCriteria,\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 } as AllOfCriteria<TQuery>;\n } else if (isOrCriteria(criteria)) {\n return {\n anyOf: criteria.anyOf.map(child => mapConditions(child, getRule)),\n } as AnyOfCriteria<TQuery>;\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).nonempty() }).strict(),\n z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }).strict(),\n z.object({ not: permissionCriteriaSchema }).strict(),\n z\n .object({\n rule: z.string(),\n params: z.array(z.unknown()),\n })\n .strict(),\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 'Backend-to-backend authentication must be configured before enabling permissions. Read more here https://backstage.io/docs/tutorials/backend-to-backend-auth',\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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"}
|
|
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 { PermissionCondition } from '@backstage/plugin-permission-common';\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 <TResourceType extends string, TParams extends any[]>(\n rule: PermissionRule<unknown, unknown, TResourceType, TParams>,\n ) =>\n (...params: TParams): PermissionCondition<TResourceType, TParams> => ({\n rule: rule.name,\n resourceType: rule.resourceType,\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 ConditionalPolicyDecision,\n PermissionCondition,\n PermissionCriteria,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\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 TResourceType,\n infer TParams\n>\n ? (...params: TParams) => PermissionCondition<TResourceType, 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, 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\n * the built-in {@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\n * each of the supplied {@link PermissionRule}s, along with a\n * `createConditionalDecision` function which builds the wrapper object needed\n * to enclose conditions when authoring {@link PermissionPolicy}\n * implementations.\n *\n * Plugin authors should generally call this method with all the built-in\n * {@link PermissionRule}s the plugin supports, and export the resulting\n * `conditions` object and `createConditionalDecision` function so that they can\n * be used by {@link PermissionPolicy} authors.\n *\n * @public\n */\nexport const createConditionExports = <\n TResourceType extends string,\n TResource,\n TRules extends Record<string, PermissionRule<TResource, any, TResourceType>>,\n>(options: {\n pluginId: string;\n resourceType: TResourceType;\n rules: TRules;\n}): {\n conditions: Conditions<TRules>;\n createConditionalDecision: (\n permission: ResourcePermission<TResourceType>,\n conditions: PermissionCriteria<PermissionCondition<TResourceType>>,\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 createConditionalDecision: (\n _permission: ResourcePermission<TResourceType>,\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 {\n AllOfCriteria,\n AnyOfCriteria,\n NotCriteria,\n PermissionCriteria,\n} from '@backstage/plugin-permission-common';\nimport { PermissionRule } from '../types';\n\n/**\n * Utility function used to parse a PermissionCriteria\n * @param criteria - a PermissionCriteria\n * @alpha\n *\n * @returns `true` if the permission criteria is of type allOf,\n * narrowing down `criteria` to the specific type.\n */\nexport const isAndCriteria = <T>(\n criteria: PermissionCriteria<T>,\n): criteria is AllOfCriteria<T> =>\n Object.prototype.hasOwnProperty.call(criteria, 'allOf');\n\n/**\n * Utility function used to parse a PermissionCriteria of type\n * @param criteria - a PermissionCriteria\n * @alpha\n *\n * @returns `true` if the permission criteria is of type anyOf,\n * narrowing down `criteria` to the specific type.\n */\nexport const isOrCriteria = <T>(\n criteria: PermissionCriteria<T>,\n): criteria is AnyOfCriteria<T> =>\n Object.prototype.hasOwnProperty.call(criteria, 'anyOf');\n\n/**\n * Utility function used to parse a PermissionCriteria\n * @param criteria - a PermissionCriteria\n * @alpha\n *\n * @returns `true` if the permission criteria is of type not,\n * narrowing down `criteria` to the specific type.\n */\nexport const isNotCriteria = <T>(\n criteria: PermissionCriteria<T>,\n): criteria is NotCriteria<T> =>\n Object.prototype.hasOwnProperty.call(criteria, 'not');\n\nexport const createGetRule = <TResource, TQuery>(\n rules: PermissionRule<TResource, TQuery, string>[],\n) => {\n const rulesMap = new Map(Object.values(rules).map(rule => [rule.name, rule]));\n\n return (name: string): PermissionRule<TResource, TQuery, string> => {\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 AllOfCriteria,\n AnyOfCriteria,\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, string>,\n): PermissionCriteria<TQuery> => {\n if (isAndCriteria(criteria)) {\n return {\n allOf: criteria.allOf.map(child => mapConditions(child, getRule)),\n } as AllOfCriteria<TQuery>;\n } else if (isOrCriteria(criteria)) {\n return {\n anyOf: criteria.anyOf.map(child => mapConditions(child, getRule)),\n } as AnyOfCriteria<TQuery>;\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, string>[],\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 DefinitivePolicyDecision,\n IdentifiedPermissionMessage,\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).nonempty() }),\n z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }),\n z.object({ not: permissionCriteriaSchema }),\n z.object({\n rule: z.string(),\n resourceType: 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 = IdentifiedPermissionMessage<{\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 =\n IdentifiedPermissionMessage<DefinitivePolicyDecision>;\n\n/**\n * A batch of {@link ApplyConditionsResponseEntry} objects.\n *\n * @public\n */\nexport type ApplyConditionsResponse = {\n items: ApplyConditionsResponseEntry[];\n};\n\nconst applyConditions = <TResourceType extends string, TResource>(\n criteria: PermissionCriteria<PermissionCondition<TResourceType>>,\n resource: TResource | undefined,\n getRule: (name: string) => PermissionRule<TResource, unknown, TResourceType>,\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 * Prevent use of type parameter from contributing to type inference.\n *\n * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795\n * @ignore\n */\ntype NoInfer<T> = T extends infer S ? S : never;\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 = <\n TResourceType extends string,\n TResource,\n>(options: {\n resourceType: TResourceType;\n // Do not infer value of TResourceType from supplied rules.\n // instead only consider the resourceType parameter, and\n // consider any rules whose resource type does not match\n // to be an error.\n rules: PermissionRule<TResource, any, NoInfer<TResourceType>>[];\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 TResourceType extends string,\n TParams extends unknown[],\n>(\n rule: PermissionRule<TResource, TQuery, TResourceType, 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, TResourceType extends string>() =>\n <TParams extends unknown[]>(\n rule: PermissionRule<TResource, TQuery, TResourceType, 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 AuthorizeResult,\n PermissionClient,\n PermissionEvaluator,\n AuthorizePermissionRequest,\n EvaluatorRequestOptions,\n AuthorizePermissionResponse,\n PolicyDecision,\n QueryPermissionRequest,\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 PermissionEvaluator {\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 'Backend-to-backend authentication must be configured before enabling permissions. Read more here https://backstage.io/docs/tutorials/backend-to-backend-auth',\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 authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<PolicyDecision[]> {\n return (await this.isEnabled(options?.token))\n ? this.permissionClient.authorizeConditional(queries, options)\n : queries.map(_ => ({ result: AuthorizeResult.ALLOW }));\n }\n\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: EvaluatorRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n return (await this.isEnabled(options?.token))\n ? this.permissionClient.authorize(requests, options)\n : requests.map(_ => ({ result: AuthorizeResult.ALLOW }));\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 private async isEnabled(token?: string) {\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 return this.permissionEnabled && !(await this.isValidServerToken(token));\n }\n}\n"],"names":["AuthorizeResult","z","Router","InputError","express","errorHandler","PermissionClient"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PermissionCriteria,
|
|
2
|
-
import { BackstageIdentityResponse } from '@backstage/plugin-auth-node';
|
|
1
|
+
import { PermissionCriteria, PermissionCondition, ResourcePermission, ConditionalPolicyDecision, IdentifiedPermissionMessage, DefinitivePolicyDecision, AllOfCriteria, AnyOfCriteria, NotCriteria, Permission, PolicyDecision, PermissionEvaluator, QueryPermissionRequest, EvaluatorRequestOptions, AuthorizePermissionRequest, AuthorizePermissionResponse } from '@backstage/plugin-permission-common';
|
|
3
2
|
import express from 'express';
|
|
3
|
+
import { BackstageIdentityResponse } from '@backstage/plugin-auth-node';
|
|
4
4
|
import { PluginEndpointDiscovery, TokenManager } from '@backstage/backend-common';
|
|
5
5
|
import { Config } from '@backstage/config';
|
|
6
6
|
|
|
@@ -20,9 +20,10 @@ import { Config } from '@backstage/config';
|
|
|
20
20
|
*
|
|
21
21
|
* @public
|
|
22
22
|
*/
|
|
23
|
-
declare type PermissionRule<TResource, TQuery, TParams extends unknown[] = unknown[]> = {
|
|
23
|
+
declare type PermissionRule<TResource, TQuery, TResourceType extends string, TParams extends unknown[] = unknown[]> = {
|
|
24
24
|
name: string;
|
|
25
25
|
description: string;
|
|
26
|
+
resourceType: TResourceType;
|
|
26
27
|
/**
|
|
27
28
|
* Apply this rule to a resource already loaded from a backing data source. The params are
|
|
28
29
|
* arguments supplied for the rule; for example, a rule could be `isOwner` with entityRefs as the
|
|
@@ -53,79 +54,7 @@ declare type PermissionRule<TResource, TQuery, TParams extends unknown[] = unkno
|
|
|
53
54
|
*
|
|
54
55
|
* @public
|
|
55
56
|
*/
|
|
56
|
-
declare const createConditionFactory: <TParams extends any[]>(rule: PermissionRule<unknown, unknown, TParams>) => (...params: TParams) =>
|
|
57
|
-
rule: string;
|
|
58
|
-
params: TParams;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* An authorization request to be evaluated by the {@link PermissionPolicy}.
|
|
63
|
-
*
|
|
64
|
-
* @remarks
|
|
65
|
-
*
|
|
66
|
-
* This differs from {@link @backstage/permission-common#AuthorizeQuery} in that `resourceRef`
|
|
67
|
-
* should never be provided. This forces policies to be written in a way that's compatible with
|
|
68
|
-
* filtering collections of resources at data load time.
|
|
69
|
-
*
|
|
70
|
-
* @public
|
|
71
|
-
*/
|
|
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
|
-
};
|
|
85
|
-
/**
|
|
86
|
-
* A conditional result to an authorization request, returned by the {@link PermissionPolicy}.
|
|
87
|
-
*
|
|
88
|
-
* @remarks
|
|
89
|
-
*
|
|
90
|
-
* This indicates that the policy allows authorization for the request, given that the returned
|
|
91
|
-
* conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin
|
|
92
|
-
* which knows about the referenced permission rules.
|
|
93
|
-
*
|
|
94
|
-
* Similar to {@link @backstage/permission-common#AuthorizeDecision}, but with the plugin and resource
|
|
95
|
-
* identifiers needed to evaluate the returned conditions.
|
|
96
|
-
* @public
|
|
97
|
-
*/
|
|
98
|
-
declare type ConditionalPolicyDecision = {
|
|
99
|
-
result: AuthorizeResult.CONDITIONAL;
|
|
100
|
-
pluginId: string;
|
|
101
|
-
resourceType: string;
|
|
102
|
-
conditions: PermissionCriteria<PermissionCondition>;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* The result of evaluating an authorization request with a {@link PermissionPolicy}.
|
|
106
|
-
*
|
|
107
|
-
* @public
|
|
108
|
-
*/
|
|
109
|
-
declare type PolicyDecision = DefinitivePolicyDecision | ConditionalPolicyDecision;
|
|
110
|
-
/**
|
|
111
|
-
* A policy to evaluate authorization requests for any permissioned action performed in Backstage.
|
|
112
|
-
*
|
|
113
|
-
* @remarks
|
|
114
|
-
*
|
|
115
|
-
* This takes as input a permission and an optional Backstage identity, and should return ALLOW if
|
|
116
|
-
* the user is permitted to execute that action; otherwise DENY. For permissions relating to
|
|
117
|
-
* resources, such a catalog entities, a conditional response can also be returned. This states
|
|
118
|
-
* that the action is allowed if the conditions provided hold true.
|
|
119
|
-
*
|
|
120
|
-
* Conditions are a rule, and parameters to evaluate against that rule. For example, the rule might
|
|
121
|
-
* be `isOwner` and the parameters a collection of entityRefs; if one of the entityRefs matches
|
|
122
|
-
* the `owner` field on a catalog entity, this would resolve to ALLOW.
|
|
123
|
-
*
|
|
124
|
-
* @public
|
|
125
|
-
*/
|
|
126
|
-
interface PermissionPolicy {
|
|
127
|
-
handle(request: PolicyAuthorizeQuery, user?: BackstageIdentityResponse): Promise<PolicyDecision>;
|
|
128
|
-
}
|
|
57
|
+
declare const createConditionFactory: <TResourceType extends string, TParams extends any[]>(rule: PermissionRule<unknown, unknown, TResourceType, TParams>) => (...params: TParams) => PermissionCondition<TResourceType, TParams>;
|
|
129
58
|
|
|
130
59
|
/**
|
|
131
60
|
* A utility type for mapping a single {@link PermissionRule} to its
|
|
@@ -133,40 +62,43 @@ interface PermissionPolicy {
|
|
|
133
62
|
*
|
|
134
63
|
* @public
|
|
135
64
|
*/
|
|
136
|
-
declare type Condition<TRule> = TRule extends PermissionRule<any, any, infer TParams> ? (...params: TParams) => PermissionCondition<TParams> : never;
|
|
65
|
+
declare type Condition<TRule> = TRule extends PermissionRule<any, any, infer TResourceType, infer TParams> ? (...params: TParams) => PermissionCondition<TResourceType, TParams> : never;
|
|
137
66
|
/**
|
|
138
67
|
* A utility type for mapping {@link PermissionRule}s to their corresponding
|
|
139
68
|
* {@link @backstage/plugin-permission-common#PermissionCondition}s.
|
|
140
69
|
*
|
|
141
70
|
* @public
|
|
142
71
|
*/
|
|
143
|
-
declare type Conditions<TRules extends Record<string, PermissionRule<any, any>>> = {
|
|
72
|
+
declare type Conditions<TRules extends Record<string, PermissionRule<any, any, any>>> = {
|
|
144
73
|
[Name in keyof TRules]: Condition<TRules[Name]>;
|
|
145
74
|
};
|
|
146
75
|
/**
|
|
147
|
-
* Creates the recommended condition-related exports for a given plugin based on
|
|
148
|
-
* {@link PermissionRule}s it supports.
|
|
76
|
+
* Creates the recommended condition-related exports for a given plugin based on
|
|
77
|
+
* the built-in {@link PermissionRule}s it supports.
|
|
149
78
|
*
|
|
150
79
|
* @remarks
|
|
151
80
|
*
|
|
152
81
|
* The function returns a `conditions` object containing a
|
|
153
|
-
* {@link @backstage/plugin-permission-common#PermissionCondition} factory for
|
|
154
|
-
* supplied {@link PermissionRule}s, along with a
|
|
155
|
-
*
|
|
82
|
+
* {@link @backstage/plugin-permission-common#PermissionCondition} factory for
|
|
83
|
+
* each of the supplied {@link PermissionRule}s, along with a
|
|
84
|
+
* `createConditionalDecision` function which builds the wrapper object needed
|
|
85
|
+
* to enclose conditions when authoring {@link PermissionPolicy}
|
|
86
|
+
* implementations.
|
|
156
87
|
*
|
|
157
|
-
* Plugin authors should generally call this method with all the built-in
|
|
158
|
-
* the plugin supports, and export the resulting
|
|
159
|
-
* function so that they can
|
|
88
|
+
* Plugin authors should generally call this method with all the built-in
|
|
89
|
+
* {@link PermissionRule}s the plugin supports, and export the resulting
|
|
90
|
+
* `conditions` object and `createConditionalDecision` function so that they can
|
|
91
|
+
* be used by {@link PermissionPolicy} authors.
|
|
160
92
|
*
|
|
161
93
|
* @public
|
|
162
94
|
*/
|
|
163
|
-
declare const createConditionExports: <TResource, TRules extends Record<string, PermissionRule<TResource, any, unknown[]>>>(options: {
|
|
95
|
+
declare const createConditionExports: <TResourceType extends string, TResource, TRules extends Record<string, PermissionRule<TResource, any, TResourceType, unknown[]>>>(options: {
|
|
164
96
|
pluginId: string;
|
|
165
|
-
resourceType:
|
|
97
|
+
resourceType: TResourceType;
|
|
166
98
|
rules: TRules;
|
|
167
99
|
}) => {
|
|
168
100
|
conditions: Conditions<TRules>;
|
|
169
|
-
|
|
101
|
+
createConditionalDecision: (permission: ResourcePermission<TResourceType>, conditions: PermissionCriteria<PermissionCondition<TResourceType, unknown[]>>) => ConditionalPolicyDecision;
|
|
170
102
|
};
|
|
171
103
|
|
|
172
104
|
/**
|
|
@@ -186,7 +118,7 @@ declare type ConditionTransformer<TQuery> = (conditions: PermissionCriteria<Perm
|
|
|
186
118
|
*
|
|
187
119
|
* @public
|
|
188
120
|
*/
|
|
189
|
-
declare const createConditionTransformer: <TQuery, TRules extends PermissionRule<any, TQuery, unknown[]>[]>(permissionRules: [...TRules]) => ConditionTransformer<TQuery>;
|
|
121
|
+
declare const createConditionTransformer: <TQuery, TRules extends PermissionRule<any, TQuery, string, unknown[]>[]>(permissionRules: [...TRules]) => ConditionTransformer<TQuery>;
|
|
190
122
|
|
|
191
123
|
/**
|
|
192
124
|
* A request to load the referenced resource and apply conditions in order to
|
|
@@ -194,7 +126,7 @@ declare const createConditionTransformer: <TQuery, TRules extends PermissionRule
|
|
|
194
126
|
*
|
|
195
127
|
* @public
|
|
196
128
|
*/
|
|
197
|
-
declare type ApplyConditionsRequestEntry =
|
|
129
|
+
declare type ApplyConditionsRequestEntry = IdentifiedPermissionMessage<{
|
|
198
130
|
resourceRef: string;
|
|
199
131
|
resourceType: string;
|
|
200
132
|
conditions: PermissionCriteria<PermissionCondition>;
|
|
@@ -213,7 +145,7 @@ declare type ApplyConditionsRequest = {
|
|
|
213
145
|
*
|
|
214
146
|
* @public
|
|
215
147
|
*/
|
|
216
|
-
declare type ApplyConditionsResponseEntry =
|
|
148
|
+
declare type ApplyConditionsResponseEntry = IdentifiedPermissionMessage<DefinitivePolicyDecision>;
|
|
217
149
|
/**
|
|
218
150
|
* A batch of {@link ApplyConditionsResponseEntry} objects.
|
|
219
151
|
*
|
|
@@ -222,6 +154,13 @@ declare type ApplyConditionsResponseEntry = Identified<DefinitivePolicyDecision>
|
|
|
222
154
|
declare type ApplyConditionsResponse = {
|
|
223
155
|
items: ApplyConditionsResponseEntry[];
|
|
224
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* Prevent use of type parameter from contributing to type inference.
|
|
159
|
+
*
|
|
160
|
+
* https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795
|
|
161
|
+
* @ignore
|
|
162
|
+
*/
|
|
163
|
+
declare type NoInfer<T> = T extends infer S ? S : never;
|
|
225
164
|
/**
|
|
226
165
|
* Create an express Router which provides an authorization route to allow
|
|
227
166
|
* integration between the permission backend and other Backstage backend
|
|
@@ -257,9 +196,9 @@ declare type ApplyConditionsResponse = {
|
|
|
257
196
|
*
|
|
258
197
|
* @public
|
|
259
198
|
*/
|
|
260
|
-
declare const createPermissionIntegrationRouter: <TResource>(options: {
|
|
261
|
-
resourceType:
|
|
262
|
-
rules: PermissionRule<TResource, any, unknown[]>[];
|
|
199
|
+
declare const createPermissionIntegrationRouter: <TResourceType extends string, TResource>(options: {
|
|
200
|
+
resourceType: TResourceType;
|
|
201
|
+
rules: PermissionRule<TResource, any, NoInfer<TResourceType>, unknown[]>[];
|
|
263
202
|
getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>;
|
|
264
203
|
}) => express.Router;
|
|
265
204
|
|
|
@@ -268,7 +207,7 @@ declare const createPermissionIntegrationRouter: <TResource>(options: {
|
|
|
268
207
|
*
|
|
269
208
|
* @public
|
|
270
209
|
*/
|
|
271
|
-
declare const createPermissionRule: <TResource, TQuery, TParams extends unknown[]>(rule: PermissionRule<TResource, TQuery, TParams>) => PermissionRule<TResource, TQuery, TParams>;
|
|
210
|
+
declare const createPermissionRule: <TResource, TQuery, TResourceType extends string, TParams extends unknown[]>(rule: PermissionRule<TResource, TQuery, TResourceType, TParams>) => PermissionRule<TResource, TQuery, TResourceType, TParams>;
|
|
272
211
|
/**
|
|
273
212
|
* Helper for making plugin-specific createPermissionRule functions, that have
|
|
274
213
|
* the TResource and TQuery type parameters populated but infer the params from
|
|
@@ -277,7 +216,7 @@ declare const createPermissionRule: <TResource, TQuery, TParams extends unknown[
|
|
|
277
216
|
*
|
|
278
217
|
* @public
|
|
279
218
|
*/
|
|
280
|
-
declare const makeCreatePermissionRule: <TResource, TQuery>() => <TParams extends unknown[]>(rule: PermissionRule<TResource, TQuery, TParams>) => PermissionRule<TResource, TQuery, TParams>;
|
|
219
|
+
declare const makeCreatePermissionRule: <TResource, TQuery, TResourceType extends string>() => <TParams extends unknown[]>(rule: PermissionRule<TResource, TQuery, TResourceType, TParams>) => PermissionRule<TResource, TQuery, TResourceType, TParams>;
|
|
281
220
|
|
|
282
221
|
/**
|
|
283
222
|
* Utility function used to parse a PermissionCriteria
|
|
@@ -307,13 +246,46 @@ declare const isOrCriteria: <T>(criteria: PermissionCriteria<T>) => criteria is
|
|
|
307
246
|
*/
|
|
308
247
|
declare const isNotCriteria: <T>(criteria: PermissionCriteria<T>) => criteria is NotCriteria<T>;
|
|
309
248
|
|
|
249
|
+
/**
|
|
250
|
+
* A query to be evaluated by the {@link PermissionPolicy}.
|
|
251
|
+
*
|
|
252
|
+
* @remarks
|
|
253
|
+
*
|
|
254
|
+
* Unlike other parts of the permission API, the policy does not accept a resource ref. This keeps
|
|
255
|
+
* the policy decoupled from the resource loading and condition applying logic.
|
|
256
|
+
*
|
|
257
|
+
* @public
|
|
258
|
+
*/
|
|
259
|
+
declare type PolicyQuery = {
|
|
260
|
+
permission: Permission;
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* A policy to evaluate authorization requests for any permissioned action performed in Backstage.
|
|
264
|
+
*
|
|
265
|
+
* @remarks
|
|
266
|
+
*
|
|
267
|
+
* This takes as input a permission and an optional Backstage identity, and should return ALLOW if
|
|
268
|
+
* the user is permitted to execute that action; otherwise DENY. For permissions relating to
|
|
269
|
+
* resources, such a catalog entities, a conditional response can also be returned. This states
|
|
270
|
+
* that the action is allowed if the conditions provided hold true.
|
|
271
|
+
*
|
|
272
|
+
* Conditions are a rule, and parameters to evaluate against that rule. For example, the rule might
|
|
273
|
+
* be `isOwner` and the parameters a collection of entityRefs; if one of the entityRefs matches
|
|
274
|
+
* the `owner` field on a catalog entity, this would resolve to ALLOW.
|
|
275
|
+
*
|
|
276
|
+
* @public
|
|
277
|
+
*/
|
|
278
|
+
interface PermissionPolicy {
|
|
279
|
+
handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise<PolicyDecision>;
|
|
280
|
+
}
|
|
281
|
+
|
|
310
282
|
/**
|
|
311
283
|
* A thin wrapper around
|
|
312
284
|
* {@link @backstage/plugin-permission-common#PermissionClient} that allows all
|
|
313
285
|
* backend-to-backend requests.
|
|
314
286
|
* @public
|
|
315
287
|
*/
|
|
316
|
-
declare class ServerPermissionClient implements
|
|
288
|
+
declare class ServerPermissionClient implements PermissionEvaluator {
|
|
317
289
|
private readonly permissionClient;
|
|
318
290
|
private readonly tokenManager;
|
|
319
291
|
private readonly permissionEnabled;
|
|
@@ -322,8 +294,10 @@ declare class ServerPermissionClient implements PermissionAuthorizer {
|
|
|
322
294
|
tokenManager: TokenManager;
|
|
323
295
|
}): ServerPermissionClient;
|
|
324
296
|
private constructor();
|
|
325
|
-
|
|
297
|
+
authorizeConditional(queries: QueryPermissionRequest[], options?: EvaluatorRequestOptions): Promise<PolicyDecision[]>;
|
|
298
|
+
authorize(requests: AuthorizePermissionRequest[], options?: EvaluatorRequestOptions): Promise<AuthorizePermissionResponse[]>;
|
|
326
299
|
private isValidServerToken;
|
|
300
|
+
private isEnabled;
|
|
327
301
|
}
|
|
328
302
|
|
|
329
|
-
export { ApplyConditionsRequest, ApplyConditionsRequestEntry, ApplyConditionsResponse, ApplyConditionsResponseEntry, Condition, ConditionTransformer,
|
|
303
|
+
export { ApplyConditionsRequest, ApplyConditionsRequestEntry, ApplyConditionsResponse, ApplyConditionsResponseEntry, Condition, ConditionTransformer, Conditions, PermissionPolicy, PermissionRule, PolicyQuery, ServerPermissionClient, createConditionExports, createConditionFactory, createConditionTransformer, createPermissionIntegrationRouter, createPermissionRule, isAndCriteria, isNotCriteria, isOrCriteria, 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.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
"start": "backstage-cli package start"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@backstage/backend-common": "^0.13.2
|
|
36
|
+
"@backstage/backend-common": "^0.13.2",
|
|
37
37
|
"@backstage/config": "^1.0.0",
|
|
38
38
|
"@backstage/errors": "^1.0.0",
|
|
39
|
-
"@backstage/plugin-auth-node": "^0.2.0
|
|
40
|
-
"@backstage/plugin-permission-common": "^0.
|
|
39
|
+
"@backstage/plugin-auth-node": "^0.2.0",
|
|
40
|
+
"@backstage/plugin-permission-common": "^0.6.0",
|
|
41
41
|
"@types/express": "^4.17.6",
|
|
42
42
|
"express": "^4.17.1",
|
|
43
43
|
"express-promise-router": "^4.1.0",
|
|
44
44
|
"zod": "^3.11.6"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@backstage/cli": "^0.
|
|
47
|
+
"@backstage/cli": "^0.17.0",
|
|
48
48
|
"@types/supertest": "^2.0.8",
|
|
49
49
|
"msw": "^0.35.0",
|
|
50
50
|
"supertest": "^6.1.3"
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"files": [
|
|
53
53
|
"dist"
|
|
54
54
|
],
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "e0e44c433319711c2fb8b175db411a621f7aaec2"
|
|
56
56
|
}
|