@smythos/sre 1.5.53 → 1.5.54
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 +98 -98
- package/LICENSE +18 -18
- package/README.md +135 -135
- package/dist/bundle-analysis-lazy.html +4949 -0
- package/dist/bundle-analysis.html +4949 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +1 -6
- package/dist/types/utils/package-manager.utils.d.ts +26 -0
- package/package.json +1 -1
- package/src/Components/APICall/APICall.class.ts +157 -157
- package/src/Components/APICall/AccessTokenManager.ts +166 -166
- package/src/Components/APICall/ArrayBufferResponse.helper.ts +58 -58
- package/src/Components/APICall/OAuth.helper.ts +447 -447
- package/src/Components/APICall/mimeTypeCategories.ts +46 -46
- package/src/Components/APICall/parseData.ts +167 -167
- package/src/Components/APICall/parseHeaders.ts +41 -41
- package/src/Components/APICall/parseProxy.ts +68 -68
- package/src/Components/APICall/parseUrl.ts +91 -91
- package/src/Components/APIEndpoint.class.ts +234 -234
- package/src/Components/APIOutput.class.ts +58 -58
- package/src/Components/AgentPlugin.class.ts +102 -102
- package/src/Components/Async.class.ts +155 -155
- package/src/Components/Await.class.ts +90 -90
- package/src/Components/Classifier.class.ts +158 -158
- package/src/Components/Component.class.ts +132 -132
- package/src/Components/ComponentHost.class.ts +38 -38
- package/src/Components/DataSourceCleaner.class.ts +92 -92
- package/src/Components/DataSourceIndexer.class.ts +181 -181
- package/src/Components/DataSourceLookup.class.ts +161 -161
- package/src/Components/ECMASandbox.class.ts +71 -71
- package/src/Components/FEncDec.class.ts +29 -29
- package/src/Components/FHash.class.ts +33 -33
- package/src/Components/FSign.class.ts +80 -80
- package/src/Components/FSleep.class.ts +25 -25
- package/src/Components/FTimestamp.class.ts +25 -25
- package/src/Components/FileStore.class.ts +78 -78
- package/src/Components/ForEach.class.ts +97 -97
- package/src/Components/GPTPlugin.class.ts +70 -70
- package/src/Components/GenAILLM.class.ts +586 -586
- package/src/Components/HuggingFace.class.ts +314 -314
- package/src/Components/Image/imageSettings.config.ts +70 -70
- package/src/Components/ImageGenerator.class.ts +502 -502
- package/src/Components/JSONFilter.class.ts +54 -54
- package/src/Components/LLMAssistant.class.ts +213 -213
- package/src/Components/LogicAND.class.ts +28 -28
- package/src/Components/LogicAtLeast.class.ts +85 -85
- package/src/Components/LogicAtMost.class.ts +86 -86
- package/src/Components/LogicOR.class.ts +29 -29
- package/src/Components/LogicXOR.class.ts +34 -34
- package/src/Components/MCPClient.class.ts +138 -138
- package/src/Components/MemoryDeleteKeyVal.class.ts +70 -70
- package/src/Components/MemoryReadKeyVal.class.ts +66 -66
- package/src/Components/MemoryWriteKeyVal.class.ts +62 -62
- package/src/Components/MemoryWriteObject.class.ts +97 -97
- package/src/Components/MultimodalLLM.class.ts +128 -128
- package/src/Components/OpenAPI.class.ts +72 -72
- package/src/Components/PromptGenerator.class.ts +122 -122
- package/src/Components/ScrapflyWebScrape.class.ts +159 -159
- package/src/Components/ServerlessCode.class.ts +123 -123
- package/src/Components/TavilyWebSearch.class.ts +98 -98
- package/src/Components/VisionLLM.class.ts +104 -104
- package/src/Components/ZapierAction.class.ts +127 -127
- package/src/Components/index.ts +97 -97
- package/src/Core/AgentProcess.helper.ts +240 -240
- package/src/Core/Connector.class.ts +123 -123
- package/src/Core/ConnectorsService.ts +197 -197
- package/src/Core/DummyConnector.ts +49 -49
- package/src/Core/HookService.ts +105 -105
- package/src/Core/SmythRuntime.class.ts +235 -235
- package/src/Core/SystemEvents.ts +16 -16
- package/src/Core/boot.ts +56 -56
- package/src/config.ts +15 -15
- package/src/constants.ts +126 -126
- package/src/data/hugging-face.params.json +579 -579
- package/src/helpers/AWSLambdaCode.helper.ts +590 -590
- package/src/helpers/BinaryInput.helper.ts +331 -331
- package/src/helpers/Conversation.helper.ts +1119 -1119
- package/src/helpers/ECMASandbox.helper.ts +54 -54
- package/src/helpers/JsonContent.helper.ts +97 -97
- package/src/helpers/LocalCache.helper.ts +97 -97
- package/src/helpers/Log.helper.ts +274 -274
- package/src/helpers/OpenApiParser.helper.ts +150 -150
- package/src/helpers/S3Cache.helper.ts +147 -147
- package/src/helpers/SmythURI.helper.ts +5 -5
- package/src/helpers/Sysconfig.helper.ts +77 -77
- package/src/helpers/TemplateString.helper.ts +243 -243
- package/src/helpers/TypeChecker.helper.ts +329 -329
- package/src/index.ts +3 -3
- package/src/index.ts.bak +3 -3
- package/src/subsystems/AgentManager/Agent.class.ts +1114 -1114
- package/src/subsystems/AgentManager/Agent.helper.ts +3 -3
- package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +230 -230
- package/src/subsystems/AgentManager/AgentData.service/connectors/CLIAgentDataConnector.class.ts +66 -66
- package/src/subsystems/AgentManager/AgentData.service/connectors/LocalAgentDataConnector.class.ts +142 -142
- package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +39 -39
- package/src/subsystems/AgentManager/AgentData.service/index.ts +18 -18
- package/src/subsystems/AgentManager/AgentLogger.class.ts +301 -297
- package/src/subsystems/AgentManager/AgentRequest.class.ts +51 -51
- package/src/subsystems/AgentManager/AgentRuntime.class.ts +559 -559
- package/src/subsystems/AgentManager/AgentSSE.class.ts +101 -101
- package/src/subsystems/AgentManager/AgentSettings.class.ts +52 -52
- package/src/subsystems/AgentManager/Component.service/ComponentConnector.ts +32 -32
- package/src/subsystems/AgentManager/Component.service/connectors/LocalComponentConnector.class.ts +60 -60
- package/src/subsystems/AgentManager/Component.service/index.ts +11 -11
- package/src/subsystems/AgentManager/EmbodimentSettings.class.ts +47 -47
- package/src/subsystems/AgentManager/ForkedAgent.class.ts +154 -154
- package/src/subsystems/AgentManager/OSResourceMonitor.ts +77 -77
- package/src/subsystems/ComputeManager/Code.service/CodeConnector.ts +98 -98
- package/src/subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class.ts +172 -172
- package/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts +131 -131
- package/src/subsystems/ComputeManager/Code.service/index.ts +13 -13
- package/src/subsystems/IO/CLI.service/CLIConnector.ts +47 -47
- package/src/subsystems/IO/CLI.service/index.ts +9 -9
- package/src/subsystems/IO/Log.service/LogConnector.ts +32 -32
- package/src/subsystems/IO/Log.service/connectors/ConsoleLog.class.ts +28 -28
- package/src/subsystems/IO/Log.service/index.ts +13 -13
- package/src/subsystems/IO/NKV.service/NKVConnector.ts +43 -43
- package/src/subsystems/IO/NKV.service/connectors/NKVLocalStorage.class.ts +234 -234
- package/src/subsystems/IO/NKV.service/connectors/NKVRAM.class.ts +204 -204
- package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +182 -182
- package/src/subsystems/IO/NKV.service/index.ts +14 -14
- package/src/subsystems/IO/Router.service/RouterConnector.ts +21 -21
- package/src/subsystems/IO/Router.service/connectors/ExpressRouter.class.ts +48 -48
- package/src/subsystems/IO/Router.service/connectors/NullRouter.class.ts +40 -40
- package/src/subsystems/IO/Router.service/index.ts +11 -11
- package/src/subsystems/IO/Storage.service/SmythFS.class.ts +489 -489
- package/src/subsystems/IO/Storage.service/StorageConnector.ts +66 -66
- package/src/subsystems/IO/Storage.service/connectors/LocalStorage.class.ts +327 -327
- package/src/subsystems/IO/Storage.service/connectors/S3Storage.class.ts +482 -482
- package/src/subsystems/IO/Storage.service/index.ts +13 -13
- package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +108 -108
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +454 -454
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +384 -384
- package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +421 -421
- package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +107 -107
- package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +109 -109
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +21 -21
- package/src/subsystems/IO/VectorDB.service/index.ts +14 -14
- package/src/subsystems/LLMManager/LLM.helper.ts +251 -251
- package/src/subsystems/LLMManager/LLM.inference.ts +339 -339
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +489 -489
- package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +171 -171
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +659 -659
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +400 -400
- package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +77 -77
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +757 -757
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +304 -304
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +250 -250
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +423 -423
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +488 -488
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +524 -524
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +100 -100
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.ts +81 -81
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +1145 -1145
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts +13 -13
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.ts +4 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +11 -11
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts +32 -32
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +471 -471
- package/src/subsystems/LLMManager/LLM.service/index.ts +44 -44
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +300 -300
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +252 -252
- package/src/subsystems/LLMManager/ModelsProvider.service/index.ts +11 -11
- package/src/subsystems/LLMManager/custom-models.ts +854 -854
- package/src/subsystems/LLMManager/models.ts +2540 -2540
- package/src/subsystems/LLMManager/paramMappings.ts +69 -69
- package/src/subsystems/MemoryManager/Cache.service/CacheConnector.ts +86 -86
- package/src/subsystems/MemoryManager/Cache.service/connectors/LocalStorageCache.class.ts +297 -297
- package/src/subsystems/MemoryManager/Cache.service/connectors/RAMCache.class.ts +201 -201
- package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +252 -252
- package/src/subsystems/MemoryManager/Cache.service/connectors/S3Cache.class.ts +373 -373
- package/src/subsystems/MemoryManager/Cache.service/index.ts +15 -15
- package/src/subsystems/MemoryManager/LLMCache.ts +72 -72
- package/src/subsystems/MemoryManager/LLMContext.ts +124 -124
- package/src/subsystems/MemoryManager/LLMMemory.service/LLMMemoryConnector.ts +26 -26
- package/src/subsystems/MemoryManager/RuntimeContext.ts +266 -266
- package/src/subsystems/Security/AccessControl/ACL.class.ts +208 -208
- package/src/subsystems/Security/AccessControl/AccessCandidate.class.ts +82 -82
- package/src/subsystems/Security/AccessControl/AccessRequest.class.ts +52 -52
- package/src/subsystems/Security/Account.service/AccountConnector.ts +44 -44
- package/src/subsystems/Security/Account.service/connectors/AWSAccount.class.ts +76 -76
- package/src/subsystems/Security/Account.service/connectors/DummyAccount.class.ts +130 -130
- package/src/subsystems/Security/Account.service/connectors/JSONFileAccount.class.ts +159 -159
- package/src/subsystems/Security/Account.service/index.ts +14 -14
- package/src/subsystems/Security/Credentials.helper.ts +62 -62
- package/src/subsystems/Security/ManagedVault.service/ManagedVaultConnector.ts +38 -38
- package/src/subsystems/Security/ManagedVault.service/connectors/NullManagedVault.class.ts +53 -53
- package/src/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.ts +154 -154
- package/src/subsystems/Security/ManagedVault.service/index.ts +12 -12
- package/src/subsystems/Security/SecureConnector.class.ts +110 -110
- package/src/subsystems/Security/Vault.service/Vault.helper.ts +30 -30
- package/src/subsystems/Security/Vault.service/VaultConnector.ts +29 -29
- package/src/subsystems/Security/Vault.service/connectors/HashicorpVault.class.ts +46 -46
- package/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +221 -221
- package/src/subsystems/Security/Vault.service/connectors/NullVault.class.ts +54 -54
- package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +140 -140
- package/src/subsystems/Security/Vault.service/index.ts +12 -12
- package/src/types/ACL.types.ts +104 -104
- package/src/types/AWS.types.ts +10 -10
- package/src/types/Agent.types.ts +61 -61
- package/src/types/AgentLogger.types.ts +17 -17
- package/src/types/Cache.types.ts +1 -1
- package/src/types/Common.types.ts +2 -2
- package/src/types/LLM.types.ts +496 -496
- package/src/types/Redis.types.ts +8 -8
- package/src/types/SRE.types.ts +64 -64
- package/src/types/Security.types.ts +14 -14
- package/src/types/Storage.types.ts +5 -5
- package/src/types/VectorDB.types.ts +86 -86
- package/src/utils/base64.utils.ts +275 -275
- package/src/utils/cli.utils.ts +68 -68
- package/src/utils/data.utils.ts +322 -322
- package/src/utils/date-time.utils.ts +22 -22
- package/src/utils/general.utils.ts +238 -238
- package/src/utils/index.ts +12 -12
- package/src/utils/lazy-client.ts +261 -261
- package/src/utils/numbers.utils.ts +13 -13
- package/src/utils/oauth.utils.ts +35 -35
- package/src/utils/string.utils.ts +414 -414
- package/src/utils/url.utils.ts +19 -19
- package/src/utils/validation.utils.ts +74 -74
- package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/SmythModelsProvider.class.d.ts +0 -39
|
@@ -1,447 +1,447 @@
|
|
|
1
|
-
// helper.ts
|
|
2
|
-
import crypto from 'crypto';
|
|
3
|
-
import OAuth from 'oauth-1.0a';
|
|
4
|
-
import AccessTokenManager from './AccessTokenManager';
|
|
5
|
-
import { REQUEST_CONTENT_TYPES } from '@sre/constants';
|
|
6
|
-
import axios, { AxiosRequestConfig } from 'axios';
|
|
7
|
-
import { Logger } from '@sre/helpers/Log.helper';
|
|
8
|
-
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
9
|
-
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
10
|
-
import { TemplateString } from '@sre/helpers/TemplateString.helper';
|
|
11
|
-
import { SystemEvents } from '@sre/Core/SystemEvents';
|
|
12
|
-
|
|
13
|
-
const console = Logger('OAuth.helper');
|
|
14
|
-
let managedVault: any;
|
|
15
|
-
|
|
16
|
-
SystemEvents.on('SRE:Booted', () => {
|
|
17
|
-
try {
|
|
18
|
-
managedVault = ConnectorService.getManagedVaultConnector();
|
|
19
|
-
} catch (error) {
|
|
20
|
-
console.warn('Could not find a compatible ManagedVault connector, OAuth APICalls will not work');
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export function extractAdditionalParamsForOAuth1(reqConfig: AxiosRequestConfig = {}) {
|
|
25
|
-
let additionalParams = {};
|
|
26
|
-
|
|
27
|
-
// Validate URL doesn't contain unresolved template variables
|
|
28
|
-
if (reqConfig.url && (reqConfig.url.includes('{{') || reqConfig.url.includes('${{'))) {
|
|
29
|
-
console.warn('Warning: URL contains unresolved template variables for OAuth1 signature:', reqConfig.url);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Parse URL parameters
|
|
33
|
-
try {
|
|
34
|
-
const url = new URL(reqConfig.url);
|
|
35
|
-
const searchParams = url.searchParams;
|
|
36
|
-
additionalParams = Object.fromEntries(searchParams.entries());
|
|
37
|
-
|
|
38
|
-
// Log if we have query parameters for debugging
|
|
39
|
-
if (searchParams.toString()) {
|
|
40
|
-
console.debug('OAuth1: Found query parameters:', Object.keys(additionalParams));
|
|
41
|
-
}
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.warn('Failed to parse URL for OAuth1 parameters:', error);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Get the content type, handling different header formats
|
|
47
|
-
const headers = reqConfig.headers || {};
|
|
48
|
-
let contentType = '';
|
|
49
|
-
|
|
50
|
-
// Headers might be an object or array of objects
|
|
51
|
-
if (Array.isArray(headers)) {
|
|
52
|
-
const contentTypeHeader = headers.find(h =>
|
|
53
|
-
Object.keys(h).some(k => k.toLowerCase() === 'content-type')
|
|
54
|
-
);
|
|
55
|
-
if (contentTypeHeader) {
|
|
56
|
-
const key = Object.keys(contentTypeHeader).find(k => k.toLowerCase() === 'content-type');
|
|
57
|
-
contentType = contentTypeHeader[key];
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
contentType = headers['Content-Type'] || headers['content-type'] || '';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Extract body parameters based on content type
|
|
64
|
-
const method = (reqConfig.method || 'GET').toUpperCase();
|
|
65
|
-
|
|
66
|
-
if (contentType.includes(REQUEST_CONTENT_TYPES.urlEncodedFormData)) {
|
|
67
|
-
// For form data, include the form parameters in the signature
|
|
68
|
-
if (reqConfig.data) {
|
|
69
|
-
let formParams = {};
|
|
70
|
-
if (typeof reqConfig.data === 'string') {
|
|
71
|
-
// Check for unresolved template variables in form data
|
|
72
|
-
if (reqConfig.data.includes('{{') || reqConfig.data.includes('${{')) {
|
|
73
|
-
console.warn('Warning: Form data contains unresolved template variables for OAuth1 signature');
|
|
74
|
-
}
|
|
75
|
-
const formData = new URLSearchParams(reqConfig.data);
|
|
76
|
-
formParams = Object.fromEntries(formData.entries());
|
|
77
|
-
} else if (reqConfig.data instanceof URLSearchParams) {
|
|
78
|
-
formParams = Object.fromEntries(reqConfig.data.entries());
|
|
79
|
-
} else if (typeof reqConfig.data === 'object') {
|
|
80
|
-
// Handle plain object
|
|
81
|
-
formParams = reqConfig.data;
|
|
82
|
-
}
|
|
83
|
-
console.debug('OAuth1: Including form parameters in signature:', Object.keys(formParams));
|
|
84
|
-
additionalParams = { ...additionalParams, ...formParams };
|
|
85
|
-
}
|
|
86
|
-
} else if (contentType.includes(REQUEST_CONTENT_TYPES.json) ||
|
|
87
|
-
contentType.includes('application/') ||
|
|
88
|
-
contentType.includes('text/')) {
|
|
89
|
-
// For JSON and other non-form data, use oauth_body_hash
|
|
90
|
-
if (reqConfig.data && method !== 'GET' && method !== 'HEAD') {
|
|
91
|
-
let bodyString = '';
|
|
92
|
-
if (typeof reqConfig.data === 'string') {
|
|
93
|
-
bodyString = reqConfig.data;
|
|
94
|
-
} else {
|
|
95
|
-
bodyString = JSON.stringify(reqConfig.data);
|
|
96
|
-
}
|
|
97
|
-
// Check for unresolved template variables
|
|
98
|
-
if (bodyString.includes('{{') || bodyString.includes('${{')) {
|
|
99
|
-
console.warn('Warning: Request body contains unresolved template variables for OAuth1 signature');
|
|
100
|
-
}
|
|
101
|
-
const hash = crypto.createHash('sha1').update(bodyString).digest('base64');
|
|
102
|
-
additionalParams['oauth_body_hash'] = hash;
|
|
103
|
-
console.debug('OAuth1: Added oauth_body_hash for', contentType);
|
|
104
|
-
}
|
|
105
|
-
} else if (contentType.includes(REQUEST_CONTENT_TYPES.multipartFormData)) {
|
|
106
|
-
// For multipart form data, only include text fields
|
|
107
|
-
if (reqConfig.data && typeof reqConfig.data === 'object' && 'entries' in reqConfig.data) {
|
|
108
|
-
const formData = reqConfig.data as FormData;
|
|
109
|
-
for (const [key, value] of formData.entries()) {
|
|
110
|
-
// Only include string values, exclude Files/Blobs
|
|
111
|
-
if (typeof value === 'string') {
|
|
112
|
-
additionalParams[key] = value;
|
|
113
|
-
} else if (typeof value === 'object' && value !== null &&
|
|
114
|
-
('size' in value || 'type' in value)) {
|
|
115
|
-
// Skip binary data (Files, Blobs, etc.)
|
|
116
|
-
continue;
|
|
117
|
-
} else {
|
|
118
|
-
// Include other simple values
|
|
119
|
-
additionalParams[key] = String(value);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
} else if (!contentType && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
|
|
124
|
-
// No content type specified but has data
|
|
125
|
-
if (reqConfig.data) {
|
|
126
|
-
const bodyString = typeof reqConfig.data === 'string' ?
|
|
127
|
-
reqConfig.data : JSON.stringify(reqConfig.data);
|
|
128
|
-
const hash = crypto.createHash('sha1').update(bodyString).digest('base64');
|
|
129
|
-
additionalParams['oauth_body_hash'] = hash;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
console.debug('OAuth1: Total parameters for signature:', Object.keys(additionalParams).length);
|
|
134
|
-
return additionalParams;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export const buildOAuth1Header = (url, method, oauth1Credentials, additionalParams = {}) => {
|
|
138
|
-
const oauth = new OAuth({
|
|
139
|
-
consumer: {
|
|
140
|
-
key: oauth1Credentials.consumerKey,
|
|
141
|
-
secret: oauth1Credentials.consumerSecret,
|
|
142
|
-
},
|
|
143
|
-
signature_method: 'HMAC-SHA1',
|
|
144
|
-
hash_function(base_string, key) {
|
|
145
|
-
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// OAuth1 requires the base URL without query parameters for signature
|
|
150
|
-
// The query parameters should be included separately in additionalParams
|
|
151
|
-
let baseUrl = url;
|
|
152
|
-
try {
|
|
153
|
-
const urlObj = new URL(url);
|
|
154
|
-
// Remove query parameters from URL for signature base
|
|
155
|
-
baseUrl = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;
|
|
156
|
-
console.debug('OAuth1: Base URL for signature:', baseUrl);
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.warn('Failed to parse URL for OAuth1 signature:', error);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Include additional parameters in the request data
|
|
162
|
-
const requestData = {
|
|
163
|
-
url: baseUrl,
|
|
164
|
-
method: method.toUpperCase(),
|
|
165
|
-
data: additionalParams, // Parameters should be in data field for oauth-1.0a library
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const token = oauth1Credentials.token && oauth1Credentials.token !== '' ?
|
|
169
|
-
{ key: oauth1Credentials.token, secret: oauth1Credentials.tokenSecret || '' } :
|
|
170
|
-
null;
|
|
171
|
-
|
|
172
|
-
const signedRequest = oauth.authorize(requestData, token);
|
|
173
|
-
return oauth.toHeader(signedRequest);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
export const retrieveOAuthTokens = async (agent, config) => {
|
|
177
|
-
let tokenKey: any = null;
|
|
178
|
-
try {
|
|
179
|
-
// To support both old and new OAuth configuration, we check for both oauth_con_id and config.id (component id)
|
|
180
|
-
tokenKey = config?.data?.oauth_con_id || `OAUTH_${config?.id}_TOKENS`;
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const result: any = await managedVault.user(AccessCandidate.agent(agent.id)).get(tokenKey);
|
|
184
|
-
const tokensData = typeof result === 'object' ? result : JSON.parse(result || '{}');
|
|
185
|
-
|
|
186
|
-
if (!tokensData) {
|
|
187
|
-
throw new Error('Failed to retrieve OAuth tokens from vault. Please authenticate ...');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Check if it's new structure (has auth_data and auth_settings) or old structure
|
|
191
|
-
const isNewStructure = tokensData.auth_data !== undefined && tokensData.auth_settings !== undefined;
|
|
192
|
-
|
|
193
|
-
// Extract tokens based on structure
|
|
194
|
-
const primaryToken = isNewStructure
|
|
195
|
-
? tokensData.auth_data?.primary
|
|
196
|
-
: tokensData.primary;
|
|
197
|
-
const secondaryToken = isNewStructure
|
|
198
|
-
? tokensData.auth_data?.secondary
|
|
199
|
-
: tokensData.secondary;
|
|
200
|
-
const expiresIn = isNewStructure
|
|
201
|
-
? tokensData.auth_data?.expires_in
|
|
202
|
-
: tokensData.expires_in;
|
|
203
|
-
|
|
204
|
-
// Extract settings based on structure
|
|
205
|
-
const type = isNewStructure
|
|
206
|
-
? tokensData.auth_settings?.type
|
|
207
|
-
: (tokensData.type || tokensData.oauth_info?.type);
|
|
208
|
-
const service = isNewStructure
|
|
209
|
-
? tokensData.auth_settings?.service
|
|
210
|
-
: tokensData.oauth_info?.service;
|
|
211
|
-
|
|
212
|
-
// Add warning logs for OAuth2
|
|
213
|
-
if (type === 'oauth2' && service !== 'oauth2_client_credentials') {
|
|
214
|
-
if (!secondaryToken) {
|
|
215
|
-
console.warn('Warning: refresh_token is missing for OAuth2');
|
|
216
|
-
}
|
|
217
|
-
if (!expiresIn) {
|
|
218
|
-
console.warn('Warning: expires_in is missing for OAuth2.');
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// sometimes refreshToken is not available . e.g in case of linkedIn. so only add check for primary token
|
|
223
|
-
if (service !== 'oauth2_client_credentials') {
|
|
224
|
-
if (!primaryToken) {
|
|
225
|
-
throw new Error('Retrieved OAuth tokens do not exist, invalid OR incomplete. Please authenticate ...');
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const responseData: any = {
|
|
230
|
-
primaryToken,
|
|
231
|
-
secondaryToken,
|
|
232
|
-
type,
|
|
233
|
-
service,
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
if (type === 'oauth') {
|
|
237
|
-
// Extract OAuth1 credentials based on structure
|
|
238
|
-
if (isNewStructure) {
|
|
239
|
-
responseData.consumerKey = tokensData.auth_settings?.consumerKey;
|
|
240
|
-
responseData.consumerSecret = tokensData.auth_settings?.consumerSecret;
|
|
241
|
-
responseData.tokenURL = tokensData.auth_settings?.tokenURL;
|
|
242
|
-
} else {
|
|
243
|
-
responseData.consumerKey = tokensData.consumerKey || tokensData.oauth_info?.consumerKey;
|
|
244
|
-
responseData.consumerSecret = tokensData.consumerSecret || tokensData.oauth_info?.consumerSecret;
|
|
245
|
-
responseData.tokenURL = tokensData.tokenURL || tokensData.oauth_info?.tokenURL;
|
|
246
|
-
}
|
|
247
|
-
responseData.team = tokensData.team || agent.teamId;
|
|
248
|
-
} else if (type === 'oauth2') {
|
|
249
|
-
// Extract OAuth2 credentials based on structure
|
|
250
|
-
if (isNewStructure) {
|
|
251
|
-
responseData.tokenURL = tokensData.auth_settings?.tokenURL;
|
|
252
|
-
responseData.clientID = tokensData.auth_settings?.clientID;
|
|
253
|
-
responseData.clientSecret = tokensData.auth_settings?.clientSecret;
|
|
254
|
-
} else {
|
|
255
|
-
responseData.tokenURL = tokensData.tokenURL || tokensData.oauth_info?.tokenURL;
|
|
256
|
-
responseData.clientID = tokensData.clientID || tokensData.oauth_info?.clientID;
|
|
257
|
-
responseData.clientSecret = tokensData.clientSecret || tokensData.oauth_info?.clientSecret;
|
|
258
|
-
}
|
|
259
|
-
responseData.expiresIn = expiresIn ?? 0; // Optional property, default to 0 if not present
|
|
260
|
-
responseData.team = tokensData.team || agent.teamId;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return { responseData, tokensData, keyId: tokenKey, isNewStructure };
|
|
264
|
-
} catch (error) {
|
|
265
|
-
throw new Error(`Failed to parse retrieved tokens: ${error}`);
|
|
266
|
-
}
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.error('Error retrieving OAuth tokens:', error);
|
|
269
|
-
throw error; // rethrow for potential handling by the calling code
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
export const handleOAuthHeaders = async (agent, config, reqConfig, logger, additionalParams = {}) => {
|
|
274
|
-
let headers = {}; // Initialize headers as an empty object
|
|
275
|
-
const { responseData: oauthTokens, tokensData, keyId, isNewStructure } = await retrieveOAuthTokens(agent, config);
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
// Build OAuth config string with template support
|
|
279
|
-
let oAuthConfigString = JSON.stringify({
|
|
280
|
-
consumerKey: oauthTokens.consumerKey || '',
|
|
281
|
-
consumerSecret: oauthTokens.consumerSecret || '',
|
|
282
|
-
clientID: oauthTokens.clientID || '',
|
|
283
|
-
clientSecret: oauthTokens.clientSecret || '',
|
|
284
|
-
tokenURL: oauthTokens.tokenURL || '',
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
oAuthConfigString = await TemplateString(oAuthConfigString).parseTeamKeysAsync(oauthTokens.team || agent.teamId).asyncResult;
|
|
288
|
-
|
|
289
|
-
const oAuthConfig = JSON.parse(oAuthConfigString);
|
|
290
|
-
// Avoid logging sensitive OAuth config in plaintext
|
|
291
|
-
// console.log('oAuthConfig', { ...oAuthConfig, clientSecret: '***' });
|
|
292
|
-
if (oauthTokens.service === 'oauth2_client_credentials') {
|
|
293
|
-
const accessToken = await getClientCredentialToken(tokensData, logger, keyId, oauthTokens, config, agent, isNewStructure);
|
|
294
|
-
headers['Authorization'] = `Bearer ${accessToken}`;
|
|
295
|
-
} else {
|
|
296
|
-
if (oauthTokens.type === 'oauth') {
|
|
297
|
-
// For OAuth1, generate and replace the signature in headers
|
|
298
|
-
// Use the full URL (with path but without query params) for OAuth1
|
|
299
|
-
const oauthHeader = buildOAuth1Header(
|
|
300
|
-
reqConfig.url,
|
|
301
|
-
reqConfig.method,
|
|
302
|
-
{
|
|
303
|
-
consumerKey: oAuthConfig.consumerKey,
|
|
304
|
-
consumerSecret: oAuthConfig.consumerSecret,
|
|
305
|
-
token: oauthTokens.primaryToken,
|
|
306
|
-
tokenSecret: oauthTokens.secondaryToken,
|
|
307
|
-
},
|
|
308
|
-
additionalParams
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
headers = { ...reqConfig.headers, ...oauthHeader };
|
|
312
|
-
logger.debug('OAuth1 access token check success.');
|
|
313
|
-
} else if (oauthTokens.type === 'oauth2') {
|
|
314
|
-
// For OAuth2, add the 'Authorization' header with the bearer token
|
|
315
|
-
const accessTokenManager = new AccessTokenManager(
|
|
316
|
-
oAuthConfig.clientID,
|
|
317
|
-
oAuthConfig.clientSecret,
|
|
318
|
-
oauthTokens.secondaryToken,
|
|
319
|
-
oAuthConfig.tokenURL,
|
|
320
|
-
oauthTokens.expiresIn,
|
|
321
|
-
oauthTokens.primaryToken,
|
|
322
|
-
tokensData,
|
|
323
|
-
keyId,
|
|
324
|
-
logger,
|
|
325
|
-
agent,
|
|
326
|
-
isNewStructure
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
const accessToken = await accessTokenManager.getAccessToken();
|
|
330
|
-
headers['Authorization'] = `Bearer ${accessToken}`;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return headers;
|
|
334
|
-
} catch (error) {
|
|
335
|
-
logger.error(`Access token check failed: ${error}`);
|
|
336
|
-
throw error;
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
const getKeyIdsFromTemplateVars = (str: string): string[] => {
|
|
341
|
-
if (!str) return [];
|
|
342
|
-
|
|
343
|
-
const pattern = /{{KEY\((.*?)\)}}/g;
|
|
344
|
-
const keyIds: any = [];
|
|
345
|
-
let match: any = [];
|
|
346
|
-
|
|
347
|
-
while ((match = pattern.exec(str)) !== null) {
|
|
348
|
-
if (match?.length < 2) continue;
|
|
349
|
-
keyIds.push(match[1]);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return keyIds;
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
async function getClientCredentialToken(tokensData, logger, keyId, oauthTokens, config, agent, isNewStructure = false) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const logAndThrowError = (message) => {
|
|
359
|
-
logger.debug(message);
|
|
360
|
-
throw new Error(message);
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
const { clientID, clientSecret, tokenURL } = oauthTokens;
|
|
365
|
-
const currentTime = new Date().getTime();
|
|
366
|
-
// Check for token expiration
|
|
367
|
-
if (!oauthTokens.expiresIn || currentTime >= Number(oauthTokens.expiresIn)) {
|
|
368
|
-
// Verify required parameters
|
|
369
|
-
if (!clientID || !clientSecret || !tokenURL) {
|
|
370
|
-
logAndThrowError('Missing client_id, client_secret OR token_url');
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const params = new URLSearchParams({
|
|
374
|
-
grant_type: 'client_credentials',
|
|
375
|
-
client_id: clientID,
|
|
376
|
-
client_secret: clientSecret,
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
const response = await axios.post(tokenURL, params.toString(), {
|
|
380
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
console.log('Access token refreshed successfully.');
|
|
384
|
-
logger.debug('Access token refreshed successfully.');
|
|
385
|
-
|
|
386
|
-
const newAccessToken = response.data.access_token;
|
|
387
|
-
const expiresInMilliseconds = response.data.expires_in * 1000;
|
|
388
|
-
const expirationTimestamp = currentTime + expiresInMilliseconds;
|
|
389
|
-
|
|
390
|
-
// Maintain the same structure format when saving
|
|
391
|
-
let updatedData;
|
|
392
|
-
if (isNewStructure) {
|
|
393
|
-
// Maintain new structure format; preserve existing fields
|
|
394
|
-
const parts = String(config?.data?.oauth_con_id ?? '').split('_');
|
|
395
|
-
const prefixSuffix = parts.length > 1 ? parts[1] : parts[0];
|
|
396
|
-
const oauthKeysPrefix = prefixSuffix ? `OAUTH_${prefixSuffix}` : undefined;
|
|
397
|
-
updatedData = {
|
|
398
|
-
...(tokensData || {}),
|
|
399
|
-
auth_data: {
|
|
400
|
-
...(tokensData?.auth_data || {}),
|
|
401
|
-
primary: newAccessToken,
|
|
402
|
-
expires_in: expirationTimestamp.toString()
|
|
403
|
-
},
|
|
404
|
-
auth_settings: {
|
|
405
|
-
...(tokensData?.auth_settings || {}),
|
|
406
|
-
type: 'oauth2',
|
|
407
|
-
tokenURL,
|
|
408
|
-
clientID,
|
|
409
|
-
clientSecret,
|
|
410
|
-
...(oauthKeysPrefix ? { oauth_keys_prefix: oauthKeysPrefix } : {}),
|
|
411
|
-
service: 'oauth2_client_credentials',
|
|
412
|
-
},
|
|
413
|
-
};
|
|
414
|
-
} else {
|
|
415
|
-
// Maintain old structure format
|
|
416
|
-
updatedData = {
|
|
417
|
-
...tokensData,
|
|
418
|
-
primary: newAccessToken,
|
|
419
|
-
expires_in: expirationTimestamp.toString()
|
|
420
|
-
};
|
|
421
|
-
// Ensure required fields are present for old structure
|
|
422
|
-
if (!updatedData.type) updatedData.type = 'oauth2';
|
|
423
|
-
if (!updatedData.tokenURL) updatedData.tokenURL = tokenURL;
|
|
424
|
-
if (!updatedData.team) updatedData.team = agent.teamId;
|
|
425
|
-
if (!updatedData.oauth_info) {
|
|
426
|
-
updatedData.oauth_info = {
|
|
427
|
-
oauth_keys_prefix: `OAUTH_${config?.data?.oauth_con_id?.split('_')[1] || config?.id}`,
|
|
428
|
-
service: 'oauth2_client_credentials',
|
|
429
|
-
tokenURL,
|
|
430
|
-
clientID,
|
|
431
|
-
clientSecret
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
await managedVault.user(AccessCandidate.agent(agent.id)).set(keyId, JSON.stringify(updatedData));
|
|
437
|
-
|
|
438
|
-
return newAccessToken;
|
|
439
|
-
} else {
|
|
440
|
-
console.log('Access token value is still valid.');
|
|
441
|
-
logger.debug('Access token value is still valid.');
|
|
442
|
-
return oauthTokens.primaryToken;
|
|
443
|
-
}
|
|
444
|
-
} catch (error) {
|
|
445
|
-
logAndThrowError(`Failed to refresh access token: ${error}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
1
|
+
// helper.ts
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import OAuth from 'oauth-1.0a';
|
|
4
|
+
import AccessTokenManager from './AccessTokenManager';
|
|
5
|
+
import { REQUEST_CONTENT_TYPES } from '@sre/constants';
|
|
6
|
+
import axios, { AxiosRequestConfig } from 'axios';
|
|
7
|
+
import { Logger } from '@sre/helpers/Log.helper';
|
|
8
|
+
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
9
|
+
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
10
|
+
import { TemplateString } from '@sre/helpers/TemplateString.helper';
|
|
11
|
+
import { SystemEvents } from '@sre/Core/SystemEvents';
|
|
12
|
+
|
|
13
|
+
const console = Logger('OAuth.helper');
|
|
14
|
+
let managedVault: any;
|
|
15
|
+
|
|
16
|
+
SystemEvents.on('SRE:Booted', () => {
|
|
17
|
+
try {
|
|
18
|
+
managedVault = ConnectorService.getManagedVaultConnector();
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.warn('Could not find a compatible ManagedVault connector, OAuth APICalls will not work');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export function extractAdditionalParamsForOAuth1(reqConfig: AxiosRequestConfig = {}) {
|
|
25
|
+
let additionalParams = {};
|
|
26
|
+
|
|
27
|
+
// Validate URL doesn't contain unresolved template variables
|
|
28
|
+
if (reqConfig.url && (reqConfig.url.includes('{{') || reqConfig.url.includes('${{'))) {
|
|
29
|
+
console.warn('Warning: URL contains unresolved template variables for OAuth1 signature:', reqConfig.url);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Parse URL parameters
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(reqConfig.url);
|
|
35
|
+
const searchParams = url.searchParams;
|
|
36
|
+
additionalParams = Object.fromEntries(searchParams.entries());
|
|
37
|
+
|
|
38
|
+
// Log if we have query parameters for debugging
|
|
39
|
+
if (searchParams.toString()) {
|
|
40
|
+
console.debug('OAuth1: Found query parameters:', Object.keys(additionalParams));
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.warn('Failed to parse URL for OAuth1 parameters:', error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Get the content type, handling different header formats
|
|
47
|
+
const headers = reqConfig.headers || {};
|
|
48
|
+
let contentType = '';
|
|
49
|
+
|
|
50
|
+
// Headers might be an object or array of objects
|
|
51
|
+
if (Array.isArray(headers)) {
|
|
52
|
+
const contentTypeHeader = headers.find(h =>
|
|
53
|
+
Object.keys(h).some(k => k.toLowerCase() === 'content-type')
|
|
54
|
+
);
|
|
55
|
+
if (contentTypeHeader) {
|
|
56
|
+
const key = Object.keys(contentTypeHeader).find(k => k.toLowerCase() === 'content-type');
|
|
57
|
+
contentType = contentTypeHeader[key];
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
contentType = headers['Content-Type'] || headers['content-type'] || '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Extract body parameters based on content type
|
|
64
|
+
const method = (reqConfig.method || 'GET').toUpperCase();
|
|
65
|
+
|
|
66
|
+
if (contentType.includes(REQUEST_CONTENT_TYPES.urlEncodedFormData)) {
|
|
67
|
+
// For form data, include the form parameters in the signature
|
|
68
|
+
if (reqConfig.data) {
|
|
69
|
+
let formParams = {};
|
|
70
|
+
if (typeof reqConfig.data === 'string') {
|
|
71
|
+
// Check for unresolved template variables in form data
|
|
72
|
+
if (reqConfig.data.includes('{{') || reqConfig.data.includes('${{')) {
|
|
73
|
+
console.warn('Warning: Form data contains unresolved template variables for OAuth1 signature');
|
|
74
|
+
}
|
|
75
|
+
const formData = new URLSearchParams(reqConfig.data);
|
|
76
|
+
formParams = Object.fromEntries(formData.entries());
|
|
77
|
+
} else if (reqConfig.data instanceof URLSearchParams) {
|
|
78
|
+
formParams = Object.fromEntries(reqConfig.data.entries());
|
|
79
|
+
} else if (typeof reqConfig.data === 'object') {
|
|
80
|
+
// Handle plain object
|
|
81
|
+
formParams = reqConfig.data;
|
|
82
|
+
}
|
|
83
|
+
console.debug('OAuth1: Including form parameters in signature:', Object.keys(formParams));
|
|
84
|
+
additionalParams = { ...additionalParams, ...formParams };
|
|
85
|
+
}
|
|
86
|
+
} else if (contentType.includes(REQUEST_CONTENT_TYPES.json) ||
|
|
87
|
+
contentType.includes('application/') ||
|
|
88
|
+
contentType.includes('text/')) {
|
|
89
|
+
// For JSON and other non-form data, use oauth_body_hash
|
|
90
|
+
if (reqConfig.data && method !== 'GET' && method !== 'HEAD') {
|
|
91
|
+
let bodyString = '';
|
|
92
|
+
if (typeof reqConfig.data === 'string') {
|
|
93
|
+
bodyString = reqConfig.data;
|
|
94
|
+
} else {
|
|
95
|
+
bodyString = JSON.stringify(reqConfig.data);
|
|
96
|
+
}
|
|
97
|
+
// Check for unresolved template variables
|
|
98
|
+
if (bodyString.includes('{{') || bodyString.includes('${{')) {
|
|
99
|
+
console.warn('Warning: Request body contains unresolved template variables for OAuth1 signature');
|
|
100
|
+
}
|
|
101
|
+
const hash = crypto.createHash('sha1').update(bodyString).digest('base64');
|
|
102
|
+
additionalParams['oauth_body_hash'] = hash;
|
|
103
|
+
console.debug('OAuth1: Added oauth_body_hash for', contentType);
|
|
104
|
+
}
|
|
105
|
+
} else if (contentType.includes(REQUEST_CONTENT_TYPES.multipartFormData)) {
|
|
106
|
+
// For multipart form data, only include text fields
|
|
107
|
+
if (reqConfig.data && typeof reqConfig.data === 'object' && 'entries' in reqConfig.data) {
|
|
108
|
+
const formData = reqConfig.data as FormData;
|
|
109
|
+
for (const [key, value] of formData.entries()) {
|
|
110
|
+
// Only include string values, exclude Files/Blobs
|
|
111
|
+
if (typeof value === 'string') {
|
|
112
|
+
additionalParams[key] = value;
|
|
113
|
+
} else if (typeof value === 'object' && value !== null &&
|
|
114
|
+
('size' in value || 'type' in value)) {
|
|
115
|
+
// Skip binary data (Files, Blobs, etc.)
|
|
116
|
+
continue;
|
|
117
|
+
} else {
|
|
118
|
+
// Include other simple values
|
|
119
|
+
additionalParams[key] = String(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} else if (!contentType && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
|
|
124
|
+
// No content type specified but has data
|
|
125
|
+
if (reqConfig.data) {
|
|
126
|
+
const bodyString = typeof reqConfig.data === 'string' ?
|
|
127
|
+
reqConfig.data : JSON.stringify(reqConfig.data);
|
|
128
|
+
const hash = crypto.createHash('sha1').update(bodyString).digest('base64');
|
|
129
|
+
additionalParams['oauth_body_hash'] = hash;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.debug('OAuth1: Total parameters for signature:', Object.keys(additionalParams).length);
|
|
134
|
+
return additionalParams;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const buildOAuth1Header = (url, method, oauth1Credentials, additionalParams = {}) => {
|
|
138
|
+
const oauth = new OAuth({
|
|
139
|
+
consumer: {
|
|
140
|
+
key: oauth1Credentials.consumerKey,
|
|
141
|
+
secret: oauth1Credentials.consumerSecret,
|
|
142
|
+
},
|
|
143
|
+
signature_method: 'HMAC-SHA1',
|
|
144
|
+
hash_function(base_string, key) {
|
|
145
|
+
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// OAuth1 requires the base URL without query parameters for signature
|
|
150
|
+
// The query parameters should be included separately in additionalParams
|
|
151
|
+
let baseUrl = url;
|
|
152
|
+
try {
|
|
153
|
+
const urlObj = new URL(url);
|
|
154
|
+
// Remove query parameters from URL for signature base
|
|
155
|
+
baseUrl = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;
|
|
156
|
+
console.debug('OAuth1: Base URL for signature:', baseUrl);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.warn('Failed to parse URL for OAuth1 signature:', error);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Include additional parameters in the request data
|
|
162
|
+
const requestData = {
|
|
163
|
+
url: baseUrl,
|
|
164
|
+
method: method.toUpperCase(),
|
|
165
|
+
data: additionalParams, // Parameters should be in data field for oauth-1.0a library
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const token = oauth1Credentials.token && oauth1Credentials.token !== '' ?
|
|
169
|
+
{ key: oauth1Credentials.token, secret: oauth1Credentials.tokenSecret || '' } :
|
|
170
|
+
null;
|
|
171
|
+
|
|
172
|
+
const signedRequest = oauth.authorize(requestData, token);
|
|
173
|
+
return oauth.toHeader(signedRequest);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const retrieveOAuthTokens = async (agent, config) => {
|
|
177
|
+
let tokenKey: any = null;
|
|
178
|
+
try {
|
|
179
|
+
// To support both old and new OAuth configuration, we check for both oauth_con_id and config.id (component id)
|
|
180
|
+
tokenKey = config?.data?.oauth_con_id || `OAUTH_${config?.id}_TOKENS`;
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const result: any = await managedVault.user(AccessCandidate.agent(agent.id)).get(tokenKey);
|
|
184
|
+
const tokensData = typeof result === 'object' ? result : JSON.parse(result || '{}');
|
|
185
|
+
|
|
186
|
+
if (!tokensData) {
|
|
187
|
+
throw new Error('Failed to retrieve OAuth tokens from vault. Please authenticate ...');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check if it's new structure (has auth_data and auth_settings) or old structure
|
|
191
|
+
const isNewStructure = tokensData.auth_data !== undefined && tokensData.auth_settings !== undefined;
|
|
192
|
+
|
|
193
|
+
// Extract tokens based on structure
|
|
194
|
+
const primaryToken = isNewStructure
|
|
195
|
+
? tokensData.auth_data?.primary
|
|
196
|
+
: tokensData.primary;
|
|
197
|
+
const secondaryToken = isNewStructure
|
|
198
|
+
? tokensData.auth_data?.secondary
|
|
199
|
+
: tokensData.secondary;
|
|
200
|
+
const expiresIn = isNewStructure
|
|
201
|
+
? tokensData.auth_data?.expires_in
|
|
202
|
+
: tokensData.expires_in;
|
|
203
|
+
|
|
204
|
+
// Extract settings based on structure
|
|
205
|
+
const type = isNewStructure
|
|
206
|
+
? tokensData.auth_settings?.type
|
|
207
|
+
: (tokensData.type || tokensData.oauth_info?.type);
|
|
208
|
+
const service = isNewStructure
|
|
209
|
+
? tokensData.auth_settings?.service
|
|
210
|
+
: tokensData.oauth_info?.service;
|
|
211
|
+
|
|
212
|
+
// Add warning logs for OAuth2
|
|
213
|
+
if (type === 'oauth2' && service !== 'oauth2_client_credentials') {
|
|
214
|
+
if (!secondaryToken) {
|
|
215
|
+
console.warn('Warning: refresh_token is missing for OAuth2');
|
|
216
|
+
}
|
|
217
|
+
if (!expiresIn) {
|
|
218
|
+
console.warn('Warning: expires_in is missing for OAuth2.');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// sometimes refreshToken is not available . e.g in case of linkedIn. so only add check for primary token
|
|
223
|
+
if (service !== 'oauth2_client_credentials') {
|
|
224
|
+
if (!primaryToken) {
|
|
225
|
+
throw new Error('Retrieved OAuth tokens do not exist, invalid OR incomplete. Please authenticate ...');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const responseData: any = {
|
|
230
|
+
primaryToken,
|
|
231
|
+
secondaryToken,
|
|
232
|
+
type,
|
|
233
|
+
service,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
if (type === 'oauth') {
|
|
237
|
+
// Extract OAuth1 credentials based on structure
|
|
238
|
+
if (isNewStructure) {
|
|
239
|
+
responseData.consumerKey = tokensData.auth_settings?.consumerKey;
|
|
240
|
+
responseData.consumerSecret = tokensData.auth_settings?.consumerSecret;
|
|
241
|
+
responseData.tokenURL = tokensData.auth_settings?.tokenURL;
|
|
242
|
+
} else {
|
|
243
|
+
responseData.consumerKey = tokensData.consumerKey || tokensData.oauth_info?.consumerKey;
|
|
244
|
+
responseData.consumerSecret = tokensData.consumerSecret || tokensData.oauth_info?.consumerSecret;
|
|
245
|
+
responseData.tokenURL = tokensData.tokenURL || tokensData.oauth_info?.tokenURL;
|
|
246
|
+
}
|
|
247
|
+
responseData.team = tokensData.team || agent.teamId;
|
|
248
|
+
} else if (type === 'oauth2') {
|
|
249
|
+
// Extract OAuth2 credentials based on structure
|
|
250
|
+
if (isNewStructure) {
|
|
251
|
+
responseData.tokenURL = tokensData.auth_settings?.tokenURL;
|
|
252
|
+
responseData.clientID = tokensData.auth_settings?.clientID;
|
|
253
|
+
responseData.clientSecret = tokensData.auth_settings?.clientSecret;
|
|
254
|
+
} else {
|
|
255
|
+
responseData.tokenURL = tokensData.tokenURL || tokensData.oauth_info?.tokenURL;
|
|
256
|
+
responseData.clientID = tokensData.clientID || tokensData.oauth_info?.clientID;
|
|
257
|
+
responseData.clientSecret = tokensData.clientSecret || tokensData.oauth_info?.clientSecret;
|
|
258
|
+
}
|
|
259
|
+
responseData.expiresIn = expiresIn ?? 0; // Optional property, default to 0 if not present
|
|
260
|
+
responseData.team = tokensData.team || agent.teamId;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return { responseData, tokensData, keyId: tokenKey, isNewStructure };
|
|
264
|
+
} catch (error) {
|
|
265
|
+
throw new Error(`Failed to parse retrieved tokens: ${error}`);
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
console.error('Error retrieving OAuth tokens:', error);
|
|
269
|
+
throw error; // rethrow for potential handling by the calling code
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export const handleOAuthHeaders = async (agent, config, reqConfig, logger, additionalParams = {}) => {
|
|
274
|
+
let headers = {}; // Initialize headers as an empty object
|
|
275
|
+
const { responseData: oauthTokens, tokensData, keyId, isNewStructure } = await retrieveOAuthTokens(agent, config);
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
// Build OAuth config string with template support
|
|
279
|
+
let oAuthConfigString = JSON.stringify({
|
|
280
|
+
consumerKey: oauthTokens.consumerKey || '',
|
|
281
|
+
consumerSecret: oauthTokens.consumerSecret || '',
|
|
282
|
+
clientID: oauthTokens.clientID || '',
|
|
283
|
+
clientSecret: oauthTokens.clientSecret || '',
|
|
284
|
+
tokenURL: oauthTokens.tokenURL || '',
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
oAuthConfigString = await TemplateString(oAuthConfigString).parseTeamKeysAsync(oauthTokens.team || agent.teamId).asyncResult;
|
|
288
|
+
|
|
289
|
+
const oAuthConfig = JSON.parse(oAuthConfigString);
|
|
290
|
+
// Avoid logging sensitive OAuth config in plaintext
|
|
291
|
+
// console.log('oAuthConfig', { ...oAuthConfig, clientSecret: '***' });
|
|
292
|
+
if (oauthTokens.service === 'oauth2_client_credentials') {
|
|
293
|
+
const accessToken = await getClientCredentialToken(tokensData, logger, keyId, oauthTokens, config, agent, isNewStructure);
|
|
294
|
+
headers['Authorization'] = `Bearer ${accessToken}`;
|
|
295
|
+
} else {
|
|
296
|
+
if (oauthTokens.type === 'oauth') {
|
|
297
|
+
// For OAuth1, generate and replace the signature in headers
|
|
298
|
+
// Use the full URL (with path but without query params) for OAuth1
|
|
299
|
+
const oauthHeader = buildOAuth1Header(
|
|
300
|
+
reqConfig.url,
|
|
301
|
+
reqConfig.method,
|
|
302
|
+
{
|
|
303
|
+
consumerKey: oAuthConfig.consumerKey,
|
|
304
|
+
consumerSecret: oAuthConfig.consumerSecret,
|
|
305
|
+
token: oauthTokens.primaryToken,
|
|
306
|
+
tokenSecret: oauthTokens.secondaryToken,
|
|
307
|
+
},
|
|
308
|
+
additionalParams
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
headers = { ...reqConfig.headers, ...oauthHeader };
|
|
312
|
+
logger.debug('OAuth1 access token check success.');
|
|
313
|
+
} else if (oauthTokens.type === 'oauth2') {
|
|
314
|
+
// For OAuth2, add the 'Authorization' header with the bearer token
|
|
315
|
+
const accessTokenManager = new AccessTokenManager(
|
|
316
|
+
oAuthConfig.clientID,
|
|
317
|
+
oAuthConfig.clientSecret,
|
|
318
|
+
oauthTokens.secondaryToken,
|
|
319
|
+
oAuthConfig.tokenURL,
|
|
320
|
+
oauthTokens.expiresIn,
|
|
321
|
+
oauthTokens.primaryToken,
|
|
322
|
+
tokensData,
|
|
323
|
+
keyId,
|
|
324
|
+
logger,
|
|
325
|
+
agent,
|
|
326
|
+
isNewStructure
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const accessToken = await accessTokenManager.getAccessToken();
|
|
330
|
+
headers['Authorization'] = `Bearer ${accessToken}`;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return headers;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
logger.error(`Access token check failed: ${error}`);
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const getKeyIdsFromTemplateVars = (str: string): string[] => {
|
|
341
|
+
if (!str) return [];
|
|
342
|
+
|
|
343
|
+
const pattern = /{{KEY\((.*?)\)}}/g;
|
|
344
|
+
const keyIds: any = [];
|
|
345
|
+
let match: any = [];
|
|
346
|
+
|
|
347
|
+
while ((match = pattern.exec(str)) !== null) {
|
|
348
|
+
if (match?.length < 2) continue;
|
|
349
|
+
keyIds.push(match[1]);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return keyIds;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
async function getClientCredentialToken(tokensData, logger, keyId, oauthTokens, config, agent, isNewStructure = false) {
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
const logAndThrowError = (message) => {
|
|
359
|
+
logger.debug(message);
|
|
360
|
+
throw new Error(message);
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const { clientID, clientSecret, tokenURL } = oauthTokens;
|
|
365
|
+
const currentTime = new Date().getTime();
|
|
366
|
+
// Check for token expiration
|
|
367
|
+
if (!oauthTokens.expiresIn || currentTime >= Number(oauthTokens.expiresIn)) {
|
|
368
|
+
// Verify required parameters
|
|
369
|
+
if (!clientID || !clientSecret || !tokenURL) {
|
|
370
|
+
logAndThrowError('Missing client_id, client_secret OR token_url');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const params = new URLSearchParams({
|
|
374
|
+
grant_type: 'client_credentials',
|
|
375
|
+
client_id: clientID,
|
|
376
|
+
client_secret: clientSecret,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const response = await axios.post(tokenURL, params.toString(), {
|
|
380
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
console.log('Access token refreshed successfully.');
|
|
384
|
+
logger.debug('Access token refreshed successfully.');
|
|
385
|
+
|
|
386
|
+
const newAccessToken = response.data.access_token;
|
|
387
|
+
const expiresInMilliseconds = response.data.expires_in * 1000;
|
|
388
|
+
const expirationTimestamp = currentTime + expiresInMilliseconds;
|
|
389
|
+
|
|
390
|
+
// Maintain the same structure format when saving
|
|
391
|
+
let updatedData;
|
|
392
|
+
if (isNewStructure) {
|
|
393
|
+
// Maintain new structure format; preserve existing fields
|
|
394
|
+
const parts = String(config?.data?.oauth_con_id ?? '').split('_');
|
|
395
|
+
const prefixSuffix = parts.length > 1 ? parts[1] : parts[0];
|
|
396
|
+
const oauthKeysPrefix = prefixSuffix ? `OAUTH_${prefixSuffix}` : undefined;
|
|
397
|
+
updatedData = {
|
|
398
|
+
...(tokensData || {}),
|
|
399
|
+
auth_data: {
|
|
400
|
+
...(tokensData?.auth_data || {}),
|
|
401
|
+
primary: newAccessToken,
|
|
402
|
+
expires_in: expirationTimestamp.toString()
|
|
403
|
+
},
|
|
404
|
+
auth_settings: {
|
|
405
|
+
...(tokensData?.auth_settings || {}),
|
|
406
|
+
type: 'oauth2',
|
|
407
|
+
tokenURL,
|
|
408
|
+
clientID,
|
|
409
|
+
clientSecret,
|
|
410
|
+
...(oauthKeysPrefix ? { oauth_keys_prefix: oauthKeysPrefix } : {}),
|
|
411
|
+
service: 'oauth2_client_credentials',
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
} else {
|
|
415
|
+
// Maintain old structure format
|
|
416
|
+
updatedData = {
|
|
417
|
+
...tokensData,
|
|
418
|
+
primary: newAccessToken,
|
|
419
|
+
expires_in: expirationTimestamp.toString()
|
|
420
|
+
};
|
|
421
|
+
// Ensure required fields are present for old structure
|
|
422
|
+
if (!updatedData.type) updatedData.type = 'oauth2';
|
|
423
|
+
if (!updatedData.tokenURL) updatedData.tokenURL = tokenURL;
|
|
424
|
+
if (!updatedData.team) updatedData.team = agent.teamId;
|
|
425
|
+
if (!updatedData.oauth_info) {
|
|
426
|
+
updatedData.oauth_info = {
|
|
427
|
+
oauth_keys_prefix: `OAUTH_${config?.data?.oauth_con_id?.split('_')[1] || config?.id}`,
|
|
428
|
+
service: 'oauth2_client_credentials',
|
|
429
|
+
tokenURL,
|
|
430
|
+
clientID,
|
|
431
|
+
clientSecret
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
await managedVault.user(AccessCandidate.agent(agent.id)).set(keyId, JSON.stringify(updatedData));
|
|
437
|
+
|
|
438
|
+
return newAccessToken;
|
|
439
|
+
} else {
|
|
440
|
+
console.log('Access token value is still valid.');
|
|
441
|
+
logger.debug('Access token value is still valid.');
|
|
442
|
+
return oauthTokens.primaryToken;
|
|
443
|
+
}
|
|
444
|
+
} catch (error) {
|
|
445
|
+
logAndThrowError(`Failed to refresh access token: ${error}`);
|
|
446
|
+
}
|
|
447
|
+
}
|