@backstage/backend-app-api 0.7.5 → 0.7.6-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,44 @@
1
1
  # @backstage/backend-app-api
2
2
 
3
- ## 0.7.5
3
+ ## 0.7.6-next.1
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - da155d1: Fixing issue with log meta fields possibly being circular refs
7
+ - 398b82a: Add support for JWKS tokens in ExternalTokenHandler.
8
+ - 9e63318: Added an optional `accessRestrictions` to external access service tokens and service principals in general, such that you can limit their access to certain plugins or permissions.
9
+ - Updated dependencies
10
+ - @backstage/backend-tasks@0.5.24-next.1
11
+ - @backstage/backend-plugin-api@0.6.19-next.1
12
+ - @backstage/plugin-permission-node@0.7.30-next.1
13
+ - @backstage/backend-common@0.23.0-next.1
14
+ - @backstage/cli-node@0.2.6-next.0
15
+ - @backstage/config-loader@1.8.0
16
+ - @backstage/plugin-auth-node@0.4.14-next.1
8
17
 
9
- ## 0.7.4
18
+ ## 0.7.6-next.0
10
19
 
11
20
  ### Patch Changes
12
21
 
13
- - ce87d0e: Fixed a potential crash when passing an object with a `null` prototype as log meta.
22
+ - b7de623: Fixed a potential crash when passing an object with a `null` prototype as log meta.
23
+ - 7d30d95: Fixing issue with log meta fields possibly being circular refs
24
+ - 6a576dc: Stop using `getVoidLogger` in tests to reduce the dependency on the soon-to-deprecate `backstage-common` package.
25
+ - 6551b3d: Deprecated core service factories and implementations and moved them over to
26
+ subpath exports on `@backstage/backend-defaults` instead. E.g.
27
+ `@backstage/backend-defaults/scheduler` is where the service factory and default
28
+ implementation of `coreServices.scheduler` now lives.
29
+ - d617103: Updating the logger redaction message to something less dramatic
30
+ - Updated dependencies
31
+ - @backstage/cli-node@0.2.6-next.0
32
+ - @backstage/backend-tasks@0.5.24-next.0
33
+ - @backstage/backend-common@0.22.1-next.0
34
+ - @backstage/backend-plugin-api@0.6.19-next.0
35
+ - @backstage/plugin-auth-node@0.4.14-next.0
36
+ - @backstage/plugin-permission-node@0.7.30-next.0
37
+ - @backstage/config-loader@1.8.0
38
+ - @backstage/cli-common@0.1.13
39
+ - @backstage/config@1.2.0
40
+ - @backstage/errors@1.2.4
41
+ - @backstage/types@1.1.1
14
42
 
15
43
  ## 0.7.3
16
44
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-app-api",
3
- "version": "0.7.5",
3
+ "version": "0.7.6-next.1",
4
4
  "main": "../dist/alpha.cjs.js",
5
5
  "types": "../dist/alpha.d.ts"
6
6
  }
package/config.d.ts CHANGED
@@ -88,6 +88,46 @@ export interface Config {
88
88
  */
89
89
  subject: string;
90
90
  };
91
+ /**
92
+ * Restricts what types of access that are permitted for this access
93
+ * method. If no access restrictions are given, it'll have unlimited
94
+ * access. This access restriction applies for the framework level;
95
+ * individual plugins may have their own access control mechanisms
96
+ * on top of this.
97
+ */
98
+ accessRestrictions?: Array<{
99
+ /**
100
+ * Permit access to make requests to this plugin.
101
+ *
102
+ * Can be further refined by setting additional fields below.
103
+ */
104
+ plugin: string;
105
+ /**
106
+ * If given, this method is limited to only performing actions
107
+ * with these named permissions in this plugin.
108
+ *
109
+ * Note that this only applies where permissions checks are
110
+ * enabled in the first place. Endpoints that are not protected by
111
+ * the permissions system at all, are not affected by this
112
+ * setting.
113
+ */
114
+ permission?: string | Array<string>;
115
+ /**
116
+ * If given, this method is limited to only performing actions
117
+ * whose permissions have these attributes.
118
+ *
119
+ * Note that this only applies where permissions checks are
120
+ * enabled in the first place. Endpoints that are not protected by
121
+ * the permissions system at all, are not affected by this
122
+ * setting.
123
+ */
124
+ permissionAttribute?: {
125
+ /**
126
+ * One of more of 'create', 'read', 'update', or 'delete'.
127
+ */
128
+ action?: string | Array<string>;
129
+ };
130
+ }>;
91
131
  }
92
132
  | {
93
133
  /**
@@ -130,6 +170,87 @@ export interface Config {
130
170
  */
131
171
  subject: string;
132
172
  };
173
+ /**
174
+ * Restricts what types of access that are permitted for this access
175
+ * method. If no access restrictions are given, it'll have unlimited
176
+ * access. This access restriction applies for the framework level;
177
+ * individual plugins may have their own access control mechanisms
178
+ * on top of this.
179
+ */
180
+ accessRestrictions?: Array<{
181
+ /**
182
+ * Permit access to make requests to this plugin.
183
+ *
184
+ * Can be further refined by setting additional fields below.
185
+ */
186
+ plugin: string;
187
+ /**
188
+ * If given, this method is limited to only performing actions
189
+ * with these named permissions in this plugin.
190
+ *
191
+ * Note that this only applies where permissions checks are
192
+ * enabled in the first place. Endpoints that are not protected by
193
+ * the permissions system at all, are not affected by this
194
+ * setting.
195
+ */
196
+ permission?: string | Array<string>;
197
+ /**
198
+ * If given, this method is limited to only performing actions
199
+ * whose permissions have these attributes.
200
+ *
201
+ * Note that this only applies where permissions checks are
202
+ * enabled in the first place. Endpoints that are not protected by
203
+ * the permissions system at all, are not affected by this
204
+ * setting.
205
+ */
206
+ permissionAttribute?: {
207
+ /**
208
+ * One of more of 'create', 'read', 'update', or 'delete'.
209
+ */
210
+ action?: string | Array<string>;
211
+ };
212
+ }>;
213
+ }
214
+ | {
215
+ /**
216
+ * This access method consists of a JWKS endpoint that can be used to
217
+ * verify JWT tokens.
218
+ *
219
+ * Callers generate JWT tokens via 3rd party tooling
220
+ * and pass them in the Authorization header:
221
+ *
222
+ * ```
223
+ * Authorization: Bearer eZv5o+fW3KnR3kVabMW4ZcDNLPl8nmMW
224
+ * ```
225
+ */
226
+ type: 'jwks';
227
+ options: {
228
+ /**
229
+ * The full URL of the JWKS endpoint.
230
+ */
231
+ url: string;
232
+ /**
233
+ * Sets the algorithm(s) that should be used to verify the JWT tokens.
234
+ * The passed JWTs must have been signed using one of the listed algorithms.
235
+ */
236
+ algorithm?: string | string[];
237
+ /**
238
+ * Sets the issuer(s) that should be used to verify the JWT tokens.
239
+ * Passed JWTs must have an `iss` claim which matches one of the specified issuers.
240
+ */
241
+ issuer?: string | string[];
242
+ /**
243
+ * Sets the audience(s) that should be used to verify the JWT tokens.
244
+ * The passed JWTs must have an "aud" claim that matches one of the audiences specified,
245
+ * or have no audience specified.
246
+ */
247
+ audience?: string | string[];
248
+ /**
249
+ * Sets an optional subject prefix. Passes the subject to called plugins.
250
+ * Useful for debugging and tracking purposes.
251
+ */
252
+ subjectPrefix?: string;
253
+ };
133
254
  }
134
255
  >;
135
256
  };
package/dist/alpha.cjs.js CHANGED
@@ -55,7 +55,6 @@ class PackageDiscoveryService {
55
55
  return [...includedPackages].filter((name) => !excludedPackagesSet.has(name));
56
56
  }
57
57
  async getBackendFeatures() {
58
- var _a;
59
58
  const packagesConfig = this.config.getOptional("backend.packages");
60
59
  if (!packagesConfig || Object.keys(packagesConfig).length === 0) {
61
60
  return { features: [] };
@@ -72,7 +71,7 @@ class PackageDiscoveryService {
72
71
  const depPkg = require(require.resolve(`${name}/package.json`, {
73
72
  paths: [packageDir]
74
73
  }));
75
- if (!((_a = depPkg == null ? void 0 : depPkg.backstage) == null ? void 0 : _a.role) || !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)) {
74
+ if (!depPkg?.backstage?.role || !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)) {
76
75
  continue;
77
76
  }
78
77
  const exportedModulePaths = [
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.cjs.js","sources":["../src/alpha/featureDiscoveryServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2023 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 BackendFeature,\n RootConfigService,\n RootLoggerService,\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport {\n featureDiscoveryServiceRef,\n FeatureDiscoveryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport { resolve as resolvePath, dirname } from 'path';\nimport fs from 'fs-extra';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nclass PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (\n !depPkg?.backstage?.role ||\n !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)\n ) {\n continue; // Not a backstage backend package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n\n/** @alpha */\nexport const featureDiscoveryServiceFactory = createServiceFactory({\n service: featureDiscoveryServiceRef,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n factory({ config, logger }) {\n return new PackageDiscoveryService(config, logger);\n },\n});\n\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n typeof value === 'object' &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n"],"names":["path","resolvePath","fs","dirname","createServiceFactory","featureDiscoveryServiceRef","coreServices"],"mappings":"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"}
1
+ {"version":3,"file":"alpha.cjs.js","sources":["../src/alpha/featureDiscoveryServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2023 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 BackendFeature,\n RootConfigService,\n RootLoggerService,\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport {\n featureDiscoveryServiceRef,\n FeatureDiscoveryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport { resolve as resolvePath, dirname } from 'path';\nimport fs from 'fs-extra';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nclass PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (\n !depPkg?.backstage?.role ||\n !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)\n ) {\n continue; // Not a backstage backend package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n\n/** @alpha */\nexport const featureDiscoveryServiceFactory = createServiceFactory({\n service: featureDiscoveryServiceRef,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n factory({ config, logger }) {\n return new PackageDiscoveryService(config, logger);\n },\n});\n\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n typeof value === 'object' &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n"],"names":["path","resolvePath","fs","dirname","createServiceFactory","featureDiscoveryServiceRef","coreServices"],"mappings":"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"}