@backstage/plugin-permission-backend 0.5.36-next.0 → 0.5.36
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 +23 -10
- package/alpha/package.json +1 -1
- package/dist/alpha.cjs.js +5 -17
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/cjs/{router-ced2cd8a.cjs.js → router-40c490a9.cjs.js} +13 -40
- package/dist/cjs/router-40c490a9.cjs.js.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +3 -6
- package/package.json +29 -30
- package/dist/cjs/router-ced2cd8a.cjs.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
# @backstage/plugin-permission-backend
|
|
2
2
|
|
|
3
|
-
## 0.5.36
|
|
3
|
+
## 0.5.36
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/backend-common@0.21.3
|
|
9
|
+
- @backstage/plugin-auth-node@0.4.8
|
|
10
|
+
- @backstage/plugin-permission-node@0.7.24
|
|
11
|
+
- @backstage/backend-plugin-api@0.6.13
|
|
12
|
+
|
|
13
|
+
## 0.5.35
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @backstage/backend-common@0.21.2
|
|
19
|
+
- @backstage/plugin-auth-node@0.4.7
|
|
20
|
+
- @backstage/plugin-permission-node@0.7.23
|
|
21
|
+
- @backstage/backend-plugin-api@0.6.12
|
|
22
|
+
|
|
23
|
+
## 0.5.34
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
10
26
|
|
|
11
27
|
- Updated dependencies
|
|
12
|
-
- @backstage/backend-common@0.21.
|
|
13
|
-
- @backstage/plugin-auth-node@0.4.
|
|
14
|
-
- @backstage/
|
|
15
|
-
- @backstage/backend-plugin-api@0.6.
|
|
16
|
-
- @backstage/plugin-permission-common@0.7.13-next.0
|
|
17
|
-
- @backstage/plugin-permission-node@0.7.24-next.0
|
|
18
|
-
- @backstage/config@1.1.2-next.0
|
|
28
|
+
- @backstage/backend-common@0.21.1
|
|
29
|
+
- @backstage/plugin-auth-node@0.4.6
|
|
30
|
+
- @backstage/plugin-permission-node@0.7.22
|
|
31
|
+
- @backstage/backend-plugin-api@0.6.11
|
|
19
32
|
|
|
20
33
|
## 0.5.33
|
|
21
34
|
|
package/alpha/package.json
CHANGED
package/dist/alpha.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var backendCommon = require('@backstage/backend-common');
|
|
6
6
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
7
7
|
var alpha = require('@backstage/plugin-permission-node/alpha');
|
|
8
|
-
var router = require('./cjs/router-
|
|
8
|
+
var router = require('./cjs/router-40c490a9.cjs.js');
|
|
9
9
|
require('zod');
|
|
10
10
|
require('express');
|
|
11
11
|
require('express-promise-router');
|
|
@@ -43,19 +43,9 @@ const permissionPlugin = backendPluginApi.createBackendPlugin({
|
|
|
43
43
|
config: backendPluginApi.coreServices.rootConfig,
|
|
44
44
|
logger: backendPluginApi.coreServices.logger,
|
|
45
45
|
discovery: backendPluginApi.coreServices.discovery,
|
|
46
|
-
|
|
47
|
-
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
48
|
-
userInfo: backendPluginApi.coreServices.userInfo
|
|
46
|
+
identity: backendPluginApi.coreServices.identity
|
|
49
47
|
},
|
|
50
|
-
async init({
|
|
51
|
-
http,
|
|
52
|
-
config,
|
|
53
|
-
logger,
|
|
54
|
-
discovery,
|
|
55
|
-
auth,
|
|
56
|
-
httpAuth,
|
|
57
|
-
userInfo
|
|
58
|
-
}) {
|
|
48
|
+
async init({ http, config, logger, discovery, identity }) {
|
|
59
49
|
const winstonLogger = backendCommon.loggerToWinstonLogger(logger);
|
|
60
50
|
if (!policies.policy) {
|
|
61
51
|
throw new Error(
|
|
@@ -66,11 +56,9 @@ const permissionPlugin = backendPluginApi.createBackendPlugin({
|
|
|
66
56
|
await router.createRouter({
|
|
67
57
|
config,
|
|
68
58
|
discovery,
|
|
59
|
+
identity,
|
|
69
60
|
logger: winstonLogger,
|
|
70
|
-
policy: policies.policy
|
|
71
|
-
auth,
|
|
72
|
-
httpAuth,
|
|
73
|
-
userInfo
|
|
61
|
+
policy: policies.policy
|
|
74
62
|
})
|
|
75
63
|
);
|
|
76
64
|
}
|
package/dist/alpha.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.cjs.js","sources":["../src/plugin.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 { loggerToWinstonLogger } from '@backstage/backend-common';\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { PermissionPolicy } from '@backstage/plugin-permission-node';\nimport {\n policyExtensionPoint,\n PolicyExtensionPoint,\n} from '@backstage/plugin-permission-node/alpha';\nimport { createRouter } from './service';\n\nclass PolicyExtensionPointImpl implements PolicyExtensionPoint {\n public policy: PermissionPolicy | undefined;\n\n setPolicy(policy: PermissionPolicy): void {\n if (this.policy) {\n throw new Error('Policy already set');\n }\n this.policy = policy;\n }\n}\n\n/**\n * Permission plugin\n *\n * @alpha\n */\nexport const permissionPlugin = createBackendPlugin({\n pluginId: 'permission',\n register(env) {\n const policies = new PolicyExtensionPointImpl();\n\n env.registerExtensionPoint(policyExtensionPoint, policies);\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n
|
|
1
|
+
{"version":3,"file":"alpha.cjs.js","sources":["../src/plugin.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 { loggerToWinstonLogger } from '@backstage/backend-common';\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { PermissionPolicy } from '@backstage/plugin-permission-node';\nimport {\n policyExtensionPoint,\n PolicyExtensionPoint,\n} from '@backstage/plugin-permission-node/alpha';\nimport { createRouter } from './service';\n\nclass PolicyExtensionPointImpl implements PolicyExtensionPoint {\n public policy: PermissionPolicy | undefined;\n\n setPolicy(policy: PermissionPolicy): void {\n if (this.policy) {\n throw new Error('Policy already set');\n }\n this.policy = policy;\n }\n}\n\n/**\n * Permission plugin\n *\n * @alpha\n */\nexport const permissionPlugin = createBackendPlugin({\n pluginId: 'permission',\n register(env) {\n const policies = new PolicyExtensionPointImpl();\n\n env.registerExtensionPoint(policyExtensionPoint, policies);\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n identity: coreServices.identity,\n },\n async init({ http, config, logger, discovery, identity }) {\n const winstonLogger = loggerToWinstonLogger(logger);\n if (!policies.policy) {\n throw new Error(\n 'No policy module installed! Please install a policy module. If you want to allow all requests, use @backstage/plugin-permission-backend-module-allow-all-policy permissionModuleAllowAllPolicy',\n );\n }\n\n http.use(\n await createRouter({\n config,\n discovery,\n identity,\n logger: winstonLogger,\n policy: policies.policy,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","policyExtensionPoint","coreServices","loggerToWinstonLogger","createRouter"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAM,wBAAyD,CAAA;AAAA,EAA/D,WAAA,GAAA;AACE,IAAO,aAAA,CAAA,IAAA,EAAA,QAAA,CAAA,CAAA;AAAA,GAAA;AAAA,EAEP,UAAU,MAAgC,EAAA;AACxC,IAAA,IAAI,KAAK,MAAQ,EAAA;AACf,MAAM,MAAA,IAAI,MAAM,oBAAoB,CAAA,CAAA;AAAA,KACtC;AACA,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AAAA,GAChB;AACF,CAAA;AAOO,MAAM,mBAAmBA,oCAAoB,CAAA;AAAA,EAClD,QAAU,EAAA,YAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,QAAA,GAAW,IAAI,wBAAyB,EAAA,CAAA;AAE9C,IAAI,GAAA,CAAA,sBAAA,CAAuBC,4BAAsB,QAAQ,CAAA,CAAA;AAEzD,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,MAAMC,6BAAa,CAAA,UAAA;AAAA,QACnB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,UAAUA,6BAAa,CAAA,QAAA;AAAA,OACzB;AAAA,MACA,MAAM,KAAK,EAAE,IAAA,EAAM,QAAQ,MAAQ,EAAA,SAAA,EAAW,UAAY,EAAA;AACxD,QAAM,MAAA,aAAA,GAAgBC,oCAAsB,MAAM,CAAA,CAAA;AAClD,QAAI,IAAA,CAAC,SAAS,MAAQ,EAAA;AACpB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,gMAAA;AAAA,WACF,CAAA;AAAA,SACF;AAEA,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,MAAMC,mBAAa,CAAA;AAAA,YACjB,MAAA;AAAA,YACA,SAAA;AAAA,YACA,QAAA;AAAA,YACA,MAAQ,EAAA,aAAA;AAAA,YACR,QAAQ,QAAS,CAAA,MAAA;AAAA,WAClB,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
|
|
@@ -34,17 +34,12 @@ const responseSchema = zod.z.object({
|
|
|
34
34
|
class PermissionIntegrationClient {
|
|
35
35
|
constructor(options) {
|
|
36
36
|
__publicField(this, "discovery");
|
|
37
|
-
__publicField(this, "auth");
|
|
38
37
|
this.discovery = options.discovery;
|
|
39
|
-
this.auth = options.auth;
|
|
40
38
|
}
|
|
41
|
-
async applyConditions(pluginId,
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
onBehalfOf: credentials,
|
|
46
|
-
targetPluginId: pluginId
|
|
47
|
-
}).then((t) => t.token);
|
|
39
|
+
async applyConditions(pluginId, decisions, authHeader) {
|
|
40
|
+
const endpoint = `${await this.discovery.getBaseUrl(
|
|
41
|
+
pluginId
|
|
42
|
+
)}/.well-known/backstage/permissions/apply-conditions`;
|
|
48
43
|
const response = await fetch__default["default"](endpoint, {
|
|
49
44
|
method: "POST",
|
|
50
45
|
body: JSON.stringify({
|
|
@@ -58,7 +53,7 @@ class PermissionIntegrationClient {
|
|
|
58
53
|
)
|
|
59
54
|
}),
|
|
60
55
|
headers: {
|
|
61
|
-
...
|
|
56
|
+
...authHeader ? { authorization: authHeader } : {},
|
|
62
57
|
"content-type": "application/json"
|
|
63
58
|
}
|
|
64
59
|
});
|
|
@@ -101,29 +96,12 @@ const evaluatePermissionRequestSchema = zod.z.object({
|
|
|
101
96
|
const evaluatePermissionRequestBatchSchema = zod.z.object({
|
|
102
97
|
items: zod.z.array(evaluatePermissionRequestSchema)
|
|
103
98
|
});
|
|
104
|
-
const handleRequest = async (requests, policy, permissionIntegrationClient,
|
|
99
|
+
const handleRequest = async (requests, user, policy, permissionIntegrationClient, authHeader) => {
|
|
105
100
|
const applyConditionsLoaderFor = lodash.memoize((pluginId) => {
|
|
106
101
|
return new DataLoader__default["default"](
|
|
107
|
-
(batch) => permissionIntegrationClient.applyConditions(pluginId,
|
|
102
|
+
(batch) => permissionIntegrationClient.applyConditions(pluginId, batch, authHeader)
|
|
108
103
|
);
|
|
109
104
|
});
|
|
110
|
-
let user;
|
|
111
|
-
if (auth.isPrincipal(credentials, "user")) {
|
|
112
|
-
const { ownershipEntityRefs } = await userInfo.getUserInfo(credentials);
|
|
113
|
-
const { token } = await auth.getPluginRequestToken({
|
|
114
|
-
onBehalfOf: credentials,
|
|
115
|
-
targetPluginId: "catalog"
|
|
116
|
-
// TODO: unknown at this point
|
|
117
|
-
});
|
|
118
|
-
user = {
|
|
119
|
-
identity: {
|
|
120
|
-
type: "user",
|
|
121
|
-
userEntityRef: credentials.principal.userEntityRef,
|
|
122
|
-
ownershipEntityRefs
|
|
123
|
-
},
|
|
124
|
-
token
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
105
|
return Promise.all(
|
|
128
106
|
requests.map(
|
|
129
107
|
({ id, resourceRef, ...request }) => policy.handle(request, user).then((decision) => {
|
|
@@ -159,16 +137,14 @@ const handleRequest = async (requests, policy, permissionIntegrationClient, cred
|
|
|
159
137
|
);
|
|
160
138
|
};
|
|
161
139
|
async function createRouter(options) {
|
|
162
|
-
const { policy, discovery, config, logger } = options;
|
|
163
|
-
const { auth, httpAuth, userInfo } = backendCommon.createLegacyAuthAdapters(options);
|
|
140
|
+
const { policy, discovery, identity, config, logger } = options;
|
|
164
141
|
if (!config.getOptionalBoolean("permission.enabled")) {
|
|
165
142
|
logger.warn(
|
|
166
143
|
"Permission backend started with permissions disabled. Enable permissions by setting permission.enabled=true."
|
|
167
144
|
);
|
|
168
145
|
}
|
|
169
146
|
const permissionIntegrationClient = new PermissionIntegrationClient({
|
|
170
|
-
discovery
|
|
171
|
-
auth
|
|
147
|
+
discovery
|
|
172
148
|
});
|
|
173
149
|
const router = Router__default["default"]();
|
|
174
150
|
router.use(express__default["default"].json());
|
|
@@ -178,9 +154,7 @@ async function createRouter(options) {
|
|
|
178
154
|
router.post(
|
|
179
155
|
"/authorize",
|
|
180
156
|
async (req, res) => {
|
|
181
|
-
const
|
|
182
|
-
allow: ["user", "none"]
|
|
183
|
-
});
|
|
157
|
+
const user = await identity.getIdentity({ request: req });
|
|
184
158
|
const parseResult = evaluatePermissionRequestBatchSchema.safeParse(
|
|
185
159
|
req.body
|
|
186
160
|
);
|
|
@@ -191,11 +165,10 @@ async function createRouter(options) {
|
|
|
191
165
|
res.json({
|
|
192
166
|
items: await handleRequest(
|
|
193
167
|
body.items,
|
|
168
|
+
user,
|
|
194
169
|
policy,
|
|
195
170
|
permissionIntegrationClient,
|
|
196
|
-
|
|
197
|
-
auth,
|
|
198
|
-
userInfo
|
|
171
|
+
req.header("authorization")
|
|
199
172
|
)
|
|
200
173
|
});
|
|
201
174
|
}
|
|
@@ -205,4 +178,4 @@ async function createRouter(options) {
|
|
|
205
178
|
}
|
|
206
179
|
|
|
207
180
|
exports.createRouter = createRouter;
|
|
208
|
-
//# sourceMappingURL=router-
|
|
181
|
+
//# sourceMappingURL=router-40c490a9.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-40c490a9.cjs.js","sources":["../../src/service/PermissionIntegrationClient.ts","../../src/service/router.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 fetch from 'node-fetch';\nimport { z } from 'zod';\nimport { PluginEndpointDiscovery } from '@backstage/backend-common';\nimport {\n AuthorizeResult,\n ConditionalPolicyDecision,\n} from '@backstage/plugin-permission-common';\nimport {\n ApplyConditionsRequestEntry,\n ApplyConditionsResponseEntry,\n} from '@backstage/plugin-permission-node';\n\nconst responseSchema = z.object({\n items: z.array(\n z.object({\n id: z.string(),\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n ),\n});\n\nexport type ResourcePolicyDecision = ConditionalPolicyDecision & {\n resourceRef: string;\n};\n\nexport class PermissionIntegrationClient {\n private readonly discovery: PluginEndpointDiscovery;\n\n constructor(options: { discovery: PluginEndpointDiscovery }) {\n this.discovery = options.discovery;\n }\n\n async applyConditions(\n pluginId: string,\n decisions: readonly ApplyConditionsRequestEntry[],\n authHeader?: string,\n ): Promise<ApplyConditionsResponseEntry[]> {\n const endpoint = `${await this.discovery.getBaseUrl(\n pluginId,\n )}/.well-known/backstage/permissions/apply-conditions`;\n\n const response = await fetch(endpoint, {\n method: 'POST',\n body: JSON.stringify({\n items: decisions.map(\n ({ id, resourceRef, resourceType, conditions }) => ({\n id,\n resourceRef,\n resourceType,\n conditions,\n }),\n ),\n }),\n headers: {\n ...(authHeader ? { authorization: authHeader } : {}),\n 'content-type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new Error(\n `Unexpected response from plugin upstream when applying conditions. Expected 200 but got ${response.status} - ${response.statusText}`,\n );\n }\n\n const result = responseSchema.parse(await response.json());\n\n return result.items;\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 { z } from 'zod';\nimport express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport {\n errorHandler,\n PluginEndpointDiscovery,\n} from '@backstage/backend-common';\nimport { InputError } from '@backstage/errors';\nimport {\n BackstageIdentityResponse,\n IdentityApi,\n} from '@backstage/plugin-auth-node';\nimport {\n AuthorizeResult,\n EvaluatePermissionResponse,\n EvaluatePermissionRequest,\n IdentifiedPermissionMessage,\n EvaluatePermissionRequestBatch,\n EvaluatePermissionResponseBatch,\n isResourcePermission,\n PermissionAttributes,\n} from '@backstage/plugin-permission-common';\nimport {\n ApplyConditionsRequestEntry,\n ApplyConditionsResponseEntry,\n PermissionPolicy,\n} from '@backstage/plugin-permission-node';\nimport { PermissionIntegrationClient } from './PermissionIntegrationClient';\nimport { memoize } from 'lodash';\nimport DataLoader from 'dataloader';\nimport { Config } from '@backstage/config';\n\nconst attributesSchema: z.ZodSchema<PermissionAttributes> = z.object({\n action: z\n .union([\n z.literal('create'),\n z.literal('read'),\n z.literal('update'),\n z.literal('delete'),\n ])\n .optional(),\n});\n\nconst permissionSchema = z.union([\n z.object({\n type: z.literal('basic'),\n name: z.string(),\n attributes: attributesSchema,\n }),\n z.object({\n type: z.literal('resource'),\n name: z.string(),\n attributes: attributesSchema,\n resourceType: z.string(),\n }),\n]);\n\nconst evaluatePermissionRequestSchema: z.ZodSchema<\n IdentifiedPermissionMessage<EvaluatePermissionRequest>\n> = z.object({\n id: z.string(),\n resourceRef: z.string().optional(),\n permission: permissionSchema,\n});\n\nconst evaluatePermissionRequestBatchSchema: z.ZodSchema<EvaluatePermissionRequestBatch> =\n z.object({\n items: z.array(evaluatePermissionRequestSchema),\n });\n\n/**\n * Options required when constructing a new {@link express#Router} using\n * {@link createRouter}.\n *\n * @public\n */\nexport interface RouterOptions {\n logger: Logger;\n discovery: PluginEndpointDiscovery;\n policy: PermissionPolicy;\n identity: IdentityApi;\n config: Config;\n}\n\nconst handleRequest = async (\n requests: IdentifiedPermissionMessage<EvaluatePermissionRequest>[],\n user: BackstageIdentityResponse | undefined,\n policy: PermissionPolicy,\n permissionIntegrationClient: PermissionIntegrationClient,\n authHeader?: string,\n): Promise<IdentifiedPermissionMessage<EvaluatePermissionResponse>[]> => {\n const applyConditionsLoaderFor = memoize((pluginId: string) => {\n return new DataLoader<\n ApplyConditionsRequestEntry,\n ApplyConditionsResponseEntry\n >(batch =>\n permissionIntegrationClient.applyConditions(pluginId, batch, authHeader),\n );\n });\n\n return Promise.all(\n requests.map(({ id, resourceRef, ...request }) =>\n policy.handle(request, user).then(decision => {\n if (decision.result !== AuthorizeResult.CONDITIONAL) {\n return {\n id,\n ...decision,\n };\n }\n\n if (!isResourcePermission(request.permission)) {\n throw new Error(\n `Conditional decision returned from permission policy for non-resource permission ${request.permission.name}`,\n );\n }\n\n if (decision.resourceType !== request.permission.resourceType) {\n throw new Error(\n `Invalid resource conditions returned from permission policy for permission ${request.permission.name}`,\n );\n }\n\n if (!resourceRef) {\n return {\n id,\n ...decision,\n };\n }\n\n return applyConditionsLoaderFor(decision.pluginId).load({\n id,\n resourceRef,\n ...decision,\n });\n }),\n ),\n );\n};\n\n/**\n * Creates a new {@link express#Router} which provides the backend API\n * for the permission system.\n *\n * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { policy, discovery, identity, config, logger } = options;\n\n if (!config.getOptionalBoolean('permission.enabled')) {\n logger.warn(\n 'Permission backend started with permissions disabled. Enable permissions by setting permission.enabled=true.',\n );\n }\n\n const permissionIntegrationClient = new PermissionIntegrationClient({\n discovery,\n });\n\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n router.post(\n '/authorize',\n async (\n req: Request<EvaluatePermissionRequestBatch>,\n res: Response<EvaluatePermissionResponseBatch>,\n ) => {\n const user = await identity.getIdentity({ request: req });\n\n const parseResult = evaluatePermissionRequestBatchSchema.safeParse(\n req.body,\n );\n\n if (!parseResult.success) {\n throw new InputError(parseResult.error.toString());\n }\n\n const body = parseResult.data;\n\n res.json({\n items: await handleRequest(\n body.items,\n user,\n policy,\n permissionIntegrationClient,\n req.header('authorization'),\n ),\n });\n },\n );\n\n router.use(errorHandler());\n\n return router;\n}\n"],"names":["z","AuthorizeResult","fetch","memoize","DataLoader","isResourcePermission","Router","express","InputError","errorHandler"],"mappings":"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"}
|
package/dist/index.cjs.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { Logger } from 'winston';
|
|
3
|
+
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
|
3
4
|
import { IdentityApi } from '@backstage/plugin-auth-node';
|
|
4
5
|
import { PermissionPolicy } from '@backstage/plugin-permission-node';
|
|
5
6
|
import { Config } from '@backstage/config';
|
|
6
|
-
import { DiscoveryService, AuthService, HttpAuthService, UserInfoService } from '@backstage/backend-plugin-api';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Options required when constructing a new {@link express#Router} using
|
|
@@ -13,13 +13,10 @@ import { DiscoveryService, AuthService, HttpAuthService, UserInfoService } from
|
|
|
13
13
|
*/
|
|
14
14
|
interface RouterOptions {
|
|
15
15
|
logger: Logger;
|
|
16
|
-
discovery:
|
|
16
|
+
discovery: PluginEndpointDiscovery;
|
|
17
17
|
policy: PermissionPolicy;
|
|
18
|
-
identity
|
|
18
|
+
identity: IdentityApi;
|
|
19
19
|
config: Config;
|
|
20
|
-
auth?: AuthService;
|
|
21
|
-
httpAuth?: HttpAuthService;
|
|
22
|
-
userInfo?: UserInfoService;
|
|
23
20
|
}
|
|
24
21
|
/**
|
|
25
22
|
* Creates a new {@link express#Router} which provides the backend API
|
package/package.json
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-permission-backend",
|
|
3
|
-
"version": "0.5.36
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.5.36",
|
|
4
|
+
"main": "./dist/index.cjs.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://backstage.io",
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "https://github.com/backstage/backstage",
|
|
14
|
-
"directory": "plugins/permission-backend"
|
|
15
|
-
},
|
|
16
|
-
"license": "Apache-2.0",
|
|
17
10
|
"exports": {
|
|
18
11
|
".": {
|
|
19
12
|
"require": "./dist/index.cjs.js",
|
|
@@ -27,29 +20,32 @@
|
|
|
27
20
|
},
|
|
28
21
|
"./package.json": "./package.json"
|
|
29
22
|
},
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
"backstage": {
|
|
24
|
+
"role": "backend-plugin"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://backstage.io",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/backstage/backstage",
|
|
30
|
+
"directory": "plugins/permission-backend"
|
|
31
|
+
},
|
|
36
32
|
"scripts": {
|
|
33
|
+
"start": "backstage-cli package start",
|
|
37
34
|
"build": "backstage-cli package build",
|
|
38
|
-
"clean": "backstage-cli package clean",
|
|
39
35
|
"lint": "backstage-cli package lint",
|
|
36
|
+
"test": "backstage-cli package test",
|
|
40
37
|
"prepack": "backstage-cli package prepack",
|
|
41
38
|
"postpack": "backstage-cli package postpack",
|
|
42
|
-
"
|
|
43
|
-
"test": "backstage-cli package test"
|
|
39
|
+
"clean": "backstage-cli package clean"
|
|
44
40
|
},
|
|
45
41
|
"dependencies": {
|
|
46
|
-
"@backstage/backend-common": "^0.21.3
|
|
47
|
-
"@backstage/backend-plugin-api": "^0.6.13
|
|
48
|
-
"@backstage/config": "^1.1.
|
|
49
|
-
"@backstage/errors": "^1.2.
|
|
50
|
-
"@backstage/plugin-auth-node": "^0.4.8
|
|
51
|
-
"@backstage/plugin-permission-common": "^0.7.
|
|
52
|
-
"@backstage/plugin-permission-node": "^0.7.24
|
|
42
|
+
"@backstage/backend-common": "^0.21.3",
|
|
43
|
+
"@backstage/backend-plugin-api": "^0.6.13",
|
|
44
|
+
"@backstage/config": "^1.1.1",
|
|
45
|
+
"@backstage/errors": "^1.2.3",
|
|
46
|
+
"@backstage/plugin-auth-node": "^0.4.8",
|
|
47
|
+
"@backstage/plugin-permission-common": "^0.7.12",
|
|
48
|
+
"@backstage/plugin-permission-node": "^0.7.24",
|
|
53
49
|
"@types/express": "*",
|
|
54
50
|
"dataloader": "^2.0.0",
|
|
55
51
|
"express": "^4.17.1",
|
|
@@ -61,11 +57,14 @@
|
|
|
61
57
|
"zod": "^3.22.4"
|
|
62
58
|
},
|
|
63
59
|
"devDependencies": {
|
|
64
|
-
"@backstage/
|
|
65
|
-
"@backstage/cli": "^0.25.3-next.0",
|
|
60
|
+
"@backstage/cli": "^0.25.2",
|
|
66
61
|
"@types/lodash": "^4.14.151",
|
|
67
62
|
"@types/supertest": "^2.0.8",
|
|
68
63
|
"msw": "^1.0.0",
|
|
69
64
|
"supertest": "^6.1.6"
|
|
70
|
-
}
|
|
65
|
+
},
|
|
66
|
+
"files": [
|
|
67
|
+
"dist",
|
|
68
|
+
"alpha"
|
|
69
|
+
]
|
|
71
70
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"router-ced2cd8a.cjs.js","sources":["../../src/service/PermissionIntegrationClient.ts","../../src/service/router.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 fetch from 'node-fetch';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n ConditionalPolicyDecision,\n} from '@backstage/plugin-permission-common';\nimport {\n ApplyConditionsRequestEntry,\n ApplyConditionsResponseEntry,\n} from '@backstage/plugin-permission-node';\nimport {\n AuthService,\n BackstageCredentials,\n DiscoveryService,\n} from '@backstage/backend-plugin-api';\n\nconst responseSchema = z.object({\n items: z.array(\n z.object({\n id: z.string(),\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n ),\n});\n\nexport type ResourcePolicyDecision = ConditionalPolicyDecision & {\n resourceRef: string;\n};\n\nexport class PermissionIntegrationClient {\n private readonly discovery: DiscoveryService;\n private readonly auth: AuthService;\n\n constructor(options: { discovery: DiscoveryService; auth: AuthService }) {\n this.discovery = options.discovery;\n this.auth = options.auth;\n }\n\n async applyConditions(\n pluginId: string,\n credentials: BackstageCredentials,\n decisions: readonly ApplyConditionsRequestEntry[],\n ): Promise<ApplyConditionsResponseEntry[]> {\n const baseUrl = await this.discovery.getBaseUrl(pluginId);\n const endpoint = `${baseUrl}/.well-known/backstage/permissions/apply-conditions`;\n\n const token = this.auth.isPrincipal(credentials, 'none')\n ? undefined\n : await this.auth\n .getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: pluginId,\n })\n .then(t => t.token);\n\n const response = await fetch(endpoint, {\n method: 'POST',\n body: JSON.stringify({\n items: decisions.map(\n ({ id, resourceRef, resourceType, conditions }) => ({\n id,\n resourceRef,\n resourceType,\n conditions,\n }),\n ),\n }),\n headers: {\n ...(token ? { authorization: `Bearer ${token}` } : {}),\n 'content-type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new Error(\n `Unexpected response from plugin upstream when applying conditions. Expected 200 but got ${response.status} - ${response.statusText}`,\n );\n }\n\n const result = responseSchema.parse(await response.json());\n\n return result.items;\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 { z } from 'zod';\nimport express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport {\n createLegacyAuthAdapters,\n errorHandler,\n} from '@backstage/backend-common';\nimport { InputError } from '@backstage/errors';\nimport {\n BackstageIdentityResponse,\n IdentityApi,\n} from '@backstage/plugin-auth-node';\nimport {\n AuthorizeResult,\n EvaluatePermissionResponse,\n EvaluatePermissionRequest,\n IdentifiedPermissionMessage,\n EvaluatePermissionRequestBatch,\n EvaluatePermissionResponseBatch,\n isResourcePermission,\n PermissionAttributes,\n} from '@backstage/plugin-permission-common';\nimport {\n ApplyConditionsRequestEntry,\n ApplyConditionsResponseEntry,\n PermissionPolicy,\n} from '@backstage/plugin-permission-node';\nimport { PermissionIntegrationClient } from './PermissionIntegrationClient';\nimport { memoize } from 'lodash';\nimport DataLoader from 'dataloader';\nimport { Config } from '@backstage/config';\nimport {\n AuthService,\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstageUserPrincipal,\n DiscoveryService,\n HttpAuthService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\n\nconst attributesSchema: z.ZodSchema<PermissionAttributes> = z.object({\n action: z\n .union([\n z.literal('create'),\n z.literal('read'),\n z.literal('update'),\n z.literal('delete'),\n ])\n .optional(),\n});\n\nconst permissionSchema = z.union([\n z.object({\n type: z.literal('basic'),\n name: z.string(),\n attributes: attributesSchema,\n }),\n z.object({\n type: z.literal('resource'),\n name: z.string(),\n attributes: attributesSchema,\n resourceType: z.string(),\n }),\n]);\n\nconst evaluatePermissionRequestSchema: z.ZodSchema<\n IdentifiedPermissionMessage<EvaluatePermissionRequest>\n> = z.object({\n id: z.string(),\n resourceRef: z.string().optional(),\n permission: permissionSchema,\n});\n\nconst evaluatePermissionRequestBatchSchema: z.ZodSchema<EvaluatePermissionRequestBatch> =\n z.object({\n items: z.array(evaluatePermissionRequestSchema),\n });\n\n/**\n * Options required when constructing a new {@link express#Router} using\n * {@link createRouter}.\n *\n * @public\n */\nexport interface RouterOptions {\n logger: Logger;\n discovery: DiscoveryService;\n policy: PermissionPolicy;\n identity?: IdentityApi;\n config: Config;\n auth?: AuthService;\n httpAuth?: HttpAuthService;\n userInfo?: UserInfoService;\n}\n\nconst handleRequest = async (\n requests: IdentifiedPermissionMessage<EvaluatePermissionRequest>[],\n policy: PermissionPolicy,\n permissionIntegrationClient: PermissionIntegrationClient,\n credentials: BackstageCredentials<\n BackstageNonePrincipal | BackstageUserPrincipal\n >,\n auth: AuthService,\n userInfo: UserInfoService,\n): Promise<IdentifiedPermissionMessage<EvaluatePermissionResponse>[]> => {\n const applyConditionsLoaderFor = memoize((pluginId: string) => {\n return new DataLoader<\n ApplyConditionsRequestEntry,\n ApplyConditionsResponseEntry\n >(batch =>\n permissionIntegrationClient.applyConditions(pluginId, credentials, batch),\n );\n });\n\n let user: BackstageIdentityResponse | undefined;\n if (auth.isPrincipal(credentials, 'user')) {\n const { ownershipEntityRefs } = await userInfo.getUserInfo(credentials);\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog', // TODO: unknown at this point\n });\n user = {\n identity: {\n type: 'user',\n userEntityRef: credentials.principal.userEntityRef,\n ownershipEntityRefs,\n },\n token,\n };\n }\n\n return Promise.all(\n requests.map(({ id, resourceRef, ...request }) =>\n policy.handle(request, user).then(decision => {\n if (decision.result !== AuthorizeResult.CONDITIONAL) {\n return {\n id,\n ...decision,\n };\n }\n\n if (!isResourcePermission(request.permission)) {\n throw new Error(\n `Conditional decision returned from permission policy for non-resource permission ${request.permission.name}`,\n );\n }\n\n if (decision.resourceType !== request.permission.resourceType) {\n throw new Error(\n `Invalid resource conditions returned from permission policy for permission ${request.permission.name}`,\n );\n }\n\n if (!resourceRef) {\n return {\n id,\n ...decision,\n };\n }\n\n return applyConditionsLoaderFor(decision.pluginId).load({\n id,\n resourceRef,\n ...decision,\n });\n }),\n ),\n );\n};\n\n/**\n * Creates a new {@link express#Router} which provides the backend API\n * for the permission system.\n *\n * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { policy, discovery, config, logger } = options;\n const { auth, httpAuth, userInfo } = createLegacyAuthAdapters(options);\n\n if (!config.getOptionalBoolean('permission.enabled')) {\n logger.warn(\n 'Permission backend started with permissions disabled. Enable permissions by setting permission.enabled=true.',\n );\n }\n\n const permissionIntegrationClient = new PermissionIntegrationClient({\n discovery,\n auth,\n });\n\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n router.post(\n '/authorize',\n async (\n req: Request<EvaluatePermissionRequestBatch>,\n res: Response<EvaluatePermissionResponseBatch>,\n ) => {\n const credentials = await httpAuth.credentials(req, {\n allow: ['user', 'none'],\n });\n\n const parseResult = evaluatePermissionRequestBatchSchema.safeParse(\n req.body,\n );\n\n if (!parseResult.success) {\n throw new InputError(parseResult.error.toString());\n }\n\n const body = parseResult.data;\n\n res.json({\n items: await handleRequest(\n body.items,\n policy,\n permissionIntegrationClient,\n credentials,\n auth,\n userInfo,\n ),\n });\n },\n );\n\n router.use(errorHandler());\n\n return router;\n}\n"],"names":["z","AuthorizeResult","fetch","memoize","DataLoader","isResourcePermission","createLegacyAuthAdapters","Router","express","InputError","errorHandler"],"mappings":"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"}
|