@backstage/backend-defaults 0.13.0 → 0.13.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/PackageDiscoveryService.cjs.js +2 -0
- package/dist/PackageDiscoveryService.cjs.js.map +1 -1
- package/dist/alpha/entrypoints/actions/DefaultActionsService.cjs.js +4 -0
- package/dist/alpha/entrypoints/actions/DefaultActionsService.cjs.js.map +1 -1
- package/dist/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.cjs.js +5 -1
- package/dist/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.cjs.js.map +1 -1
- package/dist/database.d.ts +1 -1
- package/dist/entrypoints/auditor/WinstonRootAuditorService.cjs.js +1 -0
- package/dist/entrypoints/auditor/WinstonRootAuditorService.cjs.js.map +1 -1
- package/dist/entrypoints/auth/DefaultAuthService.cjs.js +6 -0
- package/dist/entrypoints/auth/DefaultAuthService.cjs.js.map +1 -1
- package/dist/entrypoints/auth/JwksClient.cjs.js +3 -2
- package/dist/entrypoints/auth/JwksClient.cjs.js.map +1 -1
- package/dist/entrypoints/auth/external/ExternalAuthTokenHandler.cjs.js +6 -4
- package/dist/entrypoints/auth/external/ExternalAuthTokenHandler.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/PluginTokenHandler.cjs.js +14 -8
- package/dist/entrypoints/auth/plugin/PluginTokenHandler.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/DatabaseKeyStore.cjs.js +6 -4
- package/dist/entrypoints/auth/plugin/keys/DatabaseKeyStore.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/DatabasePluginKeySource.cjs.js +6 -2
- package/dist/entrypoints/auth/plugin/keys/DatabasePluginKeySource.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.cjs.js +2 -0
- package/dist/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.cjs.js.map +1 -1
- package/dist/entrypoints/auth/user/UserTokenHandler.cjs.js +6 -4
- package/dist/entrypoints/auth/user/UserTokenHandler.cjs.js.map +1 -1
- package/dist/entrypoints/database/DatabaseManager.cjs.js +9 -3
- package/dist/entrypoints/database/DatabaseManager.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/mysql.cjs.js +2 -0
- package/dist/entrypoints/database/connectors/mysql.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/postgres.cjs.js +2 -0
- package/dist/entrypoints/database/connectors/postgres.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/sqlite3.cjs.js +1 -0
- package/dist/entrypoints/database/connectors/sqlite3.cjs.js.map +1 -1
- package/dist/entrypoints/lifecycle/lifecycleServiceFactory.cjs.js +2 -0
- package/dist/entrypoints/lifecycle/lifecycleServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootHealth/rootHealthServiceFactory.cjs.js +2 -1
- package/dist/entrypoints/rootHealth/rootHealthServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootLifecycle/rootLifecycleServiceFactory.cjs.js +1 -0
- package/dist/entrypoints/rootLifecycle/rootLifecycleServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/LocalTaskWorker.cjs.js +9 -6
- package/dist/entrypoints/scheduler/lib/LocalTaskWorker.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.cjs.js +11 -8
- package/dist/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/TaskWorker.cjs.js +9 -4
- package/dist/entrypoints/scheduler/lib/TaskWorker.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.cjs.js +8 -5
- package/dist/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AwsS3UrlReader.cjs.js +8 -5
- package/dist/entrypoints/urlReader/lib/AwsS3UrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.cjs.js +9 -6
- package/dist/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AzureUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/AzureUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/BitbucketCloudUrlReader.cjs.js +12 -10
- package/dist/entrypoints/urlReader/lib/BitbucketCloudUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/BitbucketServerUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/BitbucketServerUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/BitbucketUrlReader.cjs.js +25 -13
- package/dist/entrypoints/urlReader/lib/BitbucketUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GerritUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/GerritUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GiteaUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/GiteaUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GithubUrlReader.cjs.js +11 -9
- package/dist/entrypoints/urlReader/lib/GithubUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/HarnessUrlReader.cjs.js +6 -4
- package/dist/entrypoints/urlReader/lib/HarnessUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.cjs.js +4 -3
- package/dist/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/ReadableArrayResponse.cjs.js +4 -2
- package/dist/entrypoints/urlReader/lib/tree/ReadableArrayResponse.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/TarArchiveResponse.cjs.js +7 -1
- package/dist/entrypoints/urlReader/lib/tree/TarArchiveResponse.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/ZipArchiveResponse.cjs.js +6 -1
- package/dist/entrypoints/urlReader/lib/tree/ZipArchiveResponse.cjs.js.map +1 -1
- package/dist/package.json.cjs.js +1 -1
- package/dist/urlReader.d.ts +12 -11
- package/package.json +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @backstage/backend-defaults
|
|
2
2
|
|
|
3
|
+
## 0.13.1-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9bcfa77: Adjusted the log line wording of task worker starting
|
|
8
|
+
- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
|
|
9
|
+
- b2f6a5a: Fix #31348 issue where BitbucketUrlReader ignored provided token and instead always used integration credentials
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
- @backstage/plugin-events-node@0.4.17-next.0
|
|
12
|
+
- @backstage/plugin-auth-node@0.6.9-next.0
|
|
13
|
+
- @backstage/backend-app-api@1.2.9-next.0
|
|
14
|
+
- @backstage/config-loader@1.10.6-next.0
|
|
15
|
+
- @backstage/config@1.3.6-next.0
|
|
16
|
+
- @backstage/cli-node@0.2.15-next.0
|
|
17
|
+
- @backstage/integration@1.18.2-next.0
|
|
18
|
+
- @backstage/integration-aws-node@0.1.19-next.0
|
|
19
|
+
- @backstage/plugin-permission-node@0.10.6-next.0
|
|
20
|
+
- @backstage/backend-dev-utils@0.1.5
|
|
21
|
+
- @backstage/backend-plugin-api@1.4.5-next.0
|
|
22
|
+
- @backstage/errors@1.2.7
|
|
23
|
+
- @backstage/types@1.2.2
|
|
24
|
+
|
|
3
25
|
## 0.13.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageDiscoveryService.cjs.js","sources":["../src/PackageDiscoveryService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 fs from 'fs-extra';\nimport { resolve as resolvePath, dirname } from 'path';\n\nimport {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { BackstagePackageJson } from '@backstage/cli-node';\nimport { isError } from '@backstage/errors';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n ['object', 'function'].includes(typeof value) &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\n/** @internal */\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\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 */\nexport class PackageDiscoveryService {\n
|
|
1
|
+
{"version":3,"file":"PackageDiscoveryService.cjs.js","sources":["../src/PackageDiscoveryService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 fs from 'fs-extra';\nimport { resolve as resolvePath, dirname } from 'path';\n\nimport {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { BackstagePackageJson } from '@backstage/cli-node';\nimport { isError } from '@backstage/errors';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n ['object', 'function'].includes(typeof value) &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\n/** @internal */\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\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 */\nexport class PackageDiscoveryService {\n private readonly config: RootConfigService;\n private readonly logger: RootLoggerService;\n\n constructor(config: RootConfigService, logger: RootLoggerService) {\n this.config = config;\n this.logger = logger;\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 let depPkg: BackstagePackageJson;\n try {\n const packageJsonPath = require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n });\n depPkg = require(packageJsonPath) as BackstagePackageJson;\n } catch (error) {\n // Handle packages with \"exports\" field that don't export ./package.json\n if (isError(error) && error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {\n continue; // Skip packages that don't export package.json - they can't be Backstage packages\n }\n throw error;\n }\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: Array.from(new Set(features)) };\n }\n}\n"],"names":["resolvePath","fs","dirname","isError"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultActionsService.cjs.js","sources":["../../../../src/alpha/entrypoints/actions/DefaultActionsService.ts"],"sourcesContent":["/*\n * Copyright 2025 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 AuthService,\n BackstageCredentials,\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport {\n ActionsService,\n ActionsServiceAction,\n} from '@backstage/backend-plugin-api/alpha';\n\nexport class DefaultActionsService implements ActionsService {\n private
|
|
1
|
+
{"version":3,"file":"DefaultActionsService.cjs.js","sources":["../../../../src/alpha/entrypoints/actions/DefaultActionsService.ts"],"sourcesContent":["/*\n * Copyright 2025 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 AuthService,\n BackstageCredentials,\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport {\n ActionsService,\n ActionsServiceAction,\n} from '@backstage/backend-plugin-api/alpha';\n\nexport class DefaultActionsService implements ActionsService {\n private readonly discovery: DiscoveryService;\n private readonly config: RootConfigService;\n private readonly logger: LoggerService;\n private readonly auth: AuthService;\n\n private constructor(\n discovery: DiscoveryService,\n config: RootConfigService,\n logger: LoggerService,\n auth: AuthService,\n ) {\n this.discovery = discovery;\n this.config = config;\n this.logger = logger;\n this.auth = auth;\n }\n\n static create({\n discovery,\n config,\n logger,\n auth,\n }: {\n discovery: DiscoveryService;\n config: RootConfigService;\n logger: LoggerService;\n auth: AuthService;\n }) {\n return new DefaultActionsService(discovery, config, logger, auth);\n }\n\n async list({ credentials }: { credentials: BackstageCredentials }) {\n const pluginSources =\n this.config.getOptionalStringArray('backend.actions.pluginSources') ?? [];\n\n const remoteActionsList = await Promise.all(\n pluginSources.map(async source => {\n try {\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions`,\n pluginId: source,\n credentials,\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n const { actions } = (await response.json()) as {\n actions: ActionsServiceAction;\n };\n\n return actions;\n } catch (error) {\n this.logger.warn(`Failed to fetch actions from ${source}`, error);\n return [];\n }\n }),\n );\n\n return { actions: remoteActionsList.flat() };\n }\n\n async invoke(opts: {\n id: string;\n input?: JsonObject;\n credentials: BackstageCredentials;\n }) {\n const pluginId = this.pluginIdFromActionId(opts.id);\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions/${encodeURIComponent(\n opts.id,\n )}/invoke`,\n pluginId,\n credentials: opts.credentials,\n options: {\n method: 'POST',\n body: JSON.stringify(opts.input),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const { output } = await response.json();\n return { output };\n }\n\n private async makeRequest(opts: {\n path: string;\n pluginId: string;\n options?: RequestInit;\n credentials: BackstageCredentials;\n }) {\n const { path, pluginId, credentials, options } = opts;\n const baseUrl = await this.discovery.getBaseUrl(pluginId);\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: opts.pluginId,\n });\n\n return fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n ...options?.headers,\n Authorization: `Bearer ${token}`,\n },\n });\n }\n\n private pluginIdFromActionId(id: string): string {\n const colonIndex = id.indexOf(':');\n if (colonIndex === -1) {\n throw new Error(`Invalid action id: ${id}`);\n }\n return id.substring(0, colonIndex);\n }\n}\n"],"names":["ResponseError"],"mappings":"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"}
|
|
@@ -12,13 +12,17 @@ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
|
12
12
|
var zodToJsonSchema__default = /*#__PURE__*/_interopDefaultCompat(zodToJsonSchema);
|
|
13
13
|
|
|
14
14
|
class DefaultActionsRegistryService {
|
|
15
|
+
actions = /* @__PURE__ */ new Map();
|
|
16
|
+
logger;
|
|
17
|
+
httpAuth;
|
|
18
|
+
auth;
|
|
19
|
+
metadata;
|
|
15
20
|
constructor(logger, httpAuth, auth, metadata) {
|
|
16
21
|
this.logger = logger;
|
|
17
22
|
this.httpAuth = httpAuth;
|
|
18
23
|
this.auth = auth;
|
|
19
24
|
this.metadata = metadata;
|
|
20
25
|
}
|
|
21
|
-
actions = /* @__PURE__ */ new Map();
|
|
22
26
|
static create({
|
|
23
27
|
httpAuth,
|
|
24
28
|
logger,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultActionsRegistryService.cjs.js","sources":["../../../../src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts"],"sourcesContent":["/*\n * Copyright 2025 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 AuthService,\n HttpAuthService,\n LoggerService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport PromiseRouter from 'express-promise-router';\nimport { Router, json } from 'express';\nimport { z, AnyZodObject } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport {\n ActionsRegistryActionOptions,\n ActionsRegistryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport {\n ForwardedError,\n InputError,\n NotAllowedError,\n NotFoundError,\n} from '@backstage/errors';\n\nexport class DefaultActionsRegistryService implements ActionsRegistryService {\n private actions: Map<string, ActionsRegistryActionOptions<any, any>> =\n new Map();\n\n private
|
|
1
|
+
{"version":3,"file":"DefaultActionsRegistryService.cjs.js","sources":["../../../../src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts"],"sourcesContent":["/*\n * Copyright 2025 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 AuthService,\n HttpAuthService,\n LoggerService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport PromiseRouter from 'express-promise-router';\nimport { Router, json } from 'express';\nimport { z, AnyZodObject } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport {\n ActionsRegistryActionOptions,\n ActionsRegistryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport {\n ForwardedError,\n InputError,\n NotAllowedError,\n NotFoundError,\n} from '@backstage/errors';\n\nexport class DefaultActionsRegistryService implements ActionsRegistryService {\n private actions: Map<string, ActionsRegistryActionOptions<any, any>> =\n new Map();\n\n private readonly logger: LoggerService;\n private readonly httpAuth: HttpAuthService;\n private readonly auth: AuthService;\n private readonly metadata: PluginMetadataService;\n\n private constructor(\n logger: LoggerService,\n httpAuth: HttpAuthService,\n auth: AuthService,\n metadata: PluginMetadataService,\n ) {\n this.logger = logger;\n this.httpAuth = httpAuth;\n this.auth = auth;\n this.metadata = metadata;\n }\n\n static create({\n httpAuth,\n logger,\n auth,\n metadata,\n }: {\n httpAuth: HttpAuthService;\n logger: LoggerService;\n auth: AuthService;\n metadata: PluginMetadataService;\n }): DefaultActionsRegistryService {\n return new DefaultActionsRegistryService(logger, httpAuth, auth, metadata);\n }\n\n createRouter(): Router {\n const router = PromiseRouter();\n router.use(json());\n\n router.get('/.backstage/actions/v1/actions', (_, res) => {\n return res.json({\n actions: Array.from(this.actions.entries()).map(([id, action]) => ({\n id,\n ...action,\n attributes: {\n // Inspired by the @modelcontextprotocol/sdk defaults for the hints.\n // https://github.com/modelcontextprotocol/typescript-sdk/blob/dd69efa1de8646bb6b195ff8d5f52e13739f4550/src/types.ts#L777-L812\n destructive: action.attributes?.destructive ?? true,\n idempotent: action.attributes?.idempotent ?? false,\n readOnly: action.attributes?.readOnly ?? false,\n },\n schema: {\n input: action.schema?.input\n ? zodToJsonSchema(action.schema.input(z))\n : zodToJsonSchema(z.object({})),\n output: action.schema?.output\n ? zodToJsonSchema(action.schema.output(z))\n : zodToJsonSchema(z.object({})),\n },\n })),\n });\n });\n\n router.post(\n '/.backstage/actions/v1/actions/:actionId/invoke',\n async (req, res) => {\n const credentials = await this.httpAuth.credentials(req);\n if (this.auth.isPrincipal(credentials, 'user')) {\n if (!credentials.principal.actor) {\n throw new NotAllowedError(\n `Actions must be invoked by a service, not a user`,\n );\n }\n } else if (this.auth.isPrincipal(credentials, 'none')) {\n throw new NotAllowedError(\n `Actions must be invoked by a service, not an anonymous request`,\n );\n }\n\n const action = this.actions.get(req.params.actionId);\n\n if (!action) {\n throw new NotFoundError(`Action \"${req.params.actionId}\" not found`);\n }\n\n const input = action.schema?.input\n ? action.schema.input(z).safeParse(req.body)\n : ({ success: true, data: undefined } as const);\n\n if (!input.success) {\n throw new InputError(\n `Invalid input to action \"${req.params.actionId}\"`,\n input.error,\n );\n }\n\n try {\n const result = await action.action({\n input: input.data,\n credentials,\n logger: this.logger,\n });\n\n const output = action.schema?.output\n ? action.schema.output(z).safeParse(result?.output)\n : ({ success: true, data: result?.output } as const);\n\n if (!output.success) {\n throw new InputError(\n `Invalid output from action \"${req.params.actionId}\"`,\n output.error,\n );\n }\n\n res.json({ output: output.data });\n } catch (error) {\n throw new ForwardedError(\n `Failed execution of action \"${req.params.actionId}\"`,\n error,\n );\n }\n },\n );\n return router;\n }\n\n register<\n TInputSchema extends AnyZodObject,\n TOutputSchema extends AnyZodObject,\n >(options: ActionsRegistryActionOptions<TInputSchema, TOutputSchema>): void {\n const id = `${this.metadata.getId()}:${options.name}`;\n\n if (this.actions.has(id)) {\n throw new Error(`Action with id \"${id}\" is already registered`);\n }\n\n this.actions.set(id, options);\n }\n}\n"],"names":["PromiseRouter","json","zodToJsonSchema","z","NotAllowedError","NotFoundError","InputError","ForwardedError"],"mappings":"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"}
|
package/dist/database.d.ts
CHANGED
|
@@ -35,7 +35,6 @@ type DatabaseManagerOptions = {
|
|
|
35
35
|
* not provided.
|
|
36
36
|
*/
|
|
37
37
|
declare class DatabaseManager {
|
|
38
|
-
private readonly impl;
|
|
39
38
|
/**
|
|
40
39
|
* Creates a {@link DatabaseManager} from `backend.database` config.
|
|
41
40
|
*
|
|
@@ -43,6 +42,7 @@ declare class DatabaseManager {
|
|
|
43
42
|
* @param options - An optional configuration object.
|
|
44
43
|
*/
|
|
45
44
|
static fromConfig(config: RootConfigService, options?: DatabaseManagerOptions): DatabaseManager;
|
|
45
|
+
private readonly impl;
|
|
46
46
|
private constructor();
|
|
47
47
|
/**
|
|
48
48
|
* Generates a DatabaseService for consumption by plugins.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WinstonRootAuditorService.cjs.js","sources":["../../../src/entrypoints/auditor/WinstonRootAuditorService.ts"],"sourcesContent":["/*\n * Copyright 2025 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 type {\n AuditorService,\n AuthService,\n HttpAuthService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport type { JsonObject } from '@backstage/types';\nimport type { Format } from 'logform';\nimport * as winston from 'winston';\nimport { WinstonLogger } from '../rootLogger';\nimport { DefaultAuditorService } from './DefaultAuditorService';\nimport { getSeverityLogLevelMappings } from './utils';\n\n/** @public */\nexport const defaultFormatter = winston.format.combine(\n winston.format.timestamp({\n format: 'YYYY-MM-DD HH:mm:ss',\n }),\n winston.format.errors({ stack: true }),\n winston.format.splat(),\n winston.format.json(),\n);\n\n/**\n * Adds `isAuditEvent` field\n *\n * @public\n */\nexport const auditorFieldFormat = winston.format(info => {\n return { ...info, isAuditEvent: true };\n})();\n\n/**\n * Options for creating a {@link WinstonRootAuditorService}.\n * @public\n */\nexport type WinstonRootAuditorServiceOptions = {\n meta?: JsonObject;\n format?: Format;\n transports?: winston.transport[];\n};\n\n/**\n * An implementation of the {@link @backstage/backend-plugin-api#AuditorService} that logs events using a separate winston logger.\n *\n * @public\n *\n * @example\n * ```ts\n * export const auditorServiceFactory = createServiceFactory({\n * service: coreServices.auditor,\n * deps: {\n * auth: coreServices.auth,\n * httpAuth: coreServices.httpAuth,\n * plugin: coreServices.pluginMetadata,\n * },\n * createRootContext() {\n * return WinstonRootAuditorService.create();\n * },\n * factory({ plugin, auth, httpAuth }, root) {\n * return root.forPlugin({ plugin, auth, httpAuth });\n * },\n * });\n * ```\n */\nexport class WinstonRootAuditorService {\n private
|
|
1
|
+
{"version":3,"file":"WinstonRootAuditorService.cjs.js","sources":["../../../src/entrypoints/auditor/WinstonRootAuditorService.ts"],"sourcesContent":["/*\n * Copyright 2025 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 type {\n AuditorService,\n AuthService,\n HttpAuthService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport type { JsonObject } from '@backstage/types';\nimport type { Format } from 'logform';\nimport * as winston from 'winston';\nimport { WinstonLogger } from '../rootLogger';\nimport { DefaultAuditorService } from './DefaultAuditorService';\nimport { getSeverityLogLevelMappings } from './utils';\n\n/** @public */\nexport const defaultFormatter = winston.format.combine(\n winston.format.timestamp({\n format: 'YYYY-MM-DD HH:mm:ss',\n }),\n winston.format.errors({ stack: true }),\n winston.format.splat(),\n winston.format.json(),\n);\n\n/**\n * Adds `isAuditEvent` field\n *\n * @public\n */\nexport const auditorFieldFormat = winston.format(info => {\n return { ...info, isAuditEvent: true };\n})();\n\n/**\n * Options for creating a {@link WinstonRootAuditorService}.\n * @public\n */\nexport type WinstonRootAuditorServiceOptions = {\n meta?: JsonObject;\n format?: Format;\n transports?: winston.transport[];\n};\n\n/**\n * An implementation of the {@link @backstage/backend-plugin-api#AuditorService} that logs events using a separate winston logger.\n *\n * @public\n *\n * @example\n * ```ts\n * export const auditorServiceFactory = createServiceFactory({\n * service: coreServices.auditor,\n * deps: {\n * auth: coreServices.auth,\n * httpAuth: coreServices.httpAuth,\n * plugin: coreServices.pluginMetadata,\n * },\n * createRootContext() {\n * return WinstonRootAuditorService.create();\n * },\n * factory({ plugin, auth, httpAuth }, root) {\n * return root.forPlugin({ plugin, auth, httpAuth });\n * },\n * });\n * ```\n */\nexport class WinstonRootAuditorService {\n private readonly winstonLogger: WinstonLogger;\n\n private constructor(winstonLogger: WinstonLogger) {\n this.winstonLogger = winstonLogger;\n }\n\n /**\n * Creates a {@link WinstonRootAuditorService} instance.\n */\n static create(\n options?: WinstonRootAuditorServiceOptions,\n ): WinstonRootAuditorService {\n let winstonLogger = WinstonLogger.create({\n meta: {\n service: 'backstage',\n },\n level: 'info',\n format: winston.format.combine(\n auditorFieldFormat,\n options?.format ?? defaultFormatter,\n ),\n transports: options?.transports,\n });\n\n if (options?.meta) {\n winstonLogger = winstonLogger.child(options.meta) as WinstonLogger;\n }\n\n return new WinstonRootAuditorService(winstonLogger);\n }\n\n forPlugin(deps: {\n auth: AuthService;\n config: Config;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n }): AuditorService {\n const severityLogLevelMappings = getSeverityLogLevelMappings(deps.config);\n\n return DefaultAuditorService.create(event => {\n if ('error' in event) {\n const { error, ...rest } = event;\n const childAuditLogger = this.winstonLogger.child(rest);\n\n childAuditLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n error,\n );\n } else {\n // the else statement is required for typechecking\n this.winstonLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n event,\n );\n }\n }, deps);\n }\n}\n"],"names":["winston","WinstonLogger","getSeverityLogLevelMappings","DefaultAuditorService"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BO,MAAM,gBAAA,GAAmBA,mBAAQ,MAAA,CAAO,OAAA;AAAA,EAC7CA,kBAAA,CAAQ,OAAO,SAAA,CAAU;AAAA,IACvB,MAAA,EAAQ;AAAA,GACT,CAAA;AAAA,EACDA,mBAAQ,MAAA,CAAO,MAAA,CAAO,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,EACrCA,kBAAA,CAAQ,OAAO,KAAA,EAAM;AAAA,EACrBA,kBAAA,CAAQ,OAAO,IAAA;AACjB;AAOO,MAAM,kBAAA,GAAqBA,kBAAA,CAAQ,MAAA,CAAO,CAAA,IAAA,KAAQ;AACvD,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,YAAA,EAAc,IAAA,EAAK;AACvC,CAAC,CAAA;AAmCM,MAAM,yBAAA,CAA0B;AAAA,EACpB,aAAA;AAAA,EAET,YAAY,aAAA,EAA8B;AAChD,IAAA,IAAA,CAAK,aAAA,GAAgB,aAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OACL,OAAA,EAC2B;AAC3B,IAAA,IAAI,aAAA,GAAgBC,4BAAc,MAAA,CAAO;AAAA,MACvC,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS;AAAA,OACX;AAAA,MACA,KAAA,EAAO,MAAA;AAAA,MACP,MAAA,EAAQD,mBAAQ,MAAA,CAAO,OAAA;AAAA,QACrB,kBAAA;AAAA,QACA,SAAS,MAAA,IAAU;AAAA,OACrB;AAAA,MACA,YAAY,OAAA,EAAS;AAAA,KACtB,CAAA;AAED,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,aAAA,GAAgB,aAAA,CAAc,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAAA,IAClD;AAEA,IAAA,OAAO,IAAI,0BAA0B,aAAa,CAAA;AAAA,EACpD;AAAA,EAEA,UAAU,IAAA,EAKS;AACjB,IAAA,MAAM,wBAAA,GAA2BE,iCAAA,CAA4B,IAAA,CAAK,MAAM,CAAA;AAExE,IAAA,OAAOC,2CAAA,CAAsB,OAAO,CAAA,KAAA,KAAS;AAC3C,MAAA,IAAI,WAAW,KAAA,EAAO;AACpB,QAAA,MAAM,EAAE,KAAA,EAAO,GAAG,IAAA,EAAK,GAAI,KAAA;AAC3B,QAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,aAAA,CAAc,KAAA,CAAM,IAAI,CAAA;AAEtD,QAAA,gBAAA,CAAiB,wBAAA,CAAyB,KAAA,CAAM,aAAa,CAAC,CAAA;AAAA,UAC5D,CAAA,EAAG,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,UAChC;AAAA,SACF;AAAA,MACF,CAAA,MAAO;AAEL,QAAA,IAAA,CAAK,aAAA,CAAc,wBAAA,CAAyB,KAAA,CAAM,aAAa,CAAC,CAAA;AAAA,UAC9D,CAAA,EAAG,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,UAChC;AAAA,SACF;AAAA,MACF;AAAA,IACF,GAAG,IAAI,CAAA;AAAA,EACT;AACF;;;;;;"}
|
|
@@ -5,6 +5,12 @@ var jose = require('jose');
|
|
|
5
5
|
var helpers = require('./helpers.cjs.js');
|
|
6
6
|
|
|
7
7
|
class DefaultAuthService {
|
|
8
|
+
userTokenHandler;
|
|
9
|
+
pluginTokenHandler;
|
|
10
|
+
externalTokenHandler;
|
|
11
|
+
pluginId;
|
|
12
|
+
disableDefaultAuthPolicy;
|
|
13
|
+
pluginKeySource;
|
|
8
14
|
constructor(userTokenHandler, pluginTokenHandler, externalTokenHandler, pluginId, disableDefaultAuthPolicy, pluginKeySource) {
|
|
9
15
|
this.userTokenHandler = userTokenHandler;
|
|
10
16
|
this.pluginTokenHandler = pluginTokenHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultAuthService.cjs.js","sources":["../../../src/entrypoints/auth/DefaultAuthService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 AuthService,\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalTypes,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport { decodeJwt } from 'jose';\nimport { ExternalAuthTokenHandler } from './external/ExternalAuthTokenHandler';\nimport {\n createCredentialsWithNonePrincipal,\n createCredentialsWithServicePrincipal,\n createCredentialsWithUserPrincipal,\n toInternalBackstageCredentials,\n} from './helpers';\nimport { PluginTokenHandler } from './plugin/PluginTokenHandler';\nimport { PluginKeySource } from './plugin/keys/types';\nimport { UserTokenHandler } from './user/UserTokenHandler';\n\n/** @internal */\nexport class DefaultAuthService implements AuthService {\n constructor(\n private readonly userTokenHandler: UserTokenHandler,\n private readonly pluginTokenHandler: PluginTokenHandler,\n private readonly externalTokenHandler: ExternalAuthTokenHandler,\n private readonly pluginId: string,\n private readonly disableDefaultAuthPolicy: boolean,\n private readonly pluginKeySource: PluginKeySource,\n ) {}\n\n async authenticate(\n token: string,\n options?: {\n allowLimitedAccess?: boolean;\n },\n ): Promise<BackstageCredentials> {\n const pluginResult = await this.pluginTokenHandler.verifyToken(token);\n if (pluginResult) {\n if (pluginResult.limitedUserToken) {\n const userResult = await this.userTokenHandler.verifyToken(\n pluginResult.limitedUserToken,\n );\n if (!userResult) {\n throw new AuthenticationError(\n 'Invalid user token in plugin token obo claim',\n );\n }\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n pluginResult.limitedUserToken,\n this.#getJwtExpiration(pluginResult.limitedUserToken),\n pluginResult.subject,\n );\n }\n return createCredentialsWithServicePrincipal(pluginResult.subject);\n }\n\n const userResult = await this.userTokenHandler.verifyToken(token);\n if (userResult) {\n if (\n !options?.allowLimitedAccess &&\n this.userTokenHandler.isLimitedUserToken(token)\n ) {\n throw new AuthenticationError('Illegal limited user token');\n }\n\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n token,\n this.#getJwtExpiration(token),\n );\n }\n\n const externalResult = await this.externalTokenHandler.verifyToken(token);\n if (externalResult) {\n return createCredentialsWithServicePrincipal(\n externalResult.subject,\n undefined,\n externalResult.accessRestrictions,\n );\n }\n\n throw new AuthenticationError('Illegal token');\n }\n\n isPrincipal<TType extends keyof BackstagePrincipalTypes>(\n credentials: BackstageCredentials,\n type: TType,\n ): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal;\n\n if (type === 'unknown') {\n return true;\n }\n\n if (principal.type !== type) {\n return false;\n }\n\n return true;\n }\n\n async getNoneCredentials(): Promise<\n BackstageCredentials<BackstageNonePrincipal>\n > {\n return createCredentialsWithNonePrincipal();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return createCredentialsWithServicePrincipal(`plugin:${this.pluginId}`);\n }\n\n async getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const { targetPluginId } = options;\n const internalForward = toInternalBackstageCredentials(options.onBehalfOf);\n const { type } = internalForward.principal;\n\n // Since disabling the default policy means we'll be allowing\n // unauthenticated requests through, we might have unauthenticated\n // credentials from service calls that reach this point. If that's the case,\n // we'll want to keep \"forwarding\" the unauthenticated credentials, which we\n // do by returning an empty token.\n if (type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n // check whether a plugin support the new auth system\n // by checking the public keys endpoint existence.\n switch (type) {\n // TODO: Check whether the principal is ourselves\n case 'service':\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n });\n case 'user': {\n const { token } = internalForward;\n if (!token) {\n throw new Error('User credentials is unexpectedly missing token');\n }\n const onBehalfOf = await this.userTokenHandler.createLimitedUserToken(\n token,\n );\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n onBehalfOf: {\n limitedUserToken: onBehalfOf.token,\n expiresAt: onBehalfOf.expiresAt,\n },\n });\n }\n default:\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${type}'`,\n );\n }\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n const { token: backstageToken } =\n toInternalBackstageCredentials(credentials);\n if (!backstageToken) {\n throw new AuthenticationError(\n 'User credentials is unexpectedly missing token',\n );\n }\n\n return this.userTokenHandler.createLimitedUserToken(backstageToken);\n }\n\n async listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n const { keys } = await this.pluginKeySource.listKeys();\n return { keys: keys.map(({ key }) => key) };\n }\n\n #getJwtExpiration(token: string) {\n const { exp } = decodeJwt(token);\n if (!exp) {\n throw new AuthenticationError('User token is missing expiration');\n }\n return new Date(exp * 1000);\n }\n}\n"],"names":["userResult","AuthenticationError","createCredentialsWithUserPrincipal","createCredentialsWithServicePrincipal","createCredentialsWithNonePrincipal","toInternalBackstageCredentials","decodeJwt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultAuthService.cjs.js","sources":["../../../src/entrypoints/auth/DefaultAuthService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 AuthService,\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalTypes,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport { decodeJwt } from 'jose';\nimport { ExternalAuthTokenHandler } from './external/ExternalAuthTokenHandler';\nimport {\n createCredentialsWithNonePrincipal,\n createCredentialsWithServicePrincipal,\n createCredentialsWithUserPrincipal,\n toInternalBackstageCredentials,\n} from './helpers';\nimport { PluginTokenHandler } from './plugin/PluginTokenHandler';\nimport { PluginKeySource } from './plugin/keys/types';\nimport { UserTokenHandler } from './user/UserTokenHandler';\n\n/** @internal */\nexport class DefaultAuthService implements AuthService {\n private readonly userTokenHandler: UserTokenHandler;\n private readonly pluginTokenHandler: PluginTokenHandler;\n private readonly externalTokenHandler: ExternalAuthTokenHandler;\n private readonly pluginId: string;\n private readonly disableDefaultAuthPolicy: boolean;\n private readonly pluginKeySource: PluginKeySource;\n\n constructor(\n userTokenHandler: UserTokenHandler,\n pluginTokenHandler: PluginTokenHandler,\n externalTokenHandler: ExternalAuthTokenHandler,\n pluginId: string,\n disableDefaultAuthPolicy: boolean,\n pluginKeySource: PluginKeySource,\n ) {\n this.userTokenHandler = userTokenHandler;\n this.pluginTokenHandler = pluginTokenHandler;\n this.externalTokenHandler = externalTokenHandler;\n this.pluginId = pluginId;\n this.disableDefaultAuthPolicy = disableDefaultAuthPolicy;\n this.pluginKeySource = pluginKeySource;\n }\n\n async authenticate(\n token: string,\n options?: {\n allowLimitedAccess?: boolean;\n },\n ): Promise<BackstageCredentials> {\n const pluginResult = await this.pluginTokenHandler.verifyToken(token);\n if (pluginResult) {\n if (pluginResult.limitedUserToken) {\n const userResult = await this.userTokenHandler.verifyToken(\n pluginResult.limitedUserToken,\n );\n if (!userResult) {\n throw new AuthenticationError(\n 'Invalid user token in plugin token obo claim',\n );\n }\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n pluginResult.limitedUserToken,\n this.#getJwtExpiration(pluginResult.limitedUserToken),\n pluginResult.subject,\n );\n }\n return createCredentialsWithServicePrincipal(pluginResult.subject);\n }\n\n const userResult = await this.userTokenHandler.verifyToken(token);\n if (userResult) {\n if (\n !options?.allowLimitedAccess &&\n this.userTokenHandler.isLimitedUserToken(token)\n ) {\n throw new AuthenticationError('Illegal limited user token');\n }\n\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n token,\n this.#getJwtExpiration(token),\n );\n }\n\n const externalResult = await this.externalTokenHandler.verifyToken(token);\n if (externalResult) {\n return createCredentialsWithServicePrincipal(\n externalResult.subject,\n undefined,\n externalResult.accessRestrictions,\n );\n }\n\n throw new AuthenticationError('Illegal token');\n }\n\n isPrincipal<TType extends keyof BackstagePrincipalTypes>(\n credentials: BackstageCredentials,\n type: TType,\n ): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal;\n\n if (type === 'unknown') {\n return true;\n }\n\n if (principal.type !== type) {\n return false;\n }\n\n return true;\n }\n\n async getNoneCredentials(): Promise<\n BackstageCredentials<BackstageNonePrincipal>\n > {\n return createCredentialsWithNonePrincipal();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return createCredentialsWithServicePrincipal(`plugin:${this.pluginId}`);\n }\n\n async getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const { targetPluginId } = options;\n const internalForward = toInternalBackstageCredentials(options.onBehalfOf);\n const { type } = internalForward.principal;\n\n // Since disabling the default policy means we'll be allowing\n // unauthenticated requests through, we might have unauthenticated\n // credentials from service calls that reach this point. If that's the case,\n // we'll want to keep \"forwarding\" the unauthenticated credentials, which we\n // do by returning an empty token.\n if (type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n // check whether a plugin support the new auth system\n // by checking the public keys endpoint existence.\n switch (type) {\n // TODO: Check whether the principal is ourselves\n case 'service':\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n });\n case 'user': {\n const { token } = internalForward;\n if (!token) {\n throw new Error('User credentials is unexpectedly missing token');\n }\n const onBehalfOf = await this.userTokenHandler.createLimitedUserToken(\n token,\n );\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n onBehalfOf: {\n limitedUserToken: onBehalfOf.token,\n expiresAt: onBehalfOf.expiresAt,\n },\n });\n }\n default:\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${type}'`,\n );\n }\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n const { token: backstageToken } =\n toInternalBackstageCredentials(credentials);\n if (!backstageToken) {\n throw new AuthenticationError(\n 'User credentials is unexpectedly missing token',\n );\n }\n\n return this.userTokenHandler.createLimitedUserToken(backstageToken);\n }\n\n async listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n const { keys } = await this.pluginKeySource.listKeys();\n return { keys: keys.map(({ key }) => key) };\n }\n\n #getJwtExpiration(token: string) {\n const { exp } = decodeJwt(token);\n if (!exp) {\n throw new AuthenticationError('User token is missing expiration');\n }\n return new Date(exp * 1000);\n }\n}\n"],"names":["userResult","AuthenticationError","createCredentialsWithUserPrincipal","createCredentialsWithServicePrincipal","createCredentialsWithNonePrincipal","toInternalBackstageCredentials","decodeJwt"],"mappings":"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"}
|
|
@@ -5,11 +5,12 @@ var jose = require('jose');
|
|
|
5
5
|
|
|
6
6
|
const CLOCK_MARGIN_S = 10;
|
|
7
7
|
class JwksClient {
|
|
8
|
+
#keyStore;
|
|
9
|
+
#keyStoreUpdated = 0;
|
|
10
|
+
getEndpoint;
|
|
8
11
|
constructor(getEndpoint) {
|
|
9
12
|
this.getEndpoint = getEndpoint;
|
|
10
13
|
}
|
|
11
|
-
#keyStore;
|
|
12
|
-
#keyStoreUpdated = 0;
|
|
13
14
|
get getKey() {
|
|
14
15
|
if (!this.#keyStore) {
|
|
15
16
|
throw new errors.AuthenticationError(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JwksClient.cjs.js","sources":["../../../src/entrypoints/auth/JwksClient.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { AuthenticationError } from '@backstage/errors';\nimport {\n createRemoteJWKSet,\n decodeJwt,\n decodeProtectedHeader,\n FlattenedJWSInput,\n JWSHeaderParameters,\n} from 'jose';\nimport { GetKeyFunction } from 'jose';\n\nconst CLOCK_MARGIN_S = 10;\n\nexport class JwksClient {\n #keyStore?: GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;\n #keyStoreUpdated: number = 0;\n\n
|
|
1
|
+
{"version":3,"file":"JwksClient.cjs.js","sources":["../../../src/entrypoints/auth/JwksClient.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { AuthenticationError } from '@backstage/errors';\nimport {\n createRemoteJWKSet,\n decodeJwt,\n decodeProtectedHeader,\n FlattenedJWSInput,\n JWSHeaderParameters,\n} from 'jose';\nimport { GetKeyFunction } from 'jose';\n\nconst CLOCK_MARGIN_S = 10;\n\nexport class JwksClient {\n #keyStore?: GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;\n #keyStoreUpdated: number = 0;\n\n private readonly getEndpoint: () => Promise<URL>;\n\n constructor(getEndpoint: () => Promise<URL>) {\n this.getEndpoint = getEndpoint;\n }\n\n get getKey() {\n if (!this.#keyStore) {\n throw new AuthenticationError(\n 'refreshKeyStore must be called before jwksClient.getKey',\n );\n }\n return this.#keyStore;\n }\n\n /**\n * If the last keystore refresh is stale, update the keystore URL to the latest\n */\n async refreshKeyStore(rawJwtToken: string): Promise<void> {\n const payload = await decodeJwt(rawJwtToken);\n const header = await decodeProtectedHeader(rawJwtToken);\n\n // Refresh public keys if needed\n let keyStoreHasKey;\n try {\n if (this.#keyStore) {\n // Check if the key is present in the keystore\n const [_, rawPayload, rawSignature] = rawJwtToken.split('.');\n keyStoreHasKey = await this.#keyStore(header, {\n payload: rawPayload,\n signature: rawSignature,\n });\n }\n } catch (error) {\n keyStoreHasKey = false;\n }\n // Refresh public key URL if needed\n // Add a small margin in case clocks are out of sync\n const issuedAfterLastRefresh =\n payload?.iat && payload.iat > this.#keyStoreUpdated - CLOCK_MARGIN_S;\n if (!this.#keyStore || (!keyStoreHasKey && issuedAfterLastRefresh)) {\n const endpoint = await this.getEndpoint();\n this.#keyStore = createRemoteJWKSet(endpoint);\n this.#keyStoreUpdated = Date.now() / 1000;\n }\n }\n}\n"],"names":["AuthenticationError","decodeJwt","decodeProtectedHeader","createRemoteJWKSet"],"mappings":";;;;;AA0BA,MAAM,cAAA,GAAiB,EAAA;AAEhB,MAAM,UAAA,CAAW;AAAA,EACtB,SAAA;AAAA,EACA,gBAAA,GAA2B,CAAA;AAAA,EAEV,WAAA;AAAA,EAEjB,YAAY,WAAA,EAAiC;AAC3C,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AAAA,EACrB;AAAA,EAEA,IAAI,MAAA,GAAS;AACX,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACnB,MAAA,MAAM,IAAIA,0BAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,WAAA,EAAoC;AACxD,IAAA,MAAM,OAAA,GAAU,MAAMC,cAAA,CAAU,WAAW,CAAA;AAC3C,IAAA,MAAM,MAAA,GAAS,MAAMC,0BAAA,CAAsB,WAAW,CAAA;AAGtD,IAAA,IAAI,cAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAI,KAAK,SAAA,EAAW;AAElB,QAAA,MAAM,CAAC,CAAA,EAAG,UAAA,EAAY,YAAY,CAAA,GAAI,WAAA,CAAY,MAAM,GAAG,CAAA;AAC3D,QAAA,cAAA,GAAiB,MAAM,IAAA,CAAK,SAAA,CAAU,MAAA,EAAQ;AAAA,UAC5C,OAAA,EAAS,UAAA;AAAA,UACT,SAAA,EAAW;AAAA,SACZ,CAAA;AAAA,MACH;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,cAAA,GAAiB,KAAA;AAAA,IACnB;AAGA,IAAA,MAAM,yBACJ,OAAA,EAAS,GAAA,IAAO,OAAA,CAAQ,GAAA,GAAM,KAAK,gBAAA,GAAmB,cAAA;AACxD,IAAA,IAAI,CAAC,IAAA,CAAK,SAAA,IAAc,CAAC,kBAAkB,sBAAA,EAAyB;AAClE,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,WAAA,EAAY;AACxC,MAAA,IAAA,CAAK,SAAA,GAAYC,wBAAmB,QAAQ,CAAA;AAC5C,MAAA,IAAA,CAAK,gBAAA,GAAmB,IAAA,CAAK,GAAA,EAAI,GAAI,GAAA;AAAA,IACvC;AAAA,EACF;AACF;;;;"}
|
|
@@ -20,10 +20,6 @@ const defaultHandlers = {
|
|
|
20
20
|
jwks: jwks.jwksTokenHandler
|
|
21
21
|
};
|
|
22
22
|
class ExternalAuthTokenHandler {
|
|
23
|
-
constructor(ownPluginId, contexts) {
|
|
24
|
-
this.ownPluginId = ownPluginId;
|
|
25
|
-
this.contexts = contexts;
|
|
26
|
-
}
|
|
27
23
|
static create(options) {
|
|
28
24
|
const {
|
|
29
25
|
ownPluginId,
|
|
@@ -82,6 +78,12 @@ class ExternalAuthTokenHandler {
|
|
|
82
78
|
}
|
|
83
79
|
return new ExternalAuthTokenHandler(ownPluginId, contexts);
|
|
84
80
|
}
|
|
81
|
+
ownPluginId;
|
|
82
|
+
contexts;
|
|
83
|
+
constructor(ownPluginId, contexts) {
|
|
84
|
+
this.ownPluginId = ownPluginId;
|
|
85
|
+
this.contexts = contexts;
|
|
86
|
+
}
|
|
85
87
|
async verifyToken(token) {
|
|
86
88
|
for (const { handler, allAccessRestrictions, context } of this.contexts) {
|
|
87
89
|
const result = await handler.verifyToken(token, context);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExternalAuthTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/external/ExternalAuthTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 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 BackstagePrincipalAccessRestrictions,\n createServiceRef,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { legacyTokenHandler } from './legacy';\nimport { staticTokenHandler } from './static';\nimport { jwksTokenHandler } from './jwks';\nimport { AccessRestrictionsMap, ExternalTokenHandler } from './types';\nimport { readAccessRestrictionsFromConfig } from './helpers';\n\nconst NEW_CONFIG_KEY = 'backend.auth.externalAccess';\nconst OLD_CONFIG_KEY = 'backend.auth.keys';\nlet loggedDeprecationWarning = false;\n\n/**\n * @public\n * This service is used to add custom handlers for external token.\n */\nexport const externalTokenHandlersServiceRef = createServiceRef<\n ExternalTokenHandler<unknown>\n>({\n id: 'core.auth.externalTokenHandlers',\n multiton: true,\n});\n\nconst defaultHandlers: Record<string, ExternalTokenHandler<unknown>> = {\n static: staticTokenHandler,\n legacy: legacyTokenHandler,\n jwks: jwksTokenHandler,\n};\n\ntype ContextMapEntry<T> = {\n context: T;\n handler: ExternalTokenHandler<T>;\n allAccessRestrictions?: AccessRestrictionsMap;\n};\n\n/**\n * Handles all types of external caller token types (i.e. not Backstage user\n * tokens, nor Backstage backend plugin tokens).\n *\n * @internal\n */\nexport class ExternalAuthTokenHandler {\n static create(options: {\n ownPluginId: string;\n config: RootConfigService;\n logger: LoggerService;\n externalTokenHandlers?: ExternalTokenHandler<unknown>[];\n }): ExternalAuthTokenHandler {\n const {\n ownPluginId,\n config,\n externalTokenHandlers: customHandlers = [],\n logger,\n } = options;\n\n const handlersTypes = customHandlers.reduce<\n Record<string, ExternalTokenHandler<unknown>>\n >(\n (acc, handler) => {\n if (acc[handler.type]) {\n throw new Error(\n `Duplicate external token handler type '${handler.type}', each handler must have a unique type`,\n );\n }\n acc[handler.type] = handler;\n return acc;\n },\n { ...defaultHandlers },\n );\n\n const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];\n const contexts: ContextMapEntry<unknown>[] = handlerConfigs.map(\n handlerConfig => {\n const type = handlerConfig.getString('type');\n\n const handler = handlersTypes[type];\n if (!handler) {\n const valid = Object.keys(handlersTypes)\n .map(h => `'${h}'`)\n .join(', ');\n throw new Error(\n `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`,\n );\n }\n return {\n context: handler.initialize({\n options: handlerConfig.getConfig('options'),\n }),\n handler,\n allAccessRestrictions: handlerConfig\n ? readAccessRestrictionsFromConfig(handlerConfig)\n : undefined,\n };\n },\n );\n\n // Load the old keys too\n const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];\n\n if (legacyConfigs.length && !loggedDeprecationWarning) {\n loggedDeprecationWarning = true;\n\n logger.warn(\n `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`,\n );\n }\n\n for (const legacyConfig of legacyConfigs) {\n contexts.push({\n context: legacyTokenHandler.initialize({\n legacy: true,\n options: legacyConfig,\n }),\n handler: legacyTokenHandler,\n allAccessRestrictions: readAccessRestrictionsFromConfig(legacyConfig),\n });\n }\n\n return new ExternalAuthTokenHandler(ownPluginId, contexts);\n }\n\n
|
|
1
|
+
{"version":3,"file":"ExternalAuthTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/external/ExternalAuthTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 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 BackstagePrincipalAccessRestrictions,\n createServiceRef,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { legacyTokenHandler } from './legacy';\nimport { staticTokenHandler } from './static';\nimport { jwksTokenHandler } from './jwks';\nimport { AccessRestrictionsMap, ExternalTokenHandler } from './types';\nimport { readAccessRestrictionsFromConfig } from './helpers';\n\nconst NEW_CONFIG_KEY = 'backend.auth.externalAccess';\nconst OLD_CONFIG_KEY = 'backend.auth.keys';\nlet loggedDeprecationWarning = false;\n\n/**\n * @public\n * This service is used to add custom handlers for external token.\n */\nexport const externalTokenHandlersServiceRef = createServiceRef<\n ExternalTokenHandler<unknown>\n>({\n id: 'core.auth.externalTokenHandlers',\n multiton: true,\n});\n\nconst defaultHandlers: Record<string, ExternalTokenHandler<unknown>> = {\n static: staticTokenHandler,\n legacy: legacyTokenHandler,\n jwks: jwksTokenHandler,\n};\n\ntype ContextMapEntry<T> = {\n context: T;\n handler: ExternalTokenHandler<T>;\n allAccessRestrictions?: AccessRestrictionsMap;\n};\n\n/**\n * Handles all types of external caller token types (i.e. not Backstage user\n * tokens, nor Backstage backend plugin tokens).\n *\n * @internal\n */\nexport class ExternalAuthTokenHandler {\n static create(options: {\n ownPluginId: string;\n config: RootConfigService;\n logger: LoggerService;\n externalTokenHandlers?: ExternalTokenHandler<unknown>[];\n }): ExternalAuthTokenHandler {\n const {\n ownPluginId,\n config,\n externalTokenHandlers: customHandlers = [],\n logger,\n } = options;\n\n const handlersTypes = customHandlers.reduce<\n Record<string, ExternalTokenHandler<unknown>>\n >(\n (acc, handler) => {\n if (acc[handler.type]) {\n throw new Error(\n `Duplicate external token handler type '${handler.type}', each handler must have a unique type`,\n );\n }\n acc[handler.type] = handler;\n return acc;\n },\n { ...defaultHandlers },\n );\n\n const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];\n const contexts: ContextMapEntry<unknown>[] = handlerConfigs.map(\n handlerConfig => {\n const type = handlerConfig.getString('type');\n\n const handler = handlersTypes[type];\n if (!handler) {\n const valid = Object.keys(handlersTypes)\n .map(h => `'${h}'`)\n .join(', ');\n throw new Error(\n `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`,\n );\n }\n return {\n context: handler.initialize({\n options: handlerConfig.getConfig('options'),\n }),\n handler,\n allAccessRestrictions: handlerConfig\n ? readAccessRestrictionsFromConfig(handlerConfig)\n : undefined,\n };\n },\n );\n\n // Load the old keys too\n const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];\n\n if (legacyConfigs.length && !loggedDeprecationWarning) {\n loggedDeprecationWarning = true;\n\n logger.warn(\n `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`,\n );\n }\n\n for (const legacyConfig of legacyConfigs) {\n contexts.push({\n context: legacyTokenHandler.initialize({\n legacy: true,\n options: legacyConfig,\n }),\n handler: legacyTokenHandler,\n allAccessRestrictions: readAccessRestrictionsFromConfig(legacyConfig),\n });\n }\n\n return new ExternalAuthTokenHandler(ownPluginId, contexts);\n }\n\n private readonly ownPluginId: string;\n private readonly contexts: {\n context: unknown;\n handler: ExternalTokenHandler<unknown>;\n allAccessRestrictions?: AccessRestrictionsMap;\n }[];\n\n constructor(\n ownPluginId: string,\n contexts: {\n context: unknown;\n handler: ExternalTokenHandler<unknown>;\n allAccessRestrictions?: AccessRestrictionsMap;\n }[],\n ) {\n this.ownPluginId = ownPluginId;\n this.contexts = contexts;\n }\n\n async verifyToken(token: string): Promise<\n | {\n subject: string;\n accessRestrictions?: BackstagePrincipalAccessRestrictions;\n }\n | undefined\n > {\n for (const { handler, allAccessRestrictions, context } of this.contexts) {\n const result = await handler.verifyToken(token, context);\n if (result) {\n if (allAccessRestrictions) {\n const accessRestrictions = allAccessRestrictions.get(\n this.ownPluginId,\n );\n if (!accessRestrictions) {\n const valid = [...allAccessRestrictions.keys()]\n .map(k => `'${k}'`)\n .join(', ');\n throw new NotAllowedError(\n `This token's access is restricted to plugin(s) ${valid}`,\n );\n }\n\n return {\n ...result,\n accessRestrictions,\n };\n }\n\n return result;\n }\n }\n\n return undefined;\n }\n}\n"],"names":["createServiceRef","staticTokenHandler","legacyTokenHandler","jwksTokenHandler","readAccessRestrictionsFromConfig","NotAllowedError"],"mappings":"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"}
|
|
@@ -9,14 +9,6 @@ var types = require('@backstage/types');
|
|
|
9
9
|
const SECONDS_IN_MS = 1e3;
|
|
10
10
|
const ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;
|
|
11
11
|
class DefaultPluginTokenHandler {
|
|
12
|
-
constructor(logger, ownPluginId, keySource, algorithm, keyDurationSeconds, discovery) {
|
|
13
|
-
this.logger = logger;
|
|
14
|
-
this.ownPluginId = ownPluginId;
|
|
15
|
-
this.keySource = keySource;
|
|
16
|
-
this.algorithm = algorithm;
|
|
17
|
-
this.keyDurationSeconds = keyDurationSeconds;
|
|
18
|
-
this.discovery = discovery;
|
|
19
|
-
}
|
|
20
12
|
jwksMap = /* @__PURE__ */ new Map();
|
|
21
13
|
// Tracking state for isTargetPluginSupported
|
|
22
14
|
supportedTargetPlugins = /* @__PURE__ */ new Set();
|
|
@@ -31,6 +23,20 @@ class DefaultPluginTokenHandler {
|
|
|
31
23
|
options.discovery
|
|
32
24
|
);
|
|
33
25
|
}
|
|
26
|
+
logger;
|
|
27
|
+
ownPluginId;
|
|
28
|
+
keySource;
|
|
29
|
+
algorithm;
|
|
30
|
+
keyDurationSeconds;
|
|
31
|
+
discovery;
|
|
32
|
+
constructor(logger, ownPluginId, keySource, algorithm, keyDurationSeconds, discovery) {
|
|
33
|
+
this.logger = logger;
|
|
34
|
+
this.ownPluginId = ownPluginId;
|
|
35
|
+
this.keySource = keySource;
|
|
36
|
+
this.algorithm = algorithm;
|
|
37
|
+
this.keyDurationSeconds = keyDurationSeconds;
|
|
38
|
+
this.discovery = discovery;
|
|
39
|
+
}
|
|
34
40
|
async verifyToken(token) {
|
|
35
41
|
try {
|
|
36
42
|
const { typ } = jose.decodeProtectedHeader(token);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/plugin/PluginTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\nimport { decodeJwt, importJWK, SignJWT, decodeProtectedHeader } from 'jose';\nimport { assertError, AuthenticationError } from '@backstage/errors';\nimport { jwtVerify } from 'jose';\nimport { tokenTypes } from '@backstage/plugin-auth-node';\nimport { JwksClient } from '../JwksClient';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { PluginKeySource } from './keys/types';\n\nconst SECONDS_IN_MS = 1000;\n\nconst ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;\n\ntype Options = {\n ownPluginId: string;\n keyDuration: HumanDuration;\n keySource: PluginKeySource;\n discovery: DiscoveryService;\n logger: LoggerService;\n /**\n * JWS \"alg\" (Algorithm) Header Parameter value. Defaults to ES256.\n * Must match one of the algorithms defined for IdentityClient.\n * When setting a different algorithm, check if the `key` field\n * of the `signing_keys` table can fit the length of the generated keys.\n * If not, add a knex migration file in the migrations folder.\n * More info on supported algorithms: https://github.com/panva/jose\n */\n algorithm?: string;\n};\n\n/**\n * @public\n * Issues and verifies {@link https://backstage.io/docs/auth/service-to-service-auth | service-to-service tokens}.\n */\nexport interface PluginTokenHandler {\n verifyToken(\n token: string,\n ): Promise<{ subject: string; limitedUserToken?: string } | undefined>;\n issueToken(options: {\n pluginId: string;\n targetPluginId: string;\n onBehalfOf?: { limitedUserToken: string; expiresAt: Date };\n }): Promise<{ token: string }>;\n}\n\nexport class DefaultPluginTokenHandler implements PluginTokenHandler {\n private jwksMap = new Map<string, JwksClient>();\n\n // Tracking state for isTargetPluginSupported\n private supportedTargetPlugins = new Set<string>();\n private targetPluginInflightChecks = new Map<string, Promise<boolean>>();\n\n static create(options: Options) {\n return new DefaultPluginTokenHandler(\n options.logger,\n options.ownPluginId,\n options.keySource,\n options.algorithm ?? 'ES256',\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.discovery,\n );\n }\n\n private constructor(\n private readonly logger: LoggerService,\n private readonly ownPluginId: string,\n private readonly keySource: PluginKeySource,\n private readonly algorithm: string,\n private readonly keyDurationSeconds: number,\n private readonly discovery: DiscoveryService,\n ) {}\n\n async verifyToken(\n token: string,\n ): Promise<{ subject: string; limitedUserToken?: string } | undefined> {\n try {\n const { typ } = decodeProtectedHeader(token);\n if (typ !== tokenTypes.plugin.typParam) {\n return undefined;\n }\n } catch {\n return undefined;\n }\n\n const pluginId = String(decodeJwt(token).sub);\n if (!pluginId) {\n throw new AuthenticationError('Invalid plugin token: missing subject');\n }\n if (!ALLOWED_PLUGIN_ID_PATTERN.test(pluginId)) {\n throw new AuthenticationError(\n 'Invalid plugin token: forbidden subject format',\n );\n }\n\n const jwksClient = await this.getJwksClient(pluginId);\n await jwksClient.refreshKeyStore(token); // TODO(Rugvip): Refactor so that this isn't needed\n\n const { payload } = await jwtVerify<{ sub: string; obo?: string }>(\n token,\n jwksClient.getKey,\n {\n typ: tokenTypes.plugin.typParam,\n audience: this.ownPluginId,\n requiredClaims: ['iat', 'exp', 'sub', 'aud'],\n },\n ).catch(e => {\n this.logger.warn('Failed to verify incoming plugin token', e);\n throw new AuthenticationError('Failed plugin token verification');\n });\n\n return { subject: `plugin:${payload.sub}`, limitedUserToken: payload.obo };\n }\n\n async issueToken(options: {\n pluginId: string;\n targetPluginId: string;\n onBehalfOf?: { limitedUserToken: string; expiresAt: Date };\n }): Promise<{ token: string }> {\n const { pluginId, targetPluginId, onBehalfOf } = options;\n const key = await this.keySource.getPrivateSigningKey();\n\n const sub = pluginId;\n const aud = targetPluginId;\n const iat = Math.floor(Date.now() / SECONDS_IN_MS);\n const ourExp = iat + this.keyDurationSeconds;\n const exp = onBehalfOf\n ? Math.min(\n ourExp,\n Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS),\n )\n : ourExp;\n\n const claims = { sub, aud, iat, exp, obo: onBehalfOf?.limitedUserToken };\n const token = await new SignJWT(claims)\n .setProtectedHeader({\n typ: tokenTypes.plugin.typParam,\n alg: this.algorithm,\n kid: key.kid,\n })\n .setAudience(aud)\n .setSubject(sub)\n .setIssuedAt(iat)\n .setExpirationTime(exp)\n .sign(await importJWK(key));\n\n return { token };\n }\n\n private async isTargetPluginSupported(\n targetPluginId: string,\n ): Promise<boolean> {\n if (this.supportedTargetPlugins.has(targetPluginId)) {\n return true;\n }\n const inFlight = this.targetPluginInflightChecks.get(targetPluginId);\n if (inFlight) {\n return inFlight;\n }\n\n const doCheck = async () => {\n try {\n const res = await fetch(\n `${await this.discovery.getBaseUrl(\n targetPluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n if (res.status === 404) {\n return false;\n }\n\n if (!res.ok) {\n throw new Error(`Failed to fetch jwks.json, ${res.status}`);\n }\n\n const data = await res.json();\n if (!data.keys) {\n throw new Error(`Invalid jwks.json response, missing keys`);\n }\n\n this.supportedTargetPlugins.add(targetPluginId);\n return true;\n } catch (error) {\n assertError(error);\n this.logger.error('Unexpected failure for target JWKS check', error);\n return false;\n } finally {\n this.targetPluginInflightChecks.delete(targetPluginId);\n }\n };\n\n const check = doCheck();\n this.targetPluginInflightChecks.set(targetPluginId, check);\n return check;\n }\n\n private async getJwksClient(pluginId: string) {\n const client = this.jwksMap.get(pluginId);\n if (client) {\n return client;\n }\n\n // Double check that the target plugin has a valid JWKS endpoint, otherwise avoid creating a remote key set\n if (!(await this.isTargetPluginSupported(pluginId))) {\n throw new AuthenticationError(\n `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint. ` +\n 'The target plugin needs to be migrated to be installed in an app using the new backend system.',\n );\n }\n\n const newClient = new JwksClient(async () => {\n return new URL(\n `${await this.discovery.getBaseUrl(\n pluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n });\n\n this.jwksMap.set(pluginId, newClient);\n return newClient;\n }\n}\n"],"names":["durationToMilliseconds","decodeProtectedHeader","tokenTypes","decodeJwt","AuthenticationError","jwtVerify","SignJWT","importJWK","assertError","JwksClient"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PluginTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/plugin/PluginTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\nimport { decodeJwt, importJWK, SignJWT, decodeProtectedHeader } from 'jose';\nimport { assertError, AuthenticationError } from '@backstage/errors';\nimport { jwtVerify } from 'jose';\nimport { tokenTypes } from '@backstage/plugin-auth-node';\nimport { JwksClient } from '../JwksClient';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { PluginKeySource } from './keys/types';\n\nconst SECONDS_IN_MS = 1000;\n\nconst ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;\n\ntype Options = {\n ownPluginId: string;\n keyDuration: HumanDuration;\n keySource: PluginKeySource;\n discovery: DiscoveryService;\n logger: LoggerService;\n /**\n * JWS \"alg\" (Algorithm) Header Parameter value. Defaults to ES256.\n * Must match one of the algorithms defined for IdentityClient.\n * When setting a different algorithm, check if the `key` field\n * of the `signing_keys` table can fit the length of the generated keys.\n * If not, add a knex migration file in the migrations folder.\n * More info on supported algorithms: https://github.com/panva/jose\n */\n algorithm?: string;\n};\n\n/**\n * @public\n * Issues and verifies {@link https://backstage.io/docs/auth/service-to-service-auth | service-to-service tokens}.\n */\nexport interface PluginTokenHandler {\n verifyToken(\n token: string,\n ): Promise<{ subject: string; limitedUserToken?: string } | undefined>;\n issueToken(options: {\n pluginId: string;\n targetPluginId: string;\n onBehalfOf?: { limitedUserToken: string; expiresAt: Date };\n }): Promise<{ token: string }>;\n}\n\nexport class DefaultPluginTokenHandler implements PluginTokenHandler {\n private jwksMap = new Map<string, JwksClient>();\n\n // Tracking state for isTargetPluginSupported\n private supportedTargetPlugins = new Set<string>();\n private targetPluginInflightChecks = new Map<string, Promise<boolean>>();\n\n static create(options: Options) {\n return new DefaultPluginTokenHandler(\n options.logger,\n options.ownPluginId,\n options.keySource,\n options.algorithm ?? 'ES256',\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.discovery,\n );\n }\n\n private readonly logger: LoggerService;\n private readonly ownPluginId: string;\n private readonly keySource: PluginKeySource;\n private readonly algorithm: string;\n private readonly keyDurationSeconds: number;\n private readonly discovery: DiscoveryService;\n\n private constructor(\n logger: LoggerService,\n ownPluginId: string,\n keySource: PluginKeySource,\n algorithm: string,\n keyDurationSeconds: number,\n discovery: DiscoveryService,\n ) {\n this.logger = logger;\n this.ownPluginId = ownPluginId;\n this.keySource = keySource;\n this.algorithm = algorithm;\n this.keyDurationSeconds = keyDurationSeconds;\n this.discovery = discovery;\n }\n\n async verifyToken(\n token: string,\n ): Promise<{ subject: string; limitedUserToken?: string } | undefined> {\n try {\n const { typ } = decodeProtectedHeader(token);\n if (typ !== tokenTypes.plugin.typParam) {\n return undefined;\n }\n } catch {\n return undefined;\n }\n\n const pluginId = String(decodeJwt(token).sub);\n if (!pluginId) {\n throw new AuthenticationError('Invalid plugin token: missing subject');\n }\n if (!ALLOWED_PLUGIN_ID_PATTERN.test(pluginId)) {\n throw new AuthenticationError(\n 'Invalid plugin token: forbidden subject format',\n );\n }\n\n const jwksClient = await this.getJwksClient(pluginId);\n await jwksClient.refreshKeyStore(token); // TODO(Rugvip): Refactor so that this isn't needed\n\n const { payload } = await jwtVerify<{ sub: string; obo?: string }>(\n token,\n jwksClient.getKey,\n {\n typ: tokenTypes.plugin.typParam,\n audience: this.ownPluginId,\n requiredClaims: ['iat', 'exp', 'sub', 'aud'],\n },\n ).catch(e => {\n this.logger.warn('Failed to verify incoming plugin token', e);\n throw new AuthenticationError('Failed plugin token verification');\n });\n\n return { subject: `plugin:${payload.sub}`, limitedUserToken: payload.obo };\n }\n\n async issueToken(options: {\n pluginId: string;\n targetPluginId: string;\n onBehalfOf?: { limitedUserToken: string; expiresAt: Date };\n }): Promise<{ token: string }> {\n const { pluginId, targetPluginId, onBehalfOf } = options;\n const key = await this.keySource.getPrivateSigningKey();\n\n const sub = pluginId;\n const aud = targetPluginId;\n const iat = Math.floor(Date.now() / SECONDS_IN_MS);\n const ourExp = iat + this.keyDurationSeconds;\n const exp = onBehalfOf\n ? Math.min(\n ourExp,\n Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS),\n )\n : ourExp;\n\n const claims = { sub, aud, iat, exp, obo: onBehalfOf?.limitedUserToken };\n const token = await new SignJWT(claims)\n .setProtectedHeader({\n typ: tokenTypes.plugin.typParam,\n alg: this.algorithm,\n kid: key.kid,\n })\n .setAudience(aud)\n .setSubject(sub)\n .setIssuedAt(iat)\n .setExpirationTime(exp)\n .sign(await importJWK(key));\n\n return { token };\n }\n\n private async isTargetPluginSupported(\n targetPluginId: string,\n ): Promise<boolean> {\n if (this.supportedTargetPlugins.has(targetPluginId)) {\n return true;\n }\n const inFlight = this.targetPluginInflightChecks.get(targetPluginId);\n if (inFlight) {\n return inFlight;\n }\n\n const doCheck = async () => {\n try {\n const res = await fetch(\n `${await this.discovery.getBaseUrl(\n targetPluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n if (res.status === 404) {\n return false;\n }\n\n if (!res.ok) {\n throw new Error(`Failed to fetch jwks.json, ${res.status}`);\n }\n\n const data = await res.json();\n if (!data.keys) {\n throw new Error(`Invalid jwks.json response, missing keys`);\n }\n\n this.supportedTargetPlugins.add(targetPluginId);\n return true;\n } catch (error) {\n assertError(error);\n this.logger.error('Unexpected failure for target JWKS check', error);\n return false;\n } finally {\n this.targetPluginInflightChecks.delete(targetPluginId);\n }\n };\n\n const check = doCheck();\n this.targetPluginInflightChecks.set(targetPluginId, check);\n return check;\n }\n\n private async getJwksClient(pluginId: string) {\n const client = this.jwksMap.get(pluginId);\n if (client) {\n return client;\n }\n\n // Double check that the target plugin has a valid JWKS endpoint, otherwise avoid creating a remote key set\n if (!(await this.isTargetPluginSupported(pluginId))) {\n throw new AuthenticationError(\n `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint. ` +\n 'The target plugin needs to be migrated to be installed in an app using the new backend system.',\n );\n }\n\n const newClient = new JwksClient(async () => {\n return new URL(\n `${await this.discovery.getBaseUrl(\n pluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n });\n\n this.jwksMap.set(pluginId, newClient);\n return newClient;\n }\n}\n"],"names":["durationToMilliseconds","decodeProtectedHeader","tokenTypes","decodeJwt","AuthenticationError","jwtVerify","SignJWT","importJWK","assertError","JwksClient"],"mappings":"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"}
|
|
@@ -16,10 +16,6 @@ function applyDatabaseMigrations(knex) {
|
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
class DatabaseKeyStore {
|
|
19
|
-
constructor(client, logger) {
|
|
20
|
-
this.client = client;
|
|
21
|
-
this.logger = logger;
|
|
22
|
-
}
|
|
23
19
|
static async create(options) {
|
|
24
20
|
const { database, logger } = options;
|
|
25
21
|
const client = await database.getClient();
|
|
@@ -28,6 +24,12 @@ class DatabaseKeyStore {
|
|
|
28
24
|
}
|
|
29
25
|
return new DatabaseKeyStore(client, logger);
|
|
30
26
|
}
|
|
27
|
+
client;
|
|
28
|
+
logger;
|
|
29
|
+
constructor(client, logger) {
|
|
30
|
+
this.client = client;
|
|
31
|
+
this.logger = logger;
|
|
32
|
+
}
|
|
31
33
|
async addKey(options) {
|
|
32
34
|
await this.client(TABLE).insert({
|
|
33
35
|
id: options.key.kid,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatabaseKeyStore.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2024 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 DatabaseService,\n LoggerService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { KeyStore } from './types';\n\nconst MIGRATIONS_TABLE = 'backstage_backend_public_keys__knex_migrations';\n\n/** @internal */\nexport const TABLE = 'backstage_backend_public_keys__keys';\n\ntype Row = {\n id: string;\n key: string;\n expires_at: string;\n};\n\nexport function applyDatabaseMigrations(knex: Knex): Promise<void> {\n const migrationsDir = resolvePackagePath(\n '@backstage/backend-defaults',\n 'migrations/auth',\n );\n\n return knex.migrate.latest({\n directory: migrationsDir,\n tableName: MIGRATIONS_TABLE,\n });\n}\n\n/** @internal */\nexport class DatabaseKeyStore implements KeyStore {\n static async create(options: {\n database: DatabaseService;\n logger: LoggerService;\n }) {\n const { database, logger } = options;\n\n const client = await database.getClient();\n if (!database.migrations?.skip) {\n await applyDatabaseMigrations(client);\n }\n return new DatabaseKeyStore(client, logger);\n }\n\n private
|
|
1
|
+
{"version":3,"file":"DatabaseKeyStore.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2024 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 DatabaseService,\n LoggerService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { KeyStore } from './types';\n\nconst MIGRATIONS_TABLE = 'backstage_backend_public_keys__knex_migrations';\n\n/** @internal */\nexport const TABLE = 'backstage_backend_public_keys__keys';\n\ntype Row = {\n id: string;\n key: string;\n expires_at: string;\n};\n\nexport function applyDatabaseMigrations(knex: Knex): Promise<void> {\n const migrationsDir = resolvePackagePath(\n '@backstage/backend-defaults',\n 'migrations/auth',\n );\n\n return knex.migrate.latest({\n directory: migrationsDir,\n tableName: MIGRATIONS_TABLE,\n });\n}\n\n/** @internal */\nexport class DatabaseKeyStore implements KeyStore {\n static async create(options: {\n database: DatabaseService;\n logger: LoggerService;\n }) {\n const { database, logger } = options;\n\n const client = await database.getClient();\n if (!database.migrations?.skip) {\n await applyDatabaseMigrations(client);\n }\n return new DatabaseKeyStore(client, logger);\n }\n\n private readonly client: Knex;\n private readonly logger: LoggerService;\n\n private constructor(client: Knex, logger: LoggerService) {\n this.client = client;\n this.logger = logger;\n }\n\n async addKey(options: {\n id: string;\n key: JsonObject & { kid: string };\n expiresAt: Date;\n }) {\n await this.client<Row>(TABLE).insert({\n id: options.key.kid,\n key: JSON.stringify(options.key),\n expires_at: options.expiresAt.toISOString(),\n });\n }\n\n async listKeys() {\n const rows = await this.client<Row>(TABLE).select();\n const keys = rows.map(row => ({\n id: row.id,\n key: JSON.parse(row.key),\n expiresAt: new Date(row.expires_at),\n }));\n\n const validKeys = [];\n const expiredKeys = [];\n\n for (const key of keys) {\n if (DateTime.fromJSDate(key.expiresAt) < DateTime.local()) {\n expiredKeys.push(key);\n } else {\n validKeys.push(key);\n }\n }\n\n // Lazily prune expired keys. This may cause duplicate removals if we have concurrent callers, but w/e\n if (expiredKeys.length > 0) {\n const kids = expiredKeys.map(({ key }) => key.kid);\n\n this.logger.info(\n `Removing expired plugin service keys, '${kids.join(\"', '\")}'`,\n );\n\n // We don't await this, just let it run in the background\n this.client<Row>(TABLE)\n .delete()\n .whereIn('id', kids)\n .catch(error => {\n this.logger.error(\n 'Failed to remove expired plugin service keys',\n error,\n );\n });\n }\n\n return { keys: validKeys };\n }\n}\n"],"names":["resolvePackagePath","DateTime"],"mappings":";;;;;AA0BA,MAAM,gBAAA,GAAmB,gDAAA;AAGlB,MAAM,KAAA,GAAQ;AAQd,SAAS,wBAAwB,IAAA,EAA2B;AACjE,EAAA,MAAM,aAAA,GAAgBA,mCAAA;AAAA,IACpB,6BAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,IAAA,CAAK,QAAQ,MAAA,CAAO;AAAA,IACzB,SAAA,EAAW,aAAA;AAAA,IACX,SAAA,EAAW;AAAA,GACZ,CAAA;AACH;AAGO,MAAM,gBAAA,CAAqC;AAAA,EAChD,aAAa,OAAO,OAAA,EAGjB;AACD,IAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAO,GAAI,OAAA;AAE7B,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,SAAA,EAAU;AACxC,IAAA,IAAI,CAAC,QAAA,CAAS,UAAA,EAAY,IAAA,EAAM;AAC9B,MAAA,MAAM,wBAAwB,MAAM,CAAA;AAAA,IACtC;AACA,IAAA,OAAO,IAAI,gBAAA,CAAiB,MAAA,EAAQ,MAAM,CAAA;AAAA,EAC5C;AAAA,EAEiB,MAAA;AAAA,EACA,MAAA;AAAA,EAET,WAAA,CAAY,QAAc,MAAA,EAAuB;AACvD,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,MAAM,OAAO,OAAA,EAIV;AACD,IAAA,MAAM,IAAA,CAAK,MAAA,CAAY,KAAK,CAAA,CAAE,MAAA,CAAO;AAAA,MACnC,EAAA,EAAI,QAAQ,GAAA,CAAI,GAAA;AAAA,MAChB,GAAA,EAAK,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,GAAG,CAAA;AAAA,MAC/B,UAAA,EAAY,OAAA,CAAQ,SAAA,CAAU,WAAA;AAAY,KAC3C,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,QAAA,GAAW;AACf,IAAA,MAAM,OAAO,MAAM,IAAA,CAAK,MAAA,CAAY,KAAK,EAAE,MAAA,EAAO;AAClD,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,GAAA,MAAQ;AAAA,MAC5B,IAAI,GAAA,CAAI,EAAA;AAAA,MACR,GAAA,EAAK,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAAA,MACvB,SAAA,EAAW,IAAI,IAAA,CAAK,GAAA,CAAI,UAAU;AAAA,KACpC,CAAE,CAAA;AAEF,IAAA,MAAM,YAAY,EAAC;AACnB,IAAA,MAAM,cAAc,EAAC;AAErB,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAIC,eAAS,UAAA,CAAW,GAAA,CAAI,SAAS,CAAA,GAAIA,cAAA,CAAS,OAAM,EAAG;AACzD,QAAA,WAAA,CAAY,KAAK,GAAG,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,SAAA,CAAU,KAAK,GAAG,CAAA;AAAA,MACpB;AAAA,IACF;AAGA,IAAA,IAAI,WAAA,CAAY,SAAS,CAAA,EAAG;AAC1B,MAAA,MAAM,IAAA,GAAO,YAAY,GAAA,CAAI,CAAC,EAAE,GAAA,EAAI,KAAM,IAAI,GAAG,CAAA;AAEjD,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,CAAA,uCAAA,EAA0C,IAAA,CAAK,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA;AAAA,OAC7D;AAGA,MAAA,IAAA,CAAK,MAAA,CAAY,KAAK,CAAA,CACnB,MAAA,EAAO,CACP,QAAQ,IAAA,EAAM,IAAI,CAAA,CAClB,KAAA,CAAM,CAAA,KAAA,KAAS;AACd,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,UACV,8CAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACL;AAEA,IAAA,OAAO,EAAE,MAAM,SAAA,EAAU;AAAA,EAC3B;AACF;;;;;;"}
|