@backstage/plugin-permission-backend 0.5.37-next.2 → 0.5.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @backstage/plugin-permission-backend
2
2
 
3
+ ## 0.5.38
4
+
5
+ ### Patch Changes
6
+
7
+ - 9c7fb30: Properly forward causes of errors from upstream backends in the `PermissionIntegrationClient`
8
+
9
+ ## 0.5.37
10
+
11
+ ### Patch Changes
12
+
13
+ - 4467036: Allow unauthenticated access to health check endpoint.
14
+ - 9802004: Migrated to use the new auth services introduced in [BEP-0003](https://github.com/backstage/backstage/blob/master/beps/0003-auth-architecture-evolution/README.md).
15
+
16
+ The `createRouter` function now accepts `auth`, `httpAuth` and `userInfo` options. Theses are used internally to support the new backend system, and can be ignored.
17
+
18
+ - Updated dependencies
19
+ - @backstage/backend-common@0.21.4
20
+ - @backstage/plugin-auth-node@0.4.9
21
+ - @backstage/config@1.2.0
22
+ - @backstage/errors@1.2.4
23
+ - @backstage/backend-plugin-api@0.6.14
24
+ - @backstage/plugin-permission-common@0.7.13
25
+ - @backstage/plugin-permission-node@0.7.25
26
+
3
27
  ## 0.5.37-next.2
4
28
 
5
29
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-permission-backend",
3
- "version": "0.5.37-next.2",
3
+ "version": "0.5.38",
4
4
  "main": "../dist/alpha.cjs.js",
5
5
  "types": "../dist/alpha.d.ts"
6
6
  }
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-ced2cd8a.cjs.js');
8
+ var router = require('./cjs/router-CjGokdX5.cjs.js');
9
9
  require('zod');
10
10
  require('express');
11
11
  require('express-promise-router');
@@ -73,10 +73,14 @@ const permissionPlugin = backendPluginApi.createBackendPlugin({
73
73
  userInfo
74
74
  })
75
75
  );
76
+ http.addAuthPolicy({
77
+ path: "/health",
78
+ allow: "unauthenticated"
79
+ });
76
80
  }
77
81
  });
78
82
  }
79
83
  });
80
84
 
81
- exports["default"] = permissionPlugin;
85
+ exports.default = permissionPlugin;
82
86
  //# sourceMappingURL=alpha.cjs.js.map
@@ -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 auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n },\n async init({\n http,\n config,\n logger,\n discovery,\n auth,\n httpAuth,\n userInfo,\n }) {\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 logger: winstonLogger,\n policy: policies.policy,\n auth,\n httpAuth,\n userInfo,\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,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,UAAUA,6BAAa,CAAA,QAAA;AAAA,OACzB;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,OACC,EAAA;AACD,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,MAAQ,EAAA,aAAA;AAAA,YACR,QAAQ,QAAS,CAAA,MAAA;AAAA,YACjB,IAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,WACD,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
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 auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n },\n async init({\n http,\n config,\n logger,\n discovery,\n auth,\n httpAuth,\n userInfo,\n }) {\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 logger: winstonLogger,\n policy: policies.policy,\n auth,\n httpAuth,\n userInfo,\n }),\n );\n http.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\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,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,UAAUA,6BAAa,CAAA,QAAA;AAAA,OACzB;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,OACC,EAAA;AACD,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,MAAQ,EAAA,aAAA;AAAA,YACR,QAAQ,QAAS,CAAA,MAAA;AAAA,YACjB,IAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,WACD,CAAA;AAAA,SACH,CAAA;AACA,QAAA,IAAA,CAAK,aAAc,CAAA;AAAA,UACjB,IAAM,EAAA,SAAA;AAAA,UACN,KAAO,EAAA,iBAAA;AAAA,SACR,CAAA,CAAA;AAAA,OACH;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
@@ -10,12 +10,12 @@ var fetch = require('node-fetch');
10
10
  var lodash = require('lodash');
11
11
  var DataLoader = require('dataloader');
12
12
 
13
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
13
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
14
14
 
15
- var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
16
- var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
17
- var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
18
- var DataLoader__default = /*#__PURE__*/_interopDefaultLegacy(DataLoader);
15
+ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
16
+ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
17
+ var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
18
+ var DataLoader__default = /*#__PURE__*/_interopDefaultCompat(DataLoader);
19
19
 
20
20
  var __defProp = Object.defineProperty;
21
21
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -45,7 +45,7 @@ class PermissionIntegrationClient {
45
45
  onBehalfOf: credentials,
46
46
  targetPluginId: pluginId
47
47
  }).then((t) => t.token);
48
- const response = await fetch__default["default"](endpoint, {
48
+ const response = await fetch__default.default(endpoint, {
49
49
  method: "POST",
50
50
  body: JSON.stringify({
51
51
  items: decisions.map(
@@ -63,9 +63,7 @@ class PermissionIntegrationClient {
63
63
  }
64
64
  });
65
65
  if (!response.ok) {
66
- throw new Error(
67
- `Unexpected response from plugin upstream when applying conditions. Expected 200 but got ${response.status} - ${response.statusText}`
68
- );
66
+ throw await errors.ResponseError.fromResponse(response);
69
67
  }
70
68
  const result = responseSchema.parse(await response.json());
71
69
  return result.items;
@@ -103,7 +101,7 @@ const evaluatePermissionRequestBatchSchema = zod.z.object({
103
101
  });
104
102
  const handleRequest = async (requests, policy, permissionIntegrationClient, credentials, auth, userInfo) => {
105
103
  const applyConditionsLoaderFor = lodash.memoize((pluginId) => {
106
- return new DataLoader__default["default"](
104
+ return new DataLoader__default.default(
107
105
  (batch) => permissionIntegrationClient.applyConditions(pluginId, credentials, batch)
108
106
  );
109
107
  });
@@ -170,8 +168,8 @@ async function createRouter(options) {
170
168
  discovery,
171
169
  auth
172
170
  });
173
- const router = Router__default["default"]();
174
- router.use(express__default["default"].json());
171
+ const router = Router__default.default();
172
+ router.use(express__default.default.json());
175
173
  router.get("/health", (_, response) => {
176
174
  response.json({ status: "ok" });
177
175
  });
@@ -205,4 +203,4 @@ async function createRouter(options) {
205
203
  }
206
204
 
207
205
  exports.createRouter = createRouter;
208
- //# sourceMappingURL=router-ced2cd8a.cjs.js.map
206
+ //# sourceMappingURL=router-CjGokdX5.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-CjGokdX5.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';\nimport { ResponseError } from '@backstage/errors';\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 await ResponseError.fromResponse(response);\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","ResponseError","memoize","DataLoader","isResourcePermission","createLegacyAuthAdapters","Router","express","InputError","errorHandler"],"mappings":"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"}
package/dist/index.cjs.js CHANGED
@@ -1,8 +1,6 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var router = require('./cjs/router-ced2cd8a.cjs.js');
3
+ var router = require('./cjs/router-CjGokdX5.cjs.js');
6
4
  require('zod');
7
5
  require('express');
8
6
  require('express-promise-router');
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -29,4 +29,4 @@ interface RouterOptions {
29
29
  */
30
30
  declare function createRouter(options: RouterOptions): Promise<express.Router>;
31
31
 
32
- export { RouterOptions, createRouter };
32
+ export { type RouterOptions, createRouter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-permission-backend",
3
- "version": "0.5.37-next.2",
3
+ "version": "0.5.38",
4
4
  "backstage": {
5
5
  "role": "backend-plugin"
6
6
  },
@@ -43,13 +43,13 @@
43
43
  "test": "backstage-cli package test"
44
44
  },
45
45
  "dependencies": {
46
- "@backstage/backend-common": "^0.21.4-next.2",
47
- "@backstage/backend-plugin-api": "^0.6.14-next.2",
48
- "@backstage/config": "^1.2.0-next.1",
49
- "@backstage/errors": "^1.2.4-next.0",
50
- "@backstage/plugin-auth-node": "^0.4.9-next.2",
51
- "@backstage/plugin-permission-common": "^0.7.13-next.1",
52
- "@backstage/plugin-permission-node": "^0.7.25-next.2",
46
+ "@backstage/backend-common": "^0.21.4",
47
+ "@backstage/backend-plugin-api": "^0.6.14",
48
+ "@backstage/config": "^1.2.0",
49
+ "@backstage/errors": "^1.2.4",
50
+ "@backstage/plugin-auth-node": "^0.4.9",
51
+ "@backstage/plugin-permission-common": "^0.7.13",
52
+ "@backstage/plugin-permission-node": "^0.7.25",
53
53
  "@types/express": "*",
54
54
  "dataloader": "^2.0.0",
55
55
  "express": "^4.17.1",
@@ -61,8 +61,8 @@
61
61
  "zod": "^3.22.4"
62
62
  },
63
63
  "devDependencies": {
64
- "@backstage/backend-test-utils": "^0.3.4-next.2",
65
- "@backstage/cli": "^0.25.3-next.2",
64
+ "@backstage/backend-test-utils": "^0.3.4",
65
+ "@backstage/cli": "^0.26.0",
66
66
  "@types/lodash": "^4.14.151",
67
67
  "@types/supertest": "^2.0.8",
68
68
  "msw": "^1.0.0",
@@ -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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"}