@backstage/backend-test-utils 1.0.1-next.0 → 1.0.1-next.2

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/backend-app-api/src/lib/DependencyGraph.cjs.js +182 -0
  3. package/dist/backend-app-api/src/lib/DependencyGraph.cjs.js.map +1 -0
  4. package/dist/backend-app-api/src/wiring/ServiceRegistry.cjs.js +240 -0
  5. package/dist/backend-app-api/src/wiring/ServiceRegistry.cjs.js.map +1 -0
  6. package/dist/cache/TestCaches.cjs.js +159 -0
  7. package/dist/cache/TestCaches.cjs.js.map +1 -0
  8. package/dist/cache/memcache.cjs.js +62 -0
  9. package/dist/cache/memcache.cjs.js.map +1 -0
  10. package/dist/cache/redis.cjs.js +62 -0
  11. package/dist/cache/redis.cjs.js.map +1 -0
  12. package/dist/cache/types.cjs.js +25 -0
  13. package/dist/cache/types.cjs.js.map +1 -0
  14. package/dist/database/TestDatabases.cjs.js +128 -0
  15. package/dist/database/TestDatabases.cjs.js.map +1 -0
  16. package/dist/database/mysql.cjs.js +188 -0
  17. package/dist/database/mysql.cjs.js.map +1 -0
  18. package/dist/database/postgres.cjs.js +143 -0
  19. package/dist/database/postgres.cjs.js.map +1 -0
  20. package/dist/database/sqlite.cjs.js +40 -0
  21. package/dist/database/sqlite.cjs.js.map +1 -0
  22. package/dist/database/types.cjs.js +68 -0
  23. package/dist/database/types.cjs.js.map +1 -0
  24. package/dist/filesystem/MockDirectory.cjs.js +152 -0
  25. package/dist/filesystem/MockDirectory.cjs.js.map +1 -0
  26. package/dist/index.cjs.js +25 -2327
  27. package/dist/index.cjs.js.map +1 -1
  28. package/dist/msw/registerMswTestHooks.cjs.js +10 -0
  29. package/dist/msw/registerMswTestHooks.cjs.js.map +1 -0
  30. package/dist/next/services/MockAuthService.cjs.js +111 -0
  31. package/dist/next/services/MockAuthService.cjs.js.map +1 -0
  32. package/dist/next/services/MockHttpAuthService.cjs.js +87 -0
  33. package/dist/next/services/MockHttpAuthService.cjs.js.map +1 -0
  34. package/dist/next/services/MockRootLoggerService.cjs.js +49 -0
  35. package/dist/next/services/MockRootLoggerService.cjs.js.map +1 -0
  36. package/dist/next/services/MockUserInfoService.cjs.js +26 -0
  37. package/dist/next/services/MockUserInfoService.cjs.js.map +1 -0
  38. package/dist/next/services/mockCredentials.cjs.js +148 -0
  39. package/dist/next/services/mockCredentials.cjs.js.map +1 -0
  40. package/dist/next/services/mockServices.cjs.js +294 -0
  41. package/dist/next/services/mockServices.cjs.js.map +1 -0
  42. package/dist/next/wiring/ServiceFactoryTester.cjs.js +61 -0
  43. package/dist/next/wiring/ServiceFactoryTester.cjs.js.map +1 -0
  44. package/dist/next/wiring/TestBackend.cjs.js +258 -0
  45. package/dist/next/wiring/TestBackend.cjs.js.map +1 -0
  46. package/dist/util/errorHandler.cjs.js +18 -0
  47. package/dist/util/errorHandler.cjs.js.map +1 -0
  48. package/dist/util/getDockerImageForName.cjs.js +8 -0
  49. package/dist/util/getDockerImageForName.cjs.js.map +1 -0
  50. package/dist/util/isDockerDisabledForTests.cjs.js +8 -0
  51. package/dist/util/isDockerDisabledForTests.cjs.js.map +1 -0
  52. package/package.json +11 -11
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@backstage/errors');
4
+ var mockCredentials = require('./mockCredentials.cjs.js');
5
+
6
+ class MockAuthService {
7
+ pluginId;
8
+ disableDefaultAuthPolicy;
9
+ constructor(options) {
10
+ this.pluginId = options.pluginId;
11
+ this.disableDefaultAuthPolicy = options.disableDefaultAuthPolicy;
12
+ }
13
+ async authenticate(token, options) {
14
+ switch (token) {
15
+ case mockCredentials.MOCK_USER_TOKEN:
16
+ return mockCredentials.mockCredentials.user();
17
+ case mockCredentials.MOCK_SERVICE_TOKEN:
18
+ return mockCredentials.mockCredentials.service();
19
+ case mockCredentials.MOCK_INVALID_USER_TOKEN:
20
+ throw new errors.AuthenticationError("User token is invalid");
21
+ case mockCredentials.MOCK_INVALID_USER_LIMITED_TOKEN:
22
+ throw new errors.AuthenticationError("Limited user token is invalid");
23
+ case mockCredentials.MOCK_INVALID_SERVICE_TOKEN:
24
+ throw new errors.AuthenticationError("Service token is invalid");
25
+ case "":
26
+ throw new errors.AuthenticationError("Token is empty");
27
+ }
28
+ if (token.startsWith(mockCredentials.MOCK_USER_TOKEN_PREFIX)) {
29
+ const { sub: userEntityRef } = JSON.parse(
30
+ token.slice(mockCredentials.MOCK_USER_TOKEN_PREFIX.length)
31
+ );
32
+ return mockCredentials.mockCredentials.user(userEntityRef);
33
+ }
34
+ if (token.startsWith(mockCredentials.MOCK_USER_LIMITED_TOKEN_PREFIX)) {
35
+ if (!options?.allowLimitedAccess) {
36
+ throw new errors.AuthenticationError("Limited user token is not allowed");
37
+ }
38
+ const { sub: userEntityRef } = JSON.parse(
39
+ token.slice(mockCredentials.MOCK_USER_LIMITED_TOKEN_PREFIX.length)
40
+ );
41
+ return mockCredentials.mockCredentials.user(userEntityRef);
42
+ }
43
+ if (token.startsWith(mockCredentials.MOCK_SERVICE_TOKEN_PREFIX)) {
44
+ const { sub, target, obo } = JSON.parse(
45
+ token.slice(mockCredentials.MOCK_SERVICE_TOKEN_PREFIX.length)
46
+ );
47
+ if (target && target !== this.pluginId) {
48
+ throw new errors.AuthenticationError(
49
+ `Invalid mock token target plugin ID, got '${target}' but expected '${this.pluginId}'`
50
+ );
51
+ }
52
+ if (obo) {
53
+ return mockCredentials.mockCredentials.user(obo);
54
+ }
55
+ return mockCredentials.mockCredentials.service(sub);
56
+ }
57
+ throw new errors.AuthenticationError(`Unknown mock token '${token}'`);
58
+ }
59
+ async getNoneCredentials() {
60
+ return mockCredentials.mockCredentials.none();
61
+ }
62
+ async getOwnServiceCredentials() {
63
+ return mockCredentials.mockCredentials.service(`plugin:${this.pluginId}`);
64
+ }
65
+ isPrincipal(credentials, type) {
66
+ const principal = credentials.principal;
67
+ if (type === "unknown") {
68
+ return true;
69
+ }
70
+ if (principal.type !== type) {
71
+ return false;
72
+ }
73
+ return true;
74
+ }
75
+ async getPluginRequestToken(options) {
76
+ const principal = options.onBehalfOf.principal;
77
+ if (principal.type === "none" && this.disableDefaultAuthPolicy) {
78
+ return { token: "" };
79
+ }
80
+ if (principal.type !== "user" && principal.type !== "service") {
81
+ throw new errors.AuthenticationError(
82
+ `Refused to issue service token for credential type '${principal.type}'`
83
+ );
84
+ }
85
+ return {
86
+ token: mockCredentials.mockCredentials.service.token({
87
+ onBehalfOf: options.onBehalfOf,
88
+ targetPluginId: options.targetPluginId
89
+ })
90
+ };
91
+ }
92
+ async getLimitedUserToken(credentials) {
93
+ if (credentials.principal.type !== "user") {
94
+ throw new errors.AuthenticationError(
95
+ `Refused to issue limited user token for credential type '${credentials.principal.type}'`
96
+ );
97
+ }
98
+ return {
99
+ token: mockCredentials.mockCredentials.limitedUser.token(
100
+ credentials.principal.userEntityRef
101
+ ),
102
+ expiresAt: new Date(Date.now() + 36e5)
103
+ };
104
+ }
105
+ listPublicServiceKeys() {
106
+ throw new Error("Not implemented");
107
+ }
108
+ }
109
+
110
+ exports.MockAuthService = MockAuthService;
111
+ //# sourceMappingURL=MockAuthService.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MockAuthService.cjs.js","sources":["../../../src/next/services/MockAuthService.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 BackstageCredentials,\n BackstageServicePrincipal,\n BackstagePrincipalTypes,\n BackstageUserPrincipal,\n BackstageNonePrincipal,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport {\n mockCredentials,\n MOCK_USER_TOKEN,\n MOCK_USER_TOKEN_PREFIX,\n MOCK_INVALID_USER_TOKEN,\n MOCK_USER_LIMITED_TOKEN_PREFIX,\n MOCK_INVALID_USER_LIMITED_TOKEN,\n MOCK_SERVICE_TOKEN,\n MOCK_SERVICE_TOKEN_PREFIX,\n MOCK_INVALID_SERVICE_TOKEN,\n UserTokenPayload,\n ServiceTokenPayload,\n} from './mockCredentials';\nimport { JsonObject } from '@backstage/types';\n\n/** @internal */\nexport class MockAuthService implements AuthService {\n readonly pluginId: string;\n readonly disableDefaultAuthPolicy: boolean;\n\n constructor(options: {\n pluginId: string;\n disableDefaultAuthPolicy: boolean;\n }) {\n this.pluginId = options.pluginId;\n this.disableDefaultAuthPolicy = options.disableDefaultAuthPolicy;\n }\n\n async authenticate(\n token: string,\n options?: { allowLimitedAccess?: boolean },\n ): Promise<BackstageCredentials> {\n switch (token) {\n case MOCK_USER_TOKEN:\n return mockCredentials.user();\n case MOCK_SERVICE_TOKEN:\n return mockCredentials.service();\n case MOCK_INVALID_USER_TOKEN:\n throw new AuthenticationError('User token is invalid');\n case MOCK_INVALID_USER_LIMITED_TOKEN:\n throw new AuthenticationError('Limited user token is invalid');\n case MOCK_INVALID_SERVICE_TOKEN:\n throw new AuthenticationError('Service token is invalid');\n case '':\n throw new AuthenticationError('Token is empty');\n default:\n break;\n }\n\n if (token.startsWith(MOCK_USER_TOKEN_PREFIX)) {\n const { sub: userEntityRef }: UserTokenPayload = JSON.parse(\n token.slice(MOCK_USER_TOKEN_PREFIX.length),\n );\n\n return mockCredentials.user(userEntityRef);\n }\n\n if (token.startsWith(MOCK_USER_LIMITED_TOKEN_PREFIX)) {\n if (!options?.allowLimitedAccess) {\n throw new AuthenticationError('Limited user token is not allowed');\n }\n\n const { sub: userEntityRef }: UserTokenPayload = JSON.parse(\n token.slice(MOCK_USER_LIMITED_TOKEN_PREFIX.length),\n );\n\n return mockCredentials.user(userEntityRef);\n }\n\n if (token.startsWith(MOCK_SERVICE_TOKEN_PREFIX)) {\n const { sub, target, obo }: ServiceTokenPayload = JSON.parse(\n token.slice(MOCK_SERVICE_TOKEN_PREFIX.length),\n );\n\n if (target && target !== this.pluginId) {\n throw new AuthenticationError(\n `Invalid mock token target plugin ID, got '${target}' but expected '${this.pluginId}'`,\n );\n }\n if (obo) {\n return mockCredentials.user(obo);\n }\n\n return mockCredentials.service(sub);\n }\n\n throw new AuthenticationError(`Unknown mock token '${token}'`);\n }\n\n async getNoneCredentials() {\n return mockCredentials.none();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return mockCredentials.service(`plugin:${this.pluginId}`);\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 | BackstageNonePrincipal;\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 getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const principal = options.onBehalfOf.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal\n | BackstageNonePrincipal;\n\n if (principal.type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n if (principal.type !== 'user' && principal.type !== 'service') {\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${principal.type}'`,\n );\n }\n\n return {\n token: mockCredentials.service.token({\n onBehalfOf: options.onBehalfOf,\n targetPluginId: options.targetPluginId,\n }),\n };\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n if (credentials.principal.type !== 'user') {\n throw new AuthenticationError(\n `Refused to issue limited user token for credential type '${credentials.principal.type}'`,\n );\n }\n\n return {\n token: mockCredentials.limitedUser.token(\n credentials.principal.userEntityRef,\n ),\n expiresAt: new Date(Date.now() + 3600_000),\n };\n }\n\n listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n throw new Error('Not implemented');\n }\n}\n"],"names":["MOCK_USER_TOKEN","mockCredentials","MOCK_SERVICE_TOKEN","MOCK_INVALID_USER_TOKEN","AuthenticationError","MOCK_INVALID_USER_LIMITED_TOKEN","MOCK_INVALID_SERVICE_TOKEN","MOCK_USER_TOKEN_PREFIX","MOCK_USER_LIMITED_TOKEN_PREFIX","MOCK_SERVICE_TOKEN_PREFIX"],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ var cookie = require('cookie');
4
+ var MockAuthService = require('./MockAuthService.cjs.js');
5
+ var errors = require('@backstage/errors');
6
+ var mockCredentials = require('./mockCredentials.cjs.js');
7
+
8
+ class MockHttpAuthService {
9
+ #auth;
10
+ #defaultCredentials;
11
+ constructor(pluginId, defaultCredentials) {
12
+ this.#auth = new MockAuthService.MockAuthService({
13
+ pluginId,
14
+ disableDefaultAuthPolicy: false
15
+ });
16
+ this.#defaultCredentials = defaultCredentials;
17
+ }
18
+ async #getCredentials(req, allowLimitedAccess) {
19
+ const header = req.headers.authorization;
20
+ const token = typeof header === "string" ? header.match(/^Bearer[ ]+(\S+)$/i)?.[1] : void 0;
21
+ if (token) {
22
+ if (token === mockCredentials.MOCK_NONE_TOKEN) {
23
+ return this.#auth.getNoneCredentials();
24
+ }
25
+ return await this.#auth.authenticate(token, {
26
+ allowLimitedAccess
27
+ });
28
+ }
29
+ if (allowLimitedAccess) {
30
+ const cookieHeader = req.headers.cookie;
31
+ if (cookieHeader) {
32
+ const cookies = cookie.parse(cookieHeader);
33
+ const cookie$1 = cookies[mockCredentials.MOCK_AUTH_COOKIE];
34
+ if (cookie$1) {
35
+ return await this.#auth.authenticate(cookie$1, {
36
+ allowLimitedAccess: true
37
+ });
38
+ }
39
+ }
40
+ }
41
+ return this.#defaultCredentials;
42
+ }
43
+ async credentials(req, options) {
44
+ const credentials = await this.#getCredentials(
45
+ req,
46
+ options?.allowLimitedAccess ?? false
47
+ );
48
+ const allowedPrincipalTypes = options?.allow;
49
+ if (!allowedPrincipalTypes) {
50
+ return credentials;
51
+ }
52
+ if (this.#auth.isPrincipal(credentials, "none")) {
53
+ if (allowedPrincipalTypes.includes("none")) {
54
+ return credentials;
55
+ }
56
+ throw new errors.AuthenticationError("Missing credentials");
57
+ } else if (this.#auth.isPrincipal(credentials, "user")) {
58
+ if (allowedPrincipalTypes.includes("user")) {
59
+ return credentials;
60
+ }
61
+ throw new errors.NotAllowedError(
62
+ `This endpoint does not allow 'user' credentials`
63
+ );
64
+ } else if (this.#auth.isPrincipal(credentials, "service")) {
65
+ if (allowedPrincipalTypes.includes("service")) {
66
+ return credentials;
67
+ }
68
+ throw new errors.NotAllowedError(
69
+ `This endpoint does not allow 'service' credentials`
70
+ );
71
+ }
72
+ throw new errors.NotAllowedError(
73
+ "Unknown principal type, this should never happen"
74
+ );
75
+ }
76
+ async issueUserCookie(res, options) {
77
+ const credentials = options?.credentials ?? await this.credentials(res.req, { allow: ["user"] });
78
+ res.setHeader(
79
+ "Set-Cookie",
80
+ mockCredentials.mockCredentials.limitedUser.cookie(credentials.principal.userEntityRef)
81
+ );
82
+ return { expiresAt: new Date(Date.now() + 36e5) };
83
+ }
84
+ }
85
+
86
+ exports.MockHttpAuthService = MockHttpAuthService;
87
+ //# sourceMappingURL=MockHttpAuthService.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MockHttpAuthService.cjs.js","sources":["../../../src/next/services/MockHttpAuthService.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 BackstagePrincipalTypes,\n BackstageUserPrincipal,\n HttpAuthService,\n} from '@backstage/backend-plugin-api';\nimport { Request, Response } from 'express';\nimport { parse as parseCookie } from 'cookie';\nimport { MockAuthService } from './MockAuthService';\nimport { AuthenticationError, NotAllowedError } from '@backstage/errors';\nimport {\n MOCK_NONE_TOKEN,\n MOCK_AUTH_COOKIE,\n mockCredentials,\n} from './mockCredentials';\n\n// TODO: support mock cookie auth?\nexport class MockHttpAuthService implements HttpAuthService {\n #auth: AuthService;\n #defaultCredentials: BackstageCredentials;\n\n constructor(pluginId: string, defaultCredentials: BackstageCredentials) {\n this.#auth = new MockAuthService({\n pluginId,\n disableDefaultAuthPolicy: false,\n });\n this.#defaultCredentials = defaultCredentials;\n }\n\n async #getCredentials(req: Request, allowLimitedAccess: boolean) {\n const header = req.headers.authorization;\n const token =\n typeof header === 'string'\n ? header.match(/^Bearer[ ]+(\\S+)$/i)?.[1]\n : undefined;\n\n if (token) {\n if (token === MOCK_NONE_TOKEN) {\n return this.#auth.getNoneCredentials();\n }\n\n return await this.#auth.authenticate(token, {\n allowLimitedAccess,\n });\n }\n\n if (allowLimitedAccess) {\n const cookieHeader = req.headers.cookie;\n\n if (cookieHeader) {\n const cookies = parseCookie(cookieHeader);\n const cookie = cookies[MOCK_AUTH_COOKIE];\n\n if (cookie) {\n return await this.#auth.authenticate(cookie, {\n allowLimitedAccess: true,\n });\n }\n }\n }\n\n return this.#defaultCredentials;\n }\n\n async credentials<TAllowed extends keyof BackstagePrincipalTypes = 'unknown'>(\n req: Request,\n options?: {\n allow?: Array<TAllowed>;\n allowLimitedAccess?: boolean;\n },\n ): Promise<BackstageCredentials<BackstagePrincipalTypes[TAllowed]>> {\n const credentials = await this.#getCredentials(\n req,\n options?.allowLimitedAccess ?? false,\n );\n\n const allowedPrincipalTypes = options?.allow;\n if (!allowedPrincipalTypes) {\n return credentials as any;\n }\n\n if (this.#auth.isPrincipal(credentials, 'none')) {\n if (allowedPrincipalTypes.includes('none' as TAllowed)) {\n return credentials as any;\n }\n\n throw new AuthenticationError('Missing credentials');\n } else if (this.#auth.isPrincipal(credentials, 'user')) {\n if (allowedPrincipalTypes.includes('user' as TAllowed)) {\n return credentials as any;\n }\n\n throw new NotAllowedError(\n `This endpoint does not allow 'user' credentials`,\n );\n } else if (this.#auth.isPrincipal(credentials, 'service')) {\n if (allowedPrincipalTypes.includes('service' as TAllowed)) {\n return credentials as any;\n }\n\n throw new NotAllowedError(\n `This endpoint does not allow 'service' credentials`,\n );\n }\n\n throw new NotAllowedError(\n 'Unknown principal type, this should never happen',\n );\n }\n\n async issueUserCookie(\n res: Response,\n options?: { credentials?: BackstageCredentials<BackstageUserPrincipal> },\n ): Promise<{ expiresAt: Date }> {\n const credentials =\n options?.credentials ??\n (await this.credentials(res.req, { allow: ['user'] }));\n\n res.setHeader(\n 'Set-Cookie',\n mockCredentials.limitedUser.cookie(credentials.principal.userEntityRef),\n );\n\n return { expiresAt: new Date(Date.now() + 3600_000) };\n }\n}\n"],"names":["MockAuthService","MOCK_NONE_TOKEN","parseCookie","cookie","MOCK_AUTH_COOKIE","AuthenticationError","NotAllowedError","mockCredentials"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const levels = {
4
+ none: 0,
5
+ error: 1,
6
+ warn: 2,
7
+ info: 3,
8
+ debug: 4
9
+ };
10
+ class MockRootLoggerService {
11
+ #level;
12
+ #meta;
13
+ static create(options) {
14
+ const level = options?.level ?? "none";
15
+ if (!(level in levels)) {
16
+ throw new Error(`Invalid log level '${level}'`);
17
+ }
18
+ return new MockRootLoggerService(levels[level], {});
19
+ }
20
+ error(message, meta) {
21
+ this.#log("error", message, meta);
22
+ }
23
+ warn(message, meta) {
24
+ this.#log("warn", message, meta);
25
+ }
26
+ info(message, meta) {
27
+ this.#log("info", message, meta);
28
+ }
29
+ debug(message, meta) {
30
+ this.#log("debug", message, meta);
31
+ }
32
+ child(meta) {
33
+ return new MockRootLoggerService(this.#level, { ...this.#meta, ...meta });
34
+ }
35
+ constructor(level, meta) {
36
+ this.#level = level;
37
+ this.#meta = meta;
38
+ }
39
+ #log(level, message, meta) {
40
+ const levelValue = levels[level] ?? 0;
41
+ if (levelValue <= this.#level) {
42
+ const labels = Object.entries(this.#meta).map(([key, value]) => `${key}=${value}`).join(",");
43
+ console[level](`${labels} ${message}`, meta);
44
+ }
45
+ }
46
+ }
47
+
48
+ exports.MockRootLoggerService = MockRootLoggerService;
49
+ //# sourceMappingURL=MockRootLoggerService.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MockRootLoggerService.cjs.js","sources":["../../../src/next/services/MockRootLoggerService.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n LoggerService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport type { mockServices } from './mockServices';\n\nconst levels = {\n none: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nexport class MockRootLoggerService implements RootLoggerService {\n #level: number;\n #meta: JsonObject;\n\n static create(\n options?: mockServices.rootLogger.Options,\n ): MockRootLoggerService {\n const level = options?.level ?? 'none';\n if (!(level in levels)) {\n throw new Error(`Invalid log level '${level}'`);\n }\n return new MockRootLoggerService(levels[level], {});\n }\n\n error(message: string, meta?: JsonObject | Error | undefined): void {\n this.#log('error', message, meta);\n }\n\n warn(message: string, meta?: JsonObject | Error | undefined): void {\n this.#log('warn', message, meta);\n }\n\n info(message: string, meta?: JsonObject | Error | undefined): void {\n this.#log('info', message, meta);\n }\n\n debug(message: string, meta?: JsonObject | Error | undefined): void {\n this.#log('debug', message, meta);\n }\n\n child(meta: JsonObject): LoggerService {\n return new MockRootLoggerService(this.#level, { ...this.#meta, ...meta });\n }\n\n private constructor(level: number, meta: JsonObject) {\n this.#level = level;\n this.#meta = meta;\n }\n\n #log(\n level: 'error' | 'warn' | 'info' | 'debug',\n message: string,\n meta?: JsonObject | Error | undefined,\n ) {\n const levelValue = levels[level] ?? 0;\n if (levelValue <= this.#level) {\n const labels = Object.entries(this.#meta)\n .map(([key, value]) => `${key}=${value}`)\n .join(',');\n console[level](`${labels} ${message}`, meta);\n }\n }\n}\n"],"names":[],"mappings":";;AAuBA,MAAM,MAAS,GAAA;AAAA,EACb,IAAM,EAAA,CAAA;AAAA,EACN,KAAO,EAAA,CAAA;AAAA,EACP,IAAM,EAAA,CAAA;AAAA,EACN,IAAM,EAAA,CAAA;AAAA,EACN,KAAO,EAAA,CAAA;AACT,CAAA,CAAA;AAEO,MAAM,qBAAmD,CAAA;AAAA,EAC9D,MAAA,CAAA;AAAA,EACA,KAAA,CAAA;AAAA,EAEA,OAAO,OACL,OACuB,EAAA;AACvB,IAAM,MAAA,KAAA,GAAQ,SAAS,KAAS,IAAA,MAAA,CAAA;AAChC,IAAI,IAAA,EAAE,SAAS,MAAS,CAAA,EAAA;AACtB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAsB,mBAAA,EAAA,KAAK,CAAG,CAAA,CAAA,CAAA,CAAA;AAAA,KAChD;AACA,IAAA,OAAO,IAAI,qBAAsB,CAAA,MAAA,CAAO,KAAK,CAAA,EAAG,EAAE,CAAA,CAAA;AAAA,GACpD;AAAA,EAEA,KAAA,CAAM,SAAiB,IAA6C,EAAA;AAClE,IAAK,IAAA,CAAA,IAAA,CAAK,OAAS,EAAA,OAAA,EAAS,IAAI,CAAA,CAAA;AAAA,GAClC;AAAA,EAEA,IAAA,CAAK,SAAiB,IAA6C,EAAA;AACjE,IAAK,IAAA,CAAA,IAAA,CAAK,MAAQ,EAAA,OAAA,EAAS,IAAI,CAAA,CAAA;AAAA,GACjC;AAAA,EAEA,IAAA,CAAK,SAAiB,IAA6C,EAAA;AACjE,IAAK,IAAA,CAAA,IAAA,CAAK,MAAQ,EAAA,OAAA,EAAS,IAAI,CAAA,CAAA;AAAA,GACjC;AAAA,EAEA,KAAA,CAAM,SAAiB,IAA6C,EAAA;AAClE,IAAK,IAAA,CAAA,IAAA,CAAK,OAAS,EAAA,OAAA,EAAS,IAAI,CAAA,CAAA;AAAA,GAClC;AAAA,EAEA,MAAM,IAAiC,EAAA;AACrC,IAAO,OAAA,IAAI,qBAAsB,CAAA,IAAA,CAAK,MAAQ,EAAA,EAAE,GAAG,IAAK,CAAA,KAAA,EAAO,GAAG,IAAA,EAAM,CAAA,CAAA;AAAA,GAC1E;AAAA,EAEQ,WAAA,CAAY,OAAe,IAAkB,EAAA;AACnD,IAAA,IAAA,CAAK,MAAS,GAAA,KAAA,CAAA;AACd,IAAA,IAAA,CAAK,KAAQ,GAAA,IAAA,CAAA;AAAA,GACf;AAAA,EAEA,IAAA,CACE,KACA,EAAA,OAAA,EACA,IACA,EAAA;AACA,IAAM,MAAA,UAAA,GAAa,MAAO,CAAA,KAAK,CAAK,IAAA,CAAA,CAAA;AACpC,IAAI,IAAA,UAAA,IAAc,KAAK,MAAQ,EAAA;AAC7B,MAAA,MAAM,SAAS,MAAO,CAAA,OAAA,CAAQ,KAAK,KAAK,CAAA,CACrC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAA,KAAM,GAAG,GAAG,CAAA,CAAA,EAAI,KAAK,CAAE,CAAA,CAAA,CACvC,KAAK,GAAG,CAAA,CAAA;AACX,MAAA,OAAA,CAAQ,KAAK,CAAE,CAAA,CAAA,EAAG,MAAM,CAAI,CAAA,EAAA,OAAO,IAAI,IAAI,CAAA,CAAA;AAAA,KAC7C;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@backstage/errors');
4
+
5
+ class MockUserInfoService {
6
+ customInfo;
7
+ constructor(customInfo) {
8
+ this.customInfo = customInfo ?? {};
9
+ }
10
+ async getUserInfo(credentials) {
11
+ const principal = credentials.principal;
12
+ if (principal.type !== "user") {
13
+ throw new errors.InputError(
14
+ `User info not available for principal type '${principal.type}'`
15
+ );
16
+ }
17
+ return {
18
+ userEntityRef: principal.userEntityRef,
19
+ ownershipEntityRefs: [principal.userEntityRef],
20
+ ...this.customInfo
21
+ };
22
+ }
23
+ }
24
+
25
+ exports.MockUserInfoService = MockUserInfoService;
26
+ //# sourceMappingURL=MockUserInfoService.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MockUserInfoService.cjs.js","sources":["../../../src/next/services/MockUserInfoService.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 BackstageCredentials,\n BackstageNonePrincipal,\n BackstageServicePrincipal,\n BackstageUserInfo,\n BackstageUserPrincipal,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\n\n/** @internal */\nexport class MockUserInfoService implements UserInfoService {\n private readonly customInfo: Partial<BackstageUserInfo>;\n\n constructor(customInfo?: Partial<BackstageUserInfo>) {\n this.customInfo = customInfo ?? {};\n }\n\n async getUserInfo(\n credentials: BackstageCredentials,\n ): Promise<BackstageUserInfo> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal\n | BackstageNonePrincipal;\n\n if (principal.type !== 'user') {\n throw new InputError(\n `User info not available for principal type '${principal.type}'`,\n );\n }\n\n return {\n userEntityRef: principal.userEntityRef,\n ownershipEntityRefs: [principal.userEntityRef],\n ...this.customInfo,\n };\n }\n}\n"],"names":["InputError"],"mappings":";;;;AA2BO,MAAM,mBAA+C,CAAA;AAAA,EACzC,UAAA,CAAA;AAAA,EAEjB,YAAY,UAAyC,EAAA;AACnD,IAAK,IAAA,CAAA,UAAA,GAAa,cAAc,EAAC,CAAA;AAAA,GACnC;AAAA,EAEA,MAAM,YACJ,WAC4B,EAAA;AAC5B,IAAA,MAAM,YAAY,WAAY,CAAA,SAAA,CAAA;AAK9B,IAAI,IAAA,SAAA,CAAU,SAAS,MAAQ,EAAA;AAC7B,MAAA,MAAM,IAAIA,iBAAA;AAAA,QACR,CAAA,4CAAA,EAA+C,UAAU,IAAI,CAAA,CAAA,CAAA;AAAA,OAC/D,CAAA;AAAA,KACF;AAEA,IAAO,OAAA;AAAA,MACL,eAAe,SAAU,CAAA,aAAA;AAAA,MACzB,mBAAA,EAAqB,CAAC,SAAA,CAAU,aAAa,CAAA;AAAA,MAC7C,GAAG,IAAK,CAAA,UAAA;AAAA,KACV,CAAA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_MOCK_USER_ENTITY_REF = "user:default/mock";
4
+ const DEFAULT_MOCK_SERVICE_SUBJECT = "external:test-service";
5
+ const MOCK_AUTH_COOKIE = "backstage-auth";
6
+ const MOCK_NONE_TOKEN = "mock-none-token";
7
+ const MOCK_USER_TOKEN = "mock-user-token";
8
+ const MOCK_USER_TOKEN_PREFIX = "mock-user-token:";
9
+ const MOCK_INVALID_USER_TOKEN = "mock-invalid-user-token";
10
+ const MOCK_USER_LIMITED_TOKEN_PREFIX = "mock-limited-user-token:";
11
+ const MOCK_INVALID_USER_LIMITED_TOKEN = "mock-invalid-limited-user-token";
12
+ const MOCK_SERVICE_TOKEN = "mock-service-token";
13
+ const MOCK_SERVICE_TOKEN_PREFIX = "mock-service-token:";
14
+ const MOCK_INVALID_SERVICE_TOKEN = "mock-invalid-service-token";
15
+ function validateUserEntityRef(ref) {
16
+ if (!ref.match(/^.+:.+\/.+$/)) {
17
+ throw new TypeError(
18
+ `Invalid user entity reference '${ref}', expected <kind>:<namespace>/<name>`
19
+ );
20
+ }
21
+ }
22
+ exports.mockCredentials = void 0;
23
+ ((mockCredentials2) => {
24
+ function none() {
25
+ return {
26
+ $$type: "@backstage/BackstageCredentials",
27
+ principal: { type: "none" }
28
+ };
29
+ }
30
+ mockCredentials2.none = none;
31
+ ((none2) => {
32
+ function header() {
33
+ return `Bearer ${MOCK_NONE_TOKEN}`;
34
+ }
35
+ none2.header = header;
36
+ })(none = mockCredentials2.none || (mockCredentials2.none = {}));
37
+ function user(userEntityRef = DEFAULT_MOCK_USER_ENTITY_REF) {
38
+ validateUserEntityRef(userEntityRef);
39
+ return {
40
+ $$type: "@backstage/BackstageCredentials",
41
+ principal: { type: "user", userEntityRef }
42
+ };
43
+ }
44
+ mockCredentials2.user = user;
45
+ ((user2) => {
46
+ function token(userEntityRef) {
47
+ if (userEntityRef) {
48
+ validateUserEntityRef(userEntityRef);
49
+ return `${MOCK_USER_TOKEN_PREFIX}${JSON.stringify({
50
+ sub: userEntityRef
51
+ })}`;
52
+ }
53
+ return MOCK_USER_TOKEN;
54
+ }
55
+ user2.token = token;
56
+ function header(userEntityRef) {
57
+ return `Bearer ${token(userEntityRef)}`;
58
+ }
59
+ user2.header = header;
60
+ function invalidToken() {
61
+ return MOCK_INVALID_USER_TOKEN;
62
+ }
63
+ user2.invalidToken = invalidToken;
64
+ function invalidHeader() {
65
+ return `Bearer ${invalidToken()}`;
66
+ }
67
+ user2.invalidHeader = invalidHeader;
68
+ })(user = mockCredentials2.user || (mockCredentials2.user = {}));
69
+ function limitedUser(userEntityRef = DEFAULT_MOCK_USER_ENTITY_REF) {
70
+ return user(userEntityRef);
71
+ }
72
+ mockCredentials2.limitedUser = limitedUser;
73
+ ((limitedUser2) => {
74
+ function token(userEntityRef = DEFAULT_MOCK_USER_ENTITY_REF) {
75
+ validateUserEntityRef(userEntityRef);
76
+ return `${MOCK_USER_LIMITED_TOKEN_PREFIX}${JSON.stringify({
77
+ sub: userEntityRef
78
+ })}`;
79
+ }
80
+ limitedUser2.token = token;
81
+ function cookie(userEntityRef) {
82
+ return `${MOCK_AUTH_COOKIE}=${token(userEntityRef)}`;
83
+ }
84
+ limitedUser2.cookie = cookie;
85
+ function invalidToken() {
86
+ return MOCK_INVALID_USER_LIMITED_TOKEN;
87
+ }
88
+ limitedUser2.invalidToken = invalidToken;
89
+ function invalidCookie() {
90
+ return `${MOCK_AUTH_COOKIE}=${invalidToken()}`;
91
+ }
92
+ limitedUser2.invalidCookie = invalidCookie;
93
+ })(limitedUser = mockCredentials2.limitedUser || (mockCredentials2.limitedUser = {}));
94
+ function service(subject = DEFAULT_MOCK_SERVICE_SUBJECT, accessRestrictions) {
95
+ return {
96
+ $$type: "@backstage/BackstageCredentials",
97
+ principal: {
98
+ type: "service",
99
+ subject,
100
+ ...accessRestrictions ? { accessRestrictions } : {}
101
+ }
102
+ };
103
+ }
104
+ mockCredentials2.service = service;
105
+ ((service2) => {
106
+ function token(options) {
107
+ if (options) {
108
+ const { targetPluginId, onBehalfOf } = options;
109
+ const oboPrincipal = onBehalfOf?.principal;
110
+ const obo = oboPrincipal.type === "user" ? oboPrincipal.userEntityRef : void 0;
111
+ const subject = oboPrincipal.type === "service" ? oboPrincipal.subject : void 0;
112
+ return `${MOCK_SERVICE_TOKEN_PREFIX}${JSON.stringify({
113
+ sub: subject,
114
+ obo,
115
+ target: targetPluginId
116
+ })}`;
117
+ }
118
+ return MOCK_SERVICE_TOKEN;
119
+ }
120
+ service2.token = token;
121
+ function header(options) {
122
+ return `Bearer ${token(options)}`;
123
+ }
124
+ service2.header = header;
125
+ function invalidToken() {
126
+ return MOCK_INVALID_SERVICE_TOKEN;
127
+ }
128
+ service2.invalidToken = invalidToken;
129
+ function invalidHeader() {
130
+ return `Bearer ${invalidToken()}`;
131
+ }
132
+ service2.invalidHeader = invalidHeader;
133
+ })(service = mockCredentials2.service || (mockCredentials2.service = {}));
134
+ })(exports.mockCredentials || (exports.mockCredentials = {}));
135
+
136
+ exports.DEFAULT_MOCK_SERVICE_SUBJECT = DEFAULT_MOCK_SERVICE_SUBJECT;
137
+ exports.DEFAULT_MOCK_USER_ENTITY_REF = DEFAULT_MOCK_USER_ENTITY_REF;
138
+ exports.MOCK_AUTH_COOKIE = MOCK_AUTH_COOKIE;
139
+ exports.MOCK_INVALID_SERVICE_TOKEN = MOCK_INVALID_SERVICE_TOKEN;
140
+ exports.MOCK_INVALID_USER_LIMITED_TOKEN = MOCK_INVALID_USER_LIMITED_TOKEN;
141
+ exports.MOCK_INVALID_USER_TOKEN = MOCK_INVALID_USER_TOKEN;
142
+ exports.MOCK_NONE_TOKEN = MOCK_NONE_TOKEN;
143
+ exports.MOCK_SERVICE_TOKEN = MOCK_SERVICE_TOKEN;
144
+ exports.MOCK_SERVICE_TOKEN_PREFIX = MOCK_SERVICE_TOKEN_PREFIX;
145
+ exports.MOCK_USER_LIMITED_TOKEN_PREFIX = MOCK_USER_LIMITED_TOKEN_PREFIX;
146
+ exports.MOCK_USER_TOKEN = MOCK_USER_TOKEN;
147
+ exports.MOCK_USER_TOKEN_PREFIX = MOCK_USER_TOKEN_PREFIX;
148
+ //# sourceMappingURL=mockCredentials.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockCredentials.cjs.js","sources":["../../../src/next/services/mockCredentials.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 BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalAccessRestrictions,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\n\nexport const DEFAULT_MOCK_USER_ENTITY_REF = 'user:default/mock';\nexport const DEFAULT_MOCK_SERVICE_SUBJECT = 'external:test-service';\n\nexport const MOCK_AUTH_COOKIE = 'backstage-auth';\n\nexport const MOCK_NONE_TOKEN = 'mock-none-token';\n\nexport const MOCK_USER_TOKEN = 'mock-user-token';\nexport const MOCK_USER_TOKEN_PREFIX = 'mock-user-token:';\nexport const MOCK_INVALID_USER_TOKEN = 'mock-invalid-user-token';\n\nexport const MOCK_USER_LIMITED_TOKEN_PREFIX = 'mock-limited-user-token:';\nexport const MOCK_INVALID_USER_LIMITED_TOKEN =\n 'mock-invalid-limited-user-token';\n\nexport const MOCK_SERVICE_TOKEN = 'mock-service-token';\nexport const MOCK_SERVICE_TOKEN_PREFIX = 'mock-service-token:';\nexport const MOCK_INVALID_SERVICE_TOKEN = 'mock-invalid-service-token';\n\nfunction validateUserEntityRef(ref: string) {\n if (!ref.match(/^.+:.+\\/.+$/)) {\n throw new TypeError(\n `Invalid user entity reference '${ref}', expected <kind>:<namespace>/<name>`,\n );\n }\n}\n\n/**\n * The payload that can be encoded into a mock user token.\n * @internal\n */\nexport type UserTokenPayload = {\n sub?: string;\n};\n\n/**\n * The payload that can be encoded into a mock service token.\n * @internal\n */\nexport type ServiceTokenPayload = {\n sub?: string; // service subject\n obo?: string; // user entity reference\n target?: string; // target plugin id\n};\n\n/**\n * @public\n */\nexport namespace mockCredentials {\n /**\n * Creates a mocked credentials object for a unauthenticated principal.\n */\n export function none(): BackstageCredentials<BackstageNonePrincipal> {\n return {\n $$type: '@backstage/BackstageCredentials',\n principal: { type: 'none' },\n };\n }\n\n /**\n * Utilities related to none credentials.\n */\n export namespace none {\n /**\n * Returns an authorization header that translates to unauthenticated\n * credentials.\n *\n * This is useful when one wants to explicitly test unauthenticated requests\n * while still using the default behavior of the mock HttpAuthService where\n * it defaults to user credentials.\n */\n export function header(): string {\n // NOTE: there is no .token() version of this because only the\n // HttpAuthService should know about and consume this token\n return `Bearer ${MOCK_NONE_TOKEN}`;\n }\n }\n\n /**\n * Creates a mocked credentials object for a user principal.\n *\n * The default user entity reference is 'user:default/mock'.\n */\n export function user(\n userEntityRef: string = DEFAULT_MOCK_USER_ENTITY_REF,\n ): BackstageCredentials<BackstageUserPrincipal> {\n validateUserEntityRef(userEntityRef);\n return {\n $$type: '@backstage/BackstageCredentials',\n principal: { type: 'user', userEntityRef },\n };\n }\n\n /**\n * Utilities related to user credentials.\n */\n export namespace user {\n /**\n * Creates a mocked user token. If a payload is provided it will be encoded\n * into the token and forwarded to the credentials object when authenticated\n * by the mock auth service.\n */\n export function token(userEntityRef?: string): string {\n if (userEntityRef) {\n validateUserEntityRef(userEntityRef);\n return `${MOCK_USER_TOKEN_PREFIX}${JSON.stringify({\n sub: userEntityRef,\n } satisfies UserTokenPayload)}`;\n }\n return MOCK_USER_TOKEN;\n }\n\n /**\n * Returns an authorization header with a mocked user token. If a payload is\n * provided it will be encoded into the token and forwarded to the\n * credentials object when authenticated by the mock auth service.\n */\n export function header(userEntityRef?: string): string {\n return `Bearer ${token(userEntityRef)}`;\n }\n\n export function invalidToken(): string {\n return MOCK_INVALID_USER_TOKEN;\n }\n\n export function invalidHeader(): string {\n return `Bearer ${invalidToken()}`;\n }\n }\n\n /**\n * Creates a mocked credentials object for a user principal with limited\n * access.\n *\n * The default user entity reference is 'user:default/mock'.\n */\n export function limitedUser(\n userEntityRef: string = DEFAULT_MOCK_USER_ENTITY_REF,\n ): BackstageCredentials<BackstageUserPrincipal> {\n return user(userEntityRef);\n }\n\n /**\n * Utilities related to limited user credentials.\n */\n export namespace limitedUser {\n /**\n * Creates a mocked limited user token. If a payload is provided it will be\n * encoded into the token and forwarded to the credentials object when\n * authenticated by the mock auth service.\n */\n export function token(\n userEntityRef: string = DEFAULT_MOCK_USER_ENTITY_REF,\n ): string {\n validateUserEntityRef(userEntityRef);\n return `${MOCK_USER_LIMITED_TOKEN_PREFIX}${JSON.stringify({\n sub: userEntityRef,\n } satisfies UserTokenPayload)}`;\n }\n\n /**\n * Returns an authorization header with a mocked limited user token. If a\n * payload is provided it will be encoded into the token and forwarded to\n * the credentials object when authenticated by the mock auth service.\n */\n export function cookie(userEntityRef?: string): string {\n return `${MOCK_AUTH_COOKIE}=${token(userEntityRef)}`;\n }\n\n export function invalidToken(): string {\n return MOCK_INVALID_USER_LIMITED_TOKEN;\n }\n\n export function invalidCookie(): string {\n return `${MOCK_AUTH_COOKIE}=${invalidToken()}`;\n }\n }\n\n /**\n * Creates a mocked credentials object for a service principal.\n *\n * The default subject is 'external:test-service', and no access restrictions.\n */\n export function service(\n subject: string = DEFAULT_MOCK_SERVICE_SUBJECT,\n accessRestrictions?: BackstagePrincipalAccessRestrictions,\n ): BackstageCredentials<BackstageServicePrincipal> {\n return {\n $$type: '@backstage/BackstageCredentials',\n principal: {\n type: 'service',\n subject,\n ...(accessRestrictions ? { accessRestrictions } : {}),\n },\n };\n }\n\n /**\n * Utilities related to service credentials.\n */\n export namespace service {\n /**\n * Options for the creation of mock service tokens.\n */\n export type TokenOptions = {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n };\n\n /**\n * Creates a mocked service token. The provided options will be encoded into\n * the token and forwarded to the credentials object when authenticated by\n * the mock auth service.\n */\n export function token(options?: TokenOptions): string {\n if (options) {\n const { targetPluginId, onBehalfOf } = options; // for fixed ordering\n\n const oboPrincipal = onBehalfOf?.principal as\n | BackstageServicePrincipal\n | BackstageUserPrincipal\n | BackstageNonePrincipal;\n const obo =\n oboPrincipal.type === 'user' ? oboPrincipal.userEntityRef : undefined;\n const subject =\n oboPrincipal.type === 'service' ? oboPrincipal.subject : undefined;\n\n return `${MOCK_SERVICE_TOKEN_PREFIX}${JSON.stringify({\n sub: subject,\n obo,\n target: targetPluginId,\n } satisfies ServiceTokenPayload)}`;\n }\n return MOCK_SERVICE_TOKEN;\n }\n\n /**\n * Returns an authorization header with a mocked service token. The provided\n * options will be encoded into the token and forwarded to the credentials\n * object when authenticated by the mock auth service.\n */\n export function header(options?: TokenOptions): string {\n return `Bearer ${token(options)}`;\n }\n\n export function invalidToken(): string {\n return MOCK_INVALID_SERVICE_TOKEN;\n }\n\n export function invalidHeader(): string {\n return `Bearer ${invalidToken()}`;\n }\n }\n}\n"],"names":["mockCredentials","none","user","limitedUser","service"],"mappings":"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"}