@owox/backend 0.38.0 → 0.39.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/dist/src/common/helpers/read-bounded-body.helper.d.ts +1 -0
- package/dist/src/common/helpers/read-bounded-body.helper.js +28 -0
- package/dist/src/common/scheduler/system-tasks/system-trigger-type.d.ts +3 -1
- package/dist/src/common/scheduler/system-tasks/system-trigger-type.js +2 -0
- package/dist/src/config/data-source-options.config.d.ts +4 -3
- package/dist/src/config/data-source-options.config.js +10 -3
- package/dist/src/config/plugin-collections-body-parser.config.js +3 -0
- package/dist/src/data-marts/credentials/credential.types.d.ts +26 -2
- package/dist/src/data-marts/credentials/credential.types.js +18 -6
- package/dist/src/data-marts/credentials/dto/credential-api.dto.d.ts +5 -1
- package/dist/src/data-marts/credentials/dto/credential-api.dto.js +21 -0
- package/dist/src/data-marts/credentials/dto/credential.dto.d.ts +2 -1
- package/dist/src/data-marts/credentials/entities/credential.entity.d.ts +2 -1
- package/dist/src/data-marts/credentials/entities/credential.entity.js +5 -0
- package/dist/src/data-marts/credentials/facades/credential-consumer-binding.facade.impl.js +6 -1
- package/dist/src/data-marts/credentials/mappers/credential.mapper.js +2 -0
- package/dist/src/data-marts/credentials/services/builtin-credential-definitions.js +8 -0
- package/dist/src/data-marts/credentials/services/credential-external-definition-registry.service.d.ts +4 -0
- package/dist/src/data-marts/credentials/services/credential-external-definition-registry.service.js +36 -17
- package/dist/src/data-marts/credentials/services/credential-fetch.service.d.ts +8 -0
- package/dist/src/data-marts/credentials/services/credential-fetch.service.js +115 -46
- package/dist/src/data-marts/credentials/services/credential-validation-probe.service.d.ts +3 -1
- package/dist/src/data-marts/credentials/services/credential-validation-probe.service.js +10 -2
- package/dist/src/data-marts/credentials/services/credential.service.d.ts +4 -0
- package/dist/src/data-marts/credentials/services/credential.service.js +36 -0
- package/dist/src/data-marts/credentials/services/google-service-account-key.d.ts +6 -0
- package/dist/src/data-marts/credentials/services/google-service-account-key.js +46 -0
- package/dist/src/data-marts/credentials/services/google-service-account-token.service.d.ts +25 -0
- package/dist/src/data-marts/credentials/services/google-service-account-token.service.js +177 -0
- package/dist/src/data-marts/credentials/use-cases/create-credential.service.d.ts +1 -0
- package/dist/src/data-marts/credentials/use-cases/create-credential.service.js +10 -2
- package/dist/src/data-marts/credentials/use-cases/update-credential.service.d.ts +1 -0
- package/dist/src/data-marts/credentials/use-cases/update-credential.service.js +28 -4
- package/dist/src/data-marts/credentials/use-cases/validate-credential.service.js +2 -5
- package/dist/src/data-marts/data-marts.module.js +4 -0
- package/dist/src/data-marts/data-storage-types/snowflake/adapters/snowflake-api.adapter.js +1 -0
- package/dist/src/data-marts/dto/presentation/run-data-mart-request-api.dto.js +4 -2
- package/dist/src/data-marts/services/connector/backfill-segment.d.ts +22 -0
- package/dist/src/data-marts/services/connector/backfill-segment.js +91 -0
- package/dist/src/data-marts/services/connector/connector-execution.service.d.ts +1 -0
- package/dist/src/data-marts/services/connector/connector-execution.service.js +3 -0
- package/dist/src/data-marts/services/connector/connector-executor.service.d.ts +4 -1
- package/dist/src/data-marts/services/connector/connector-executor.service.js +107 -23
- package/dist/src/data-marts/services/connector/connector-run.service.d.ts +3 -0
- package/dist/src/data-marts/services/connector/connector-run.service.js +98 -4
- package/dist/src/data-marts/services/data-mart-run.service.d.ts +1 -0
- package/dist/src/data-marts/services/data-mart-run.service.js +9 -0
- package/dist/src/data-marts/system-triggers/processors/repair-backfill-chains-processor.d.ts +14 -0
- package/dist/src/data-marts/system-triggers/processors/repair-backfill-chains-processor.js +45 -0
- package/dist/src/data-marts/use-cases/cancel-data-mart-run.service.d.ts +4 -0
- package/dist/src/data-marts/use-cases/cancel-data-mart-run.service.js +46 -1
- package/dist/src/data-marts/utils/manual-backfill-range.d.ts +52 -0
- package/dist/src/data-marts/utils/manual-backfill-range.js +110 -8
- package/dist/src/ee/mcp/instructions/mcp-system-instructions.d.ts +1 -1
- package/dist/src/ee/mcp/instructions/mcp-system-instructions.js +1 -0
- package/dist/src/idp/decorators/auth-context.decorator.js +1 -0
- package/dist/src/idp/guards/idp.guard.d.ts +3 -0
- package/dist/src/idp/guards/idp.guard.js +38 -8
- package/dist/src/idp/oauth/controllers/oauth-authorization.controller.js +8 -6
- package/dist/src/idp/oauth/mcp-client-integration.d.ts +1 -0
- package/dist/src/idp/oauth/mcp-client-integration.js +15 -0
- package/dist/src/idp/ports/plugin-runtime-authorizer.port.d.ts +6 -0
- package/dist/src/idp/types/auth.types.d.ts +1 -0
- package/dist/src/migrations/1790900000000-add-plugin-version-backend.d.ts +6 -0
- package/dist/src/migrations/1790900000000-add-plugin-version-backend.js +21 -0
- package/dist/src/migrations/1790900000001-create-plugin-action-execution.d.ts +6 -0
- package/dist/src/migrations/1790900000001-create-plugin-action-execution.js +77 -0
- package/dist/src/migrations/1791000000000-add-credential-public-metadata.d.ts +6 -0
- package/dist/src/migrations/1791000000000-add-credential-public-metadata.js +23 -0
- package/dist/src/plugin-host/collections/dto/domain/plugin-collection.types.d.ts +1 -0
- package/dist/src/plugin-host/collections/errors/plugin-collection.errors.d.ts +6 -0
- package/dist/src/plugin-host/collections/errors/plugin-collection.errors.js +19 -1
- package/dist/src/plugin-host/collections/services/plugin-collection-declaration.service.js +3 -3
- package/dist/src/plugin-host/collections/use-cases/put-plugin-collection-document.service.js +7 -0
- package/dist/src/plugin-host/constants/plugin-action-limits.d.ts +34 -0
- package/dist/src/plugin-host/constants/plugin-action-limits.js +51 -0
- package/dist/src/plugin-host/controllers/plugin-action-runtime.controller.d.ts +20 -0
- package/dist/src/plugin-host/controllers/plugin-action-runtime.controller.js +123 -0
- package/dist/src/plugin-host/controllers/plugin-credential-runtime.controller.js +11 -19
- package/dist/src/plugin-host/controllers/plugin-history.controller.d.ts +10 -0
- package/dist/src/plugin-host/controllers/plugin-history.controller.js +57 -0
- package/dist/src/plugin-host/controllers/plugin-release-check.controller.d.ts +10 -0
- package/dist/src/plugin-host/controllers/plugin-release-check.controller.js +58 -0
- package/dist/src/plugin-host/dto/domain/check-plugin-release.command.d.ts +22 -0
- package/dist/src/plugin-host/dto/domain/check-plugin-release.command.js +17 -0
- package/dist/src/plugin-host/dto/domain/list-plugin-history.command.d.ts +10 -0
- package/dist/src/plugin-host/dto/domain/list-plugin-history.command.js +18 -0
- package/dist/src/plugin-host/dto/domain/plugin-action-execution.command.d.ts +23 -0
- package/dist/src/plugin-host/dto/domain/plugin-action-execution.command.js +44 -0
- package/dist/src/plugin-host/dto/domain/plugin-action-execution.dto.d.ts +36 -0
- package/dist/src/plugin-host/dto/domain/plugin-action-execution.dto.js +3 -0
- package/dist/src/plugin-host/dto/domain/plugin-history.dto.d.ts +29 -0
- package/dist/src/plugin-host/dto/domain/plugin-history.dto.js +3 -0
- package/dist/src/plugin-host/dto/domain/plugin-view.dto.d.ts +1 -0
- package/dist/src/plugin-host/dto/presentation/plugin-action-api.dto.d.ts +44 -0
- package/dist/src/plugin-host/dto/presentation/plugin-action-api.dto.js +208 -0
- package/dist/src/plugin-host/dto/presentation/plugin-gallery-api.dto.d.ts +2 -0
- package/dist/src/plugin-host/dto/presentation/plugin-gallery-api.dto.js +7 -0
- package/dist/src/plugin-host/dto/presentation/plugin-history-api.dto.d.ts +29 -0
- package/dist/src/plugin-host/dto/presentation/plugin-history-api.dto.js +148 -0
- package/dist/src/plugin-host/dto/presentation/publication-api.dto.d.ts +17 -0
- package/dist/src/plugin-host/dto/presentation/publication-api.dto.js +95 -1
- package/dist/src/plugin-host/entities/plugin-action-execution.entity.d.ts +24 -0
- package/dist/src/plugin-host/entities/plugin-action-execution.entity.js +136 -0
- package/dist/src/plugin-host/entities/plugin-version.entity.d.ts +2 -1
- package/dist/src/plugin-host/entities/plugin-version.entity.js +5 -0
- package/dist/src/plugin-host/enums/plugin-action-execution-status.enum.d.ts +9 -0
- package/dist/src/plugin-host/enums/plugin-action-execution-status.enum.js +22 -0
- package/dist/src/plugin-host/enums/release-rejection-code.enum.d.ts +3 -0
- package/dist/src/plugin-host/enums/release-rejection-code.enum.js +6 -0
- package/dist/src/plugin-host/errors/plugin-action.errors.d.ts +45 -0
- package/dist/src/plugin-host/errors/plugin-action.errors.js +70 -0
- package/dist/src/plugin-host/errors/plugin-host.errors.d.ts +6 -0
- package/dist/src/plugin-host/errors/plugin-host.errors.js +10 -1
- package/dist/src/plugin-host/filters/plugin-host-exception.filter.js +2 -2
- package/dist/src/plugin-host/mappers/plugin-action-execution.mapper.d.ts +3 -0
- package/dist/src/plugin-host/mappers/plugin-action-execution.mapper.js +22 -0
- package/dist/src/plugin-host/mappers/plugin-presentation.mapper.d.ts +16 -1
- package/dist/src/plugin-host/mappers/plugin-presentation.mapper.js +80 -1
- package/dist/src/plugin-host/plugin-host.module.js +32 -0
- package/dist/src/plugin-host/services/action-input-validator.service.d.ts +23 -0
- package/dist/src/plugin-host/services/action-input-validator.service.js +277 -0
- package/dist/src/plugin-host/services/external-credential-definition-sync.service.d.ts +3 -0
- package/dist/src/plugin-host/services/external-credential-definition-sync.service.js +47 -15
- package/dist/src/plugin-host/services/plugin-action-dispatcher.service.d.ts +13 -0
- package/dist/src/plugin-host/services/plugin-action-dispatcher.service.js +140 -0
- package/dist/src/plugin-host/services/plugin-action-execution.service.d.ts +69 -0
- package/dist/src/plugin-host/services/plugin-action-execution.service.js +239 -0
- package/dist/src/plugin-host/services/plugin-action-starter.service.d.ts +22 -0
- package/dist/src/plugin-host/services/plugin-action-starter.service.js +81 -0
- package/dist/src/plugin-host/services/plugin-audit.service.d.ts +13 -0
- package/dist/src/plugin-host/services/plugin-audit.service.js +78 -0
- package/dist/src/plugin-host/services/plugin-publication.service.d.ts +2 -0
- package/dist/src/plugin-host/services/plugin-publication.service.js +18 -10
- package/dist/src/plugin-host/services/plugin-runtime-authorizer.service.d.ts +5 -2
- package/dist/src/plugin-host/services/plugin-runtime-authorizer.service.js +31 -3
- package/dist/src/plugin-host/services/plugin-version.service.d.ts +2 -1
- package/dist/src/plugin-host/services/release-candidate-rules.service.d.ts +51 -0
- package/dist/src/plugin-host/services/release-candidate-rules.service.js +198 -0
- package/dist/src/plugin-host/services/remote-url-validator.service.d.ts +7 -0
- package/dist/src/plugin-host/services/remote-url-validator.service.js +4 -0
- package/dist/src/plugin-host/system-triggers/plugin-action-execution-sweep.processor.d.ts +14 -0
- package/dist/src/plugin-host/system-triggers/plugin-action-execution-sweep.processor.js +50 -0
- package/dist/src/plugin-host/use-cases/cancel-plugin-action-execution.service.d.ts +8 -0
- package/dist/src/plugin-host/use-cases/cancel-plugin-action-execution.service.js +39 -0
- package/dist/src/plugin-host/use-cases/check-plugin-release.service.d.ts +22 -0
- package/dist/src/plugin-host/use-cases/check-plugin-release.service.js +152 -0
- package/dist/src/plugin-host/use-cases/get-current-plugin-action-execution.service.d.ts +14 -0
- package/dist/src/plugin-host/use-cases/get-current-plugin-action-execution.service.js +85 -0
- package/dist/src/plugin-host/use-cases/get-plugin-action-execution.service.d.ts +9 -0
- package/dist/src/plugin-host/use-cases/get-plugin-action-execution.service.js +67 -0
- package/dist/src/plugin-host/use-cases/get-plugin-installation-entry.service.js +2 -7
- package/dist/src/plugin-host/use-cases/install-plugin.service.js +4 -0
- package/dist/src/plugin-host/use-cases/list-plugin-history.service.d.ts +25 -0
- package/dist/src/plugin-host/use-cases/list-plugin-history.service.js +147 -0
- package/dist/src/plugin-host/use-cases/plugin-publisher-access.d.ts +5 -0
- package/dist/src/plugin-host/use-cases/plugin-publisher-access.js +28 -0
- package/dist/src/plugin-host/use-cases/start-plugin-action-execution.service.d.ts +12 -0
- package/dist/src/plugin-host/use-cases/start-plugin-action-execution.service.js +64 -0
- package/dist/src/plugin-host/use-cases/sync-plugin-releases.service.d.ts +3 -5
- package/dist/src/plugin-host/use-cases/sync-plugin-releases.service.js +15 -59
- package/dist/src/plugin-host/use-cases/uninstall-plugin.service.js +3 -1
- package/dist/src/plugin-host/use-cases/update-plugin.service.d.ts +0 -1
- package/dist/src/plugin-host/use-cases/update-plugin.service.js +3 -25
- package/dist/src/plugin-host/utils/action-input-schema.util.d.ts +14 -0
- package/dist/src/plugin-host/utils/action-input-schema.util.js +133 -0
- package/dist/src/plugin-host/utils/credential-handles.util.d.ts +3 -0
- package/dist/src/plugin-host/utils/credential-handles.util.js +13 -0
- package/dist/src/plugin-host/utils/execution-token.util.d.ts +5 -0
- package/dist/src/plugin-host/utils/execution-token.util.js +13 -0
- package/dist/src/plugin-host/utils/json-shape.util.d.ts +1 -0
- package/dist/src/plugin-host/utils/json-shape.util.js +21 -0
- package/dist/src/plugin-host/utils/plugin-action-outcome.util.d.ts +10 -0
- package/dist/src/plugin-host/utils/plugin-action-outcome.util.js +67 -0
- package/dist/src/plugin-host/utils/plugin-manifest.util.d.ts +121 -0
- package/dist/src/plugin-host/utils/plugin-manifest.util.js +111 -1
- package/dist/src/plugin-host/utils/request-abort-signal.util.d.ts +5 -0
- package/dist/src/plugin-host/utils/request-abort-signal.util.js +19 -0
- package/package.json +10 -8
|
@@ -5,11 +5,18 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var CredentialFetchService_1;
|
|
8
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
13
|
exports.CredentialFetchService = void 0;
|
|
10
14
|
const common_1 = require("@nestjs/common");
|
|
11
15
|
const safe_url_helper_1 = require("../../../common/helpers/safe-url.helper");
|
|
12
16
|
const guarded_dispatcher_1 = require("../../../common/helpers/guarded-dispatcher");
|
|
17
|
+
const read_bounded_body_helper_1 = require("../../../common/helpers/read-bounded-body.helper");
|
|
18
|
+
const credential_service_1 = require("./credential.service");
|
|
19
|
+
const google_service_account_token_service_1 = require("./google-service-account-token.service");
|
|
13
20
|
const MAX_REQUEST_BODY_BYTES = 1024 * 1024;
|
|
14
21
|
const MAX_RESPONSE_BODY_BYTES = 5 * 1024 * 1024;
|
|
15
22
|
const MAX_REDIRECTS = 5;
|
|
@@ -37,35 +44,65 @@ const FORWARDED_RESPONSE_HEADERS = new Set([
|
|
|
37
44
|
'retry-after',
|
|
38
45
|
'x-request-id',
|
|
39
46
|
]);
|
|
40
|
-
let CredentialFetchService = class CredentialFetchService {
|
|
47
|
+
let CredentialFetchService = CredentialFetchService_1 = class CredentialFetchService {
|
|
48
|
+
googleTokens;
|
|
49
|
+
credentials;
|
|
50
|
+
logger = new common_1.Logger(CredentialFetchService_1.name);
|
|
51
|
+
constructor(googleTokens, credentials) {
|
|
52
|
+
this.googleTokens = googleTokens;
|
|
53
|
+
this.credentials = credentials;
|
|
54
|
+
}
|
|
41
55
|
async run(binding, input, limits = {}) {
|
|
42
56
|
const response = await this.runRaw(binding, input, limits);
|
|
57
|
+
const body = await (0, read_bounded_body_helper_1.readBoundedBody)(response, limits.maxResponseBodyBytes ?? MAX_RESPONSE_BODY_BYTES);
|
|
58
|
+
if (body === null) {
|
|
59
|
+
throw new common_1.BadRequestException('Credential response body is too large');
|
|
60
|
+
}
|
|
43
61
|
return {
|
|
44
62
|
status: response.status,
|
|
45
63
|
headers: pickResponseHeaders(response.headers),
|
|
46
|
-
bodyBase64:
|
|
64
|
+
bodyBase64: body.toString('base64'),
|
|
47
65
|
};
|
|
48
66
|
}
|
|
49
67
|
async runRaw(binding, input, limits = {}) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
!HTTP_TOKEN_PATTERN.test(
|
|
55
|
-
FORBIDDEN_FETCH_METHODS.has(
|
|
68
|
+
const url = await this.assertAllowedUrl(input.url, binding.definition);
|
|
69
|
+
const method = input.method.toUpperCase();
|
|
70
|
+
if (method.length === 0 ||
|
|
71
|
+
method.length > 32 ||
|
|
72
|
+
!HTTP_TOKEN_PATTERN.test(method) ||
|
|
73
|
+
FORBIDDEN_FETCH_METHODS.has(method)) {
|
|
56
74
|
throw new common_1.BadRequestException('Credential request method is not Fetch-compatible');
|
|
57
75
|
}
|
|
58
|
-
|
|
76
|
+
const body = decodeBody(input.bodyBase64);
|
|
59
77
|
if (body.byteLength > MAX_REQUEST_BODY_BYTES) {
|
|
60
78
|
throw new common_1.BadRequestException('Credential request body is too large');
|
|
61
79
|
}
|
|
62
|
-
if ((
|
|
63
|
-
throw new common_1.BadRequestException(`${
|
|
80
|
+
if ((method === 'GET' || method === 'HEAD') && body.byteLength > 0) {
|
|
81
|
+
throw new common_1.BadRequestException(`${method} requests cannot carry a body`);
|
|
64
82
|
}
|
|
65
83
|
const timeoutSignal = AbortSignal.timeout(limits.timeoutMs ?? TIMEOUT_MS);
|
|
66
84
|
const signal = limits.signal ? AbortSignal.any([timeoutSignal, limits.signal]) : timeoutSignal;
|
|
85
|
+
let googleToken;
|
|
86
|
+
const send = async () => {
|
|
87
|
+
const { header, token } = await this.authorization(binding, signal);
|
|
88
|
+
googleToken = token;
|
|
89
|
+
return this.send(binding, input.headers, url, method, body, header, signal, limits);
|
|
90
|
+
};
|
|
91
|
+
const response = await send();
|
|
92
|
+
if (binding.definition.auth.type !== 'google-service-account' || response.status !== 401) {
|
|
93
|
+
return response;
|
|
94
|
+
}
|
|
95
|
+
void response.body?.cancel().catch(() => undefined);
|
|
96
|
+
if (googleToken)
|
|
97
|
+
this.googleTokens.drop(googleTokenRequest(binding), googleToken);
|
|
98
|
+
return send();
|
|
99
|
+
}
|
|
100
|
+
async send(binding, supplied, startUrl, startMethod, startBody, authorization, signal, limits) {
|
|
101
|
+
let currentUrl = startUrl;
|
|
102
|
+
let currentMethod = startMethod;
|
|
103
|
+
let body = startBody;
|
|
67
104
|
for (let hop = 0; hop <= MAX_REDIRECTS; hop += 1) {
|
|
68
|
-
const headers = this.buildRequestHeaders(
|
|
105
|
+
const headers = this.buildRequestHeaders(supplied, authorization);
|
|
69
106
|
let response;
|
|
70
107
|
try {
|
|
71
108
|
response = await fetch(currentUrl, (0, guarded_dispatcher_1.withGuardedDispatcher)({
|
|
@@ -88,7 +125,7 @@ let CredentialFetchService = class CredentialFetchService {
|
|
|
88
125
|
if (hop === MAX_REDIRECTS) {
|
|
89
126
|
throw new common_1.ForbiddenException('Credential request exceeded the redirect limit');
|
|
90
127
|
}
|
|
91
|
-
currentUrl = await this.assertAllowedUrl(new URL(location, currentUrl).toString(), binding.definition
|
|
128
|
+
currentUrl = await this.assertAllowedUrl(new URL(location, currentUrl).toString(), binding.definition);
|
|
92
129
|
if (response.status === 303 ||
|
|
93
130
|
((response.status === 301 || response.status === 302) && currentMethod === 'POST')) {
|
|
94
131
|
currentMethod = currentMethod === 'HEAD' ? 'HEAD' : 'GET';
|
|
@@ -100,7 +137,46 @@ let CredentialFetchService = class CredentialFetchService {
|
|
|
100
137
|
}
|
|
101
138
|
throw new common_1.ForbiddenException('Credential request exceeded the redirect limit');
|
|
102
139
|
}
|
|
103
|
-
async
|
|
140
|
+
async authorization(binding, signal) {
|
|
141
|
+
const { auth } = binding.definition;
|
|
142
|
+
if (auth.type === 'header') {
|
|
143
|
+
return { header: [auth.headerName, `${auth.prefix ?? ''}${binding.secret.value}`] };
|
|
144
|
+
}
|
|
145
|
+
if (binding.requirement.scopes.length === 0) {
|
|
146
|
+
throw new common_1.BadRequestException({
|
|
147
|
+
code: 'CREDENTIAL_SCOPE_INVALID',
|
|
148
|
+
message: 'Google issued no access token for the scopes this plugin declares',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const token = await this.googleTokens.getAccessToken({
|
|
153
|
+
...googleTokenRequest(binding),
|
|
154
|
+
signal,
|
|
155
|
+
});
|
|
156
|
+
return { header: ['authorization', `Bearer ${token}`], token };
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (!(error instanceof google_service_account_token_service_1.GoogleTokenError))
|
|
160
|
+
throw error;
|
|
161
|
+
if (error.failure === 'rejected') {
|
|
162
|
+
await this.credentials
|
|
163
|
+
.markRejected(binding.credentialId, binding.secret, error.message)
|
|
164
|
+
.catch(() => this.logger.warn(`Could not record the rejected key of Credential ${binding.credentialId}`));
|
|
165
|
+
throw new common_1.ForbiddenException({
|
|
166
|
+
code: 'CREDENTIAL_REJECTED',
|
|
167
|
+
message: `Google rejected the service account key: ${error.message}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (error.failure === 'scope') {
|
|
171
|
+
throw new common_1.BadRequestException({
|
|
172
|
+
code: 'CREDENTIAL_SCOPE_INVALID',
|
|
173
|
+
message: 'Google issued no access token for the scopes this plugin declares',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
throw new common_1.BadGatewayException('Credential request could not be completed');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async assertAllowedUrl(rawUrl, definition) {
|
|
104
180
|
let url;
|
|
105
181
|
try {
|
|
106
182
|
url = await (0, safe_url_helper_1.assertPublicHttpUrl)(rawUrl, { allowedProtocols: ['https:'] });
|
|
@@ -108,15 +184,14 @@ let CredentialFetchService = class CredentialFetchService {
|
|
|
108
184
|
catch {
|
|
109
185
|
throw new common_1.ForbiddenException('Credential request URL is not allowed');
|
|
110
186
|
}
|
|
111
|
-
|
|
112
|
-
if (!allowedOrigins.has(url.origin)) {
|
|
187
|
+
if (!definition.origins.some(origin => originAllows(origin, url, definition.auth.type))) {
|
|
113
188
|
throw new common_1.ForbiddenException('Credential request origin is not declared by the definition');
|
|
114
189
|
}
|
|
115
190
|
return url.toString();
|
|
116
191
|
}
|
|
117
|
-
buildRequestHeaders(
|
|
192
|
+
buildRequestHeaders(supplied, [authName, authValue]) {
|
|
118
193
|
const headers = new Headers();
|
|
119
|
-
const secretHeader =
|
|
194
|
+
const secretHeader = authName.toLowerCase();
|
|
120
195
|
const entries = Object.entries(supplied ?? {});
|
|
121
196
|
if (entries.length > 100) {
|
|
122
197
|
throw new common_1.BadRequestException('Credential request contains too many headers');
|
|
@@ -135,18 +210,36 @@ let CredentialFetchService = class CredentialFetchService {
|
|
|
135
210
|
}
|
|
136
211
|
headers.set(name, value);
|
|
137
212
|
}
|
|
138
|
-
headers.set(
|
|
213
|
+
headers.set(authName, authValue);
|
|
139
214
|
return headers;
|
|
140
215
|
}
|
|
141
216
|
};
|
|
142
217
|
exports.CredentialFetchService = CredentialFetchService;
|
|
143
|
-
exports.CredentialFetchService = CredentialFetchService = __decorate([
|
|
144
|
-
(0, common_1.Injectable)()
|
|
218
|
+
exports.CredentialFetchService = CredentialFetchService = CredentialFetchService_1 = __decorate([
|
|
219
|
+
(0, common_1.Injectable)(),
|
|
220
|
+
__metadata("design:paramtypes", [google_service_account_token_service_1.GoogleServiceAccountTokenService,
|
|
221
|
+
credential_service_1.CredentialService])
|
|
145
222
|
], CredentialFetchService);
|
|
223
|
+
function googleTokenRequest(binding) {
|
|
224
|
+
return {
|
|
225
|
+
credentialId: binding.credentialId,
|
|
226
|
+
secret: binding.secret,
|
|
227
|
+
scopes: binding.requirement.scopes,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function originAllows(origin, url, authType) {
|
|
231
|
+
const declared = new URL(origin);
|
|
232
|
+
if (authType === 'google-service-account' && declared.hostname.startsWith('*.')) {
|
|
233
|
+
return (url.protocol === 'https:' &&
|
|
234
|
+
url.port === '' &&
|
|
235
|
+
url.hostname.endsWith(declared.hostname.slice(1)));
|
|
236
|
+
}
|
|
237
|
+
return declared.origin === url.origin;
|
|
238
|
+
}
|
|
146
239
|
function boundResponseBody(response, maxBytes) {
|
|
147
240
|
const declaredLength = Number(response.headers.get('content-length'));
|
|
148
241
|
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
149
|
-
void response.body?.cancel();
|
|
242
|
+
void response.body?.cancel().catch(() => undefined);
|
|
150
243
|
throw new common_1.BadRequestException('Credential response body is too large');
|
|
151
244
|
}
|
|
152
245
|
if (!response.body)
|
|
@@ -192,30 +285,6 @@ function decodeBody(encoded) {
|
|
|
192
285
|
}
|
|
193
286
|
return body;
|
|
194
287
|
}
|
|
195
|
-
async function readBoundedBody(response, maxBytes) {
|
|
196
|
-
const declaredLength = Number(response.headers.get('content-length'));
|
|
197
|
-
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
198
|
-
await response.body?.cancel();
|
|
199
|
-
throw new common_1.BadRequestException('Credential response body is too large');
|
|
200
|
-
}
|
|
201
|
-
if (!response.body)
|
|
202
|
-
return Buffer.alloc(0);
|
|
203
|
-
const reader = response.body.getReader();
|
|
204
|
-
const chunks = [];
|
|
205
|
-
let total = 0;
|
|
206
|
-
while (true) {
|
|
207
|
-
const { done, value } = await reader.read();
|
|
208
|
-
if (done)
|
|
209
|
-
break;
|
|
210
|
-
total += value.byteLength;
|
|
211
|
-
if (total > maxBytes) {
|
|
212
|
-
await reader.cancel();
|
|
213
|
-
throw new common_1.BadRequestException('Credential response body is too large');
|
|
214
|
-
}
|
|
215
|
-
chunks.push(value);
|
|
216
|
-
}
|
|
217
|
-
return Buffer.concat(chunks.map(chunk => Buffer.from(chunk)), total);
|
|
218
|
-
}
|
|
219
288
|
function pickResponseHeaders(headers) {
|
|
220
289
|
const result = {};
|
|
221
290
|
headers.forEach((value, name) => {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { CredentialSecret, CredentialValidationResult } from '../credential.types';
|
|
2
2
|
import type { ResolvedCredentialDefinition } from '../dto/credential-api.dto';
|
|
3
3
|
import { CredentialFetchService } from './credential-fetch.service';
|
|
4
|
+
import { GoogleServiceAccountTokenService } from './google-service-account-token.service';
|
|
4
5
|
export declare class CredentialValidationProbeService {
|
|
5
6
|
private readonly credentialFetch;
|
|
6
|
-
|
|
7
|
+
private readonly googleTokens;
|
|
8
|
+
constructor(credentialFetch: CredentialFetchService, googleTokens: GoogleServiceAccountTokenService);
|
|
7
9
|
run(definition: ResolvedCredentialDefinition, secret: CredentialSecret): Promise<CredentialValidationResult>;
|
|
8
10
|
}
|
|
@@ -12,15 +12,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.CredentialValidationProbeService = void 0;
|
|
13
13
|
const common_1 = require("@nestjs/common");
|
|
14
14
|
const credential_fetch_service_1 = require("./credential-fetch.service");
|
|
15
|
+
const google_service_account_token_service_1 = require("./google-service-account-token.service");
|
|
15
16
|
const PROBE_TIMEOUT_MS = 10_000;
|
|
16
17
|
const PROBE_MAX_RESPONSE_BODY_BYTES = 64 * 1024;
|
|
17
18
|
const DEFAULT_REJECTED_STATUSES = new Set([400, 401, 403]);
|
|
18
19
|
let CredentialValidationProbeService = class CredentialValidationProbeService {
|
|
19
20
|
credentialFetch;
|
|
20
|
-
|
|
21
|
+
googleTokens;
|
|
22
|
+
constructor(credentialFetch, googleTokens) {
|
|
21
23
|
this.credentialFetch = credentialFetch;
|
|
24
|
+
this.googleTokens = googleTokens;
|
|
22
25
|
}
|
|
23
26
|
async run(definition, secret) {
|
|
27
|
+
if (definition.contract.auth.type === 'google-service-account') {
|
|
28
|
+
return this.googleTokens.verify(secret);
|
|
29
|
+
}
|
|
24
30
|
const validatedAt = new Date();
|
|
25
31
|
const probe = definition.contract.validation;
|
|
26
32
|
if (!probe) {
|
|
@@ -37,6 +43,7 @@ let CredentialValidationProbeService = class CredentialValidationProbeService {
|
|
|
37
43
|
definitionId: definition.contract.id,
|
|
38
44
|
optional: false,
|
|
39
45
|
models: [],
|
|
46
|
+
scopes: [],
|
|
40
47
|
},
|
|
41
48
|
secret,
|
|
42
49
|
definition: definition.contract,
|
|
@@ -81,7 +88,8 @@ let CredentialValidationProbeService = class CredentialValidationProbeService {
|
|
|
81
88
|
exports.CredentialValidationProbeService = CredentialValidationProbeService;
|
|
82
89
|
exports.CredentialValidationProbeService = CredentialValidationProbeService = __decorate([
|
|
83
90
|
(0, common_1.Injectable)(),
|
|
84
|
-
__metadata("design:paramtypes", [credential_fetch_service_1.CredentialFetchService
|
|
91
|
+
__metadata("design:paramtypes", [credential_fetch_service_1.CredentialFetchService,
|
|
92
|
+
google_service_account_token_service_1.GoogleServiceAccountTokenService])
|
|
85
93
|
], CredentialValidationProbeService);
|
|
86
94
|
function isSuccessful(status) {
|
|
87
95
|
return status >= 200 && status < 300;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Repository } from 'typeorm';
|
|
2
2
|
import { CredentialConsumerBinding } from '../entities/credential-consumer-binding.entity';
|
|
3
|
+
import type { CredentialSecret, CredentialValidationResult } from '../credential.types';
|
|
3
4
|
import { Credential } from '../entities/credential.entity';
|
|
4
5
|
export declare class CredentialService {
|
|
5
6
|
private readonly credentials;
|
|
@@ -14,6 +15,9 @@ export declare class CredentialService {
|
|
|
14
15
|
getLastUsedAt(credentialId: string): Promise<Date | null>;
|
|
15
16
|
countActiveBindings(credentialId: string): Promise<number>;
|
|
16
17
|
softDelete(id: string, projectId: string): Promise<void>;
|
|
18
|
+
writeValidation(id: string, checkedSecret: CredentialSecret, result: CredentialValidationResult): Promise<void>;
|
|
19
|
+
markRejected(id: string, rejectedSecret: CredentialSecret, message: string): Promise<void>;
|
|
20
|
+
private updateValidationWhileSecret;
|
|
17
21
|
markLastUsed(input: {
|
|
18
22
|
readonly credentialId: string;
|
|
19
23
|
readonly consumerType: 'plugin-installation';
|
|
@@ -81,6 +81,42 @@ let CredentialService = class CredentialService {
|
|
|
81
81
|
async softDelete(id, projectId) {
|
|
82
82
|
await this.credentials.softDelete({ id, projectId });
|
|
83
83
|
}
|
|
84
|
+
async writeValidation(id, checkedSecret, result) {
|
|
85
|
+
await this.updateValidationWhileSecret(id, checkedSecret, {
|
|
86
|
+
validationState: result.state,
|
|
87
|
+
validationMessage: result.message.slice(0, 2000),
|
|
88
|
+
validatedAt: result.validatedAt,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async markRejected(id, rejectedSecret, message) {
|
|
92
|
+
const cut = message.slice(0, 2000);
|
|
93
|
+
const stored = await this.credentials.findOne({
|
|
94
|
+
where: { id },
|
|
95
|
+
select: { id: true, validationState: true, validationMessage: true },
|
|
96
|
+
});
|
|
97
|
+
if (!stored || (stored.validationState === 'rejected' && stored.validationMessage === cut)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
await this.updateValidationWhileSecret(id, rejectedSecret, {
|
|
101
|
+
validationState: 'rejected',
|
|
102
|
+
validationMessage: cut,
|
|
103
|
+
validatedAt: new Date(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async updateValidationWhileSecret(id, secret, values) {
|
|
107
|
+
const type = this.credentials.manager.connection.options.type;
|
|
108
|
+
const storedValue = type === 'mysql' || type === 'mariadb'
|
|
109
|
+
? "JSON_UNQUOTE(JSON_EXTRACT(secret, '$.value'))"
|
|
110
|
+
: "JSON_EXTRACT(secret, '$.value')";
|
|
111
|
+
await this.credentials
|
|
112
|
+
.createQueryBuilder()
|
|
113
|
+
.update(credential_entity_1.Credential)
|
|
114
|
+
.set(values)
|
|
115
|
+
.where('id = :id', { id })
|
|
116
|
+
.andWhere('deletedAt IS NULL')
|
|
117
|
+
.andWhere(`${storedValue} = :secretValue`, { secretValue: secret.value })
|
|
118
|
+
.execute();
|
|
119
|
+
}
|
|
84
120
|
async markLastUsed(input, now = new Date()) {
|
|
85
121
|
const threshold = new Date(now.getTime() - 5 * 60 * 1000);
|
|
86
122
|
try {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type GoogleServiceAccountKey } from '../../../common/schemas/google-service-account-key.schema';
|
|
2
|
+
import type { CredentialPublicMetadata } from '../credential.types';
|
|
3
|
+
export declare function parseGoogleServiceAccountKey(value: string): {
|
|
4
|
+
readonly key: GoogleServiceAccountKey;
|
|
5
|
+
readonly metadata: CredentialPublicMetadata;
|
|
6
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseGoogleServiceAccountKey = parseGoogleServiceAccountKey;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const google_service_account_key_schema_1 = require("../../../common/schemas/google-service-account-key.schema");
|
|
7
|
+
function parseGoogleServiceAccountKey(value) {
|
|
8
|
+
let raw;
|
|
9
|
+
try {
|
|
10
|
+
raw = JSON.parse(value);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new common_1.BadRequestException('The key is not valid JSON');
|
|
14
|
+
}
|
|
15
|
+
const parsed = google_service_account_key_schema_1.GoogleServiceAccountKeySchema.safeParse(raw);
|
|
16
|
+
if (!parsed.success) {
|
|
17
|
+
const [issue] = parsed.error.issues;
|
|
18
|
+
const path = issue.path.join('.');
|
|
19
|
+
throw new common_1.BadRequestException(path ? `${path}: ${issue.message}` : issue.message);
|
|
20
|
+
}
|
|
21
|
+
const key = parsed.data;
|
|
22
|
+
if (!key.client_email.toLowerCase().endsWith('.gserviceaccount.com')) {
|
|
23
|
+
throw new common_1.BadRequestException('client_email must be a service account address');
|
|
24
|
+
}
|
|
25
|
+
let rsa = false;
|
|
26
|
+
try {
|
|
27
|
+
rsa = (0, node_crypto_1.createPrivateKey)(key.private_key).asymmetricKeyType === 'rsa';
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
}
|
|
31
|
+
if (!rsa) {
|
|
32
|
+
throw new common_1.BadRequestException('private_key must be an RSA key');
|
|
33
|
+
}
|
|
34
|
+
if (key.universe_domain !== 'googleapis.com') {
|
|
35
|
+
throw new common_1.BadRequestException('Only the googleapis.com universe is supported');
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
key,
|
|
39
|
+
metadata: {
|
|
40
|
+
clientEmail: key.client_email,
|
|
41
|
+
projectId: key.project_id,
|
|
42
|
+
privateKeyId: key.private_key_id,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=google-service-account-key.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type JWTOptions } from 'google-auth-library';
|
|
2
|
+
import type { CredentialSecret, CredentialValidationResult } from '../credential.types';
|
|
3
|
+
export declare const GOOGLE_TOKEN_TRANSPORTER: unique symbol;
|
|
4
|
+
export type GoogleTokenTransporter = NonNullable<JWTOptions['transporter']>;
|
|
5
|
+
export type GoogleTokenFailure = 'rejected' | 'scope' | 'unavailable';
|
|
6
|
+
export declare class GoogleTokenError extends Error {
|
|
7
|
+
readonly failure: GoogleTokenFailure;
|
|
8
|
+
constructor(failure: GoogleTokenFailure, message: string);
|
|
9
|
+
}
|
|
10
|
+
export interface GoogleTokenRequest {
|
|
11
|
+
readonly credentialId: string;
|
|
12
|
+
readonly secret: CredentialSecret;
|
|
13
|
+
readonly scopes: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export declare class GoogleServiceAccountTokenService {
|
|
16
|
+
private readonly transporter?;
|
|
17
|
+
private readonly clients;
|
|
18
|
+
constructor(transporter?: GoogleTokenTransporter | undefined);
|
|
19
|
+
verify(secret: CredentialSecret): Promise<CredentialValidationResult>;
|
|
20
|
+
getAccessToken(input: GoogleTokenRequest & {
|
|
21
|
+
readonly signal: AbortSignal;
|
|
22
|
+
}): Promise<string>;
|
|
23
|
+
drop(input: GoogleTokenRequest, staleToken: string): void;
|
|
24
|
+
private createClient;
|
|
25
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.GoogleServiceAccountTokenService = exports.GoogleTokenError = exports.GOOGLE_TOKEN_TRANSPORTER = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const google_auth_library_1 = require("google-auth-library");
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const google_service_account_key_1 = require("./google-service-account-key");
|
|
20
|
+
exports.GOOGLE_TOKEN_TRANSPORTER = Symbol('GOOGLE_TOKEN_TRANSPORTER');
|
|
21
|
+
class GoogleTokenError extends Error {
|
|
22
|
+
failure;
|
|
23
|
+
constructor(failure, message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.failure = failure;
|
|
26
|
+
this.name = 'GoogleTokenError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.GoogleTokenError = GoogleTokenError;
|
|
30
|
+
const IDLE_MS = 60 * 60 * 1000;
|
|
31
|
+
const VERIFY_TIMEOUT_MS = 10_000;
|
|
32
|
+
const TOKEN_REQUEST_TIMEOUT_MS = 10_000;
|
|
33
|
+
const NEW_KEY_HINT = ' A key created in the last minute may not work yet.';
|
|
34
|
+
const VERIFY_SCOPES = ['https://www.googleapis.com/auth/userinfo.email'];
|
|
35
|
+
const NO_ACCESS_TOKEN_MESSAGE = 'Could not refresh access token.';
|
|
36
|
+
let GoogleServiceAccountTokenService = class GoogleServiceAccountTokenService {
|
|
37
|
+
transporter;
|
|
38
|
+
clients = new Map();
|
|
39
|
+
constructor(transporter) {
|
|
40
|
+
this.transporter = transporter;
|
|
41
|
+
}
|
|
42
|
+
async verify(secret) {
|
|
43
|
+
const validatedAt = new Date();
|
|
44
|
+
let client;
|
|
45
|
+
try {
|
|
46
|
+
client = this.createClient(secret, VERIFY_SCOPES);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
state: 'rejected',
|
|
51
|
+
message: error instanceof Error ? error.message : 'The key is not valid',
|
|
52
|
+
validatedAt,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await mint(client, AbortSignal.timeout(VERIFY_TIMEOUT_MS));
|
|
57
|
+
return { state: 'verified', message: 'Google issued a token for this key', validatedAt };
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof GoogleTokenError && error.failure === 'rejected') {
|
|
61
|
+
return { state: 'rejected', message: `${error.message}${NEW_KEY_HINT}`, validatedAt };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
state: 'unknown',
|
|
65
|
+
message: 'Google could not be reached to check the key',
|
|
66
|
+
validatedAt,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async getAccessToken(input) {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
for (const [key, entry] of this.clients) {
|
|
73
|
+
if (now - entry.lastUsedAt > IDLE_MS)
|
|
74
|
+
this.clients.delete(key);
|
|
75
|
+
}
|
|
76
|
+
const key = cacheKey(input);
|
|
77
|
+
let entry = this.clients.get(key);
|
|
78
|
+
if (!entry) {
|
|
79
|
+
entry = { client: this.createClient(input.secret, input.scopes), lastUsedAt: now };
|
|
80
|
+
this.clients.set(key, entry);
|
|
81
|
+
}
|
|
82
|
+
entry.lastUsedAt = now;
|
|
83
|
+
try {
|
|
84
|
+
return await mint(entry.client, input.signal);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (!input.signal.aborted && this.clients.get(key) === entry)
|
|
88
|
+
this.clients.delete(key);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
drop(input, staleToken) {
|
|
93
|
+
const key = cacheKey(input);
|
|
94
|
+
if (this.clients.get(key)?.client.credentials.access_token === staleToken) {
|
|
95
|
+
this.clients.delete(key);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
createClient(secret, scopes) {
|
|
99
|
+
let key;
|
|
100
|
+
try {
|
|
101
|
+
({ key } = (0, google_service_account_key_1.parseGoogleServiceAccountKey)(secret.value));
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
throw new GoogleTokenError('rejected', error instanceof Error ? error.message : 'The key is not valid');
|
|
105
|
+
}
|
|
106
|
+
return new google_auth_library_1.JWT({
|
|
107
|
+
email: key.client_email,
|
|
108
|
+
key: key.private_key,
|
|
109
|
+
scopes: [...scopes],
|
|
110
|
+
transporter: this.transporter,
|
|
111
|
+
useAuthRequestParameters: false,
|
|
112
|
+
transporterOptions: { timeout: TOKEN_REQUEST_TIMEOUT_MS },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
exports.GoogleServiceAccountTokenService = GoogleServiceAccountTokenService;
|
|
117
|
+
exports.GoogleServiceAccountTokenService = GoogleServiceAccountTokenService = __decorate([
|
|
118
|
+
(0, common_1.Injectable)(),
|
|
119
|
+
__param(0, (0, common_1.Optional)()),
|
|
120
|
+
__param(0, (0, common_1.Inject)(exports.GOOGLE_TOKEN_TRANSPORTER)),
|
|
121
|
+
__metadata("design:paramtypes", [Object])
|
|
122
|
+
], GoogleServiceAccountTokenService);
|
|
123
|
+
function cacheKey(input) {
|
|
124
|
+
const secretHash = (0, node_crypto_1.createHash)('sha256').update(input.secret.value).digest('hex');
|
|
125
|
+
return `${input.credentialId}:${secretHash}:${input.scopes.join(' ')}`;
|
|
126
|
+
}
|
|
127
|
+
async function mint(client, signal) {
|
|
128
|
+
let token;
|
|
129
|
+
try {
|
|
130
|
+
({ token } = await raceSignal(client.getAccessToken(), signal));
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw classify(error);
|
|
134
|
+
}
|
|
135
|
+
if (!token)
|
|
136
|
+
throw new GoogleTokenError('scope', 'Google issued no access token');
|
|
137
|
+
return token;
|
|
138
|
+
}
|
|
139
|
+
function raceSignal(promise, signal) {
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
const onAbort = () => reject(signal.reason ?? new Error('aborted'));
|
|
142
|
+
if (signal.aborted) {
|
|
143
|
+
onAbort();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
147
|
+
}
|
|
148
|
+
promise.then(value => {
|
|
149
|
+
signal.removeEventListener('abort', onAbort);
|
|
150
|
+
resolve(value);
|
|
151
|
+
}, (error) => {
|
|
152
|
+
signal.removeEventListener('abort', onAbort);
|
|
153
|
+
reject(error);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function classify(error) {
|
|
158
|
+
if (error instanceof GoogleTokenError)
|
|
159
|
+
return error;
|
|
160
|
+
const response = error?.response;
|
|
161
|
+
if (!response) {
|
|
162
|
+
return error instanceof Error && error.message === NO_ACCESS_TOKEN_MESSAGE
|
|
163
|
+
? new GoogleTokenError('scope', 'Google issued no access token')
|
|
164
|
+
: new GoogleTokenError('unavailable', 'Google could not be reached');
|
|
165
|
+
}
|
|
166
|
+
const status = response.status ?? 0;
|
|
167
|
+
const code = response.data?.error;
|
|
168
|
+
if (status >= 400 && status < 500 && status !== 429 && typeof code === 'string') {
|
|
169
|
+
if (code === 'invalid_scope') {
|
|
170
|
+
return new GoogleTokenError('scope', 'Google issued no access token');
|
|
171
|
+
}
|
|
172
|
+
const description = response.data?.error_description;
|
|
173
|
+
return new GoogleTokenError('rejected', typeof description === 'string' && description ? `${code}: ${description}` : code);
|
|
174
|
+
}
|
|
175
|
+
return new GoogleTokenError('unavailable', 'Google could not be reached');
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=google-service-account-token.service.js.map
|
|
@@ -22,6 +22,7 @@ export declare class CreateCredentialService {
|
|
|
22
22
|
private readonly credentialContexts;
|
|
23
23
|
constructor(credentials: CredentialService, definitions: CredentialDefinitionService, view: CredentialViewService, validationProbe: CredentialValidationProbeService, contexts: ContextService, idpProjections: IdpProjectionsFacade, owners: Repository<CredentialOwner>, credentialContexts: Repository<CredentialContext>);
|
|
24
24
|
run(context: AuthorizationContext, input: CreateCredentialApiDto): Promise<CredentialDto>;
|
|
25
|
+
private persist;
|
|
25
26
|
}
|
|
26
27
|
export declare function validateAiMappings(mappings: CredentialAiModelMappings | null | undefined, ai: CredentialDefinitionContract['ai'] | undefined): void;
|
|
27
28
|
export declare function normalizeAiConfiguration(mappings: CredentialAiModelMappings | null | undefined, modes: CredentialAiModelMappingModes | null | undefined, ai: CredentialDefinitionContract['ai'] | undefined, current?: {
|