@mcp-abap-adt/auth-providers 4.0.0 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -0
- package/README.md +180 -27
- package/dist/auth/clientCredentialsAuth.d.ts.map +1 -1
- package/dist/auth/clientCredentialsAuth.js +2 -1
- package/dist/auth/oauthErrorBody.d.ts +15 -0
- package/dist/auth/oauthErrorBody.d.ts.map +1 -0
- package/dist/auth/oauthErrorBody.js +58 -0
- package/dist/auth/saml2TokenExchange.d.ts.map +1 -1
- package/dist/auth/saml2TokenExchange.js +9 -2
- package/dist/auth/samlBearerAssertion.d.ts.map +1 -1
- package/dist/auth/samlBearerAssertion.js +4 -1
- package/dist/auth/tokenRefresher.d.ts.map +1 -1
- package/dist/auth/tokenRefresher.js +2 -1
- package/dist/providers/BaseTokenProvider.d.ts +3 -1
- package/dist/providers/BaseTokenProvider.d.ts.map +1 -1
- package/dist/providers/BaseTokenProvider.js +4 -4
- package/dist/providers/saml2Utils.d.ts +3 -2
- package/dist/providers/saml2Utils.d.ts.map +1 -1
- package/dist/providers/saml2Utils.js +8 -0
- package/dist/validation/assertionValidator.d.ts.map +1 -1
- package/dist/validation/assertionValidator.js +207 -132
- package/dist/validation/signedNode.d.ts +2 -2
- package/dist/validation/signedNode.js +16 -5
- package/package.json +6 -12
- package/bin/auth-authorization-code.ts +0 -147
- package/bin/auth-client-credentials.ts +0 -109
- package/bin/utils/parseConfig.ts +0 -270
- package/dist/__tests__/helpers/configHelpers.d.ts +0 -46
- package/dist/__tests__/helpers/configHelpers.d.ts.map +0 -1
- package/dist/__tests__/helpers/configHelpers.js +0 -232
- package/dist/__tests__/helpers/netHelpers.d.ts +0 -19
- package/dist/__tests__/helpers/netHelpers.d.ts.map +0 -1
- package/dist/__tests__/helpers/netHelpers.js +0 -92
- package/dist/__tests__/helpers/testLogger.d.ts +0 -7
- package/dist/__tests__/helpers/testLogger.d.ts.map +0 -1
- package/dist/__tests__/helpers/testLogger.js +0 -45
- package/dist/__tests__/integration/stand/formLogin.d.ts +0 -68
- package/dist/__tests__/integration/stand/formLogin.d.ts.map +0 -1
- package/dist/__tests__/integration/stand/formLogin.js +0 -194
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/auth-providers",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"description": "Token providers for MCP ABAP ADT auth-broker",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
-
"bin",
|
|
10
9
|
"README.md",
|
|
11
10
|
"CHANGELOG.md",
|
|
12
11
|
"LICENSE",
|
|
@@ -38,18 +37,14 @@
|
|
|
38
37
|
"publishConfig": {
|
|
39
38
|
"access": "public"
|
|
40
39
|
},
|
|
41
|
-
"bin": {
|
|
42
|
-
"auth-authorization-code": "./bin/auth-authorization-code.ts",
|
|
43
|
-
"auth-client-credentials": "./bin/auth-client-credentials.ts"
|
|
44
|
-
},
|
|
45
40
|
"scripts": {
|
|
46
41
|
"chrono": "./tools/version-stats.sh",
|
|
47
|
-
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
42
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo",
|
|
48
43
|
"lint": "npx biome check --write src",
|
|
49
44
|
"lint:check": "npx biome check src",
|
|
50
45
|
"format": "npx biome format --write src",
|
|
51
|
-
"build": "npm run --silent clean && npx biome check src --diagnostic-level=error && npx tsc -p tsconfig.json",
|
|
52
|
-
"build:fast": "npx tsc -p tsconfig.json",
|
|
46
|
+
"build": "npm run --silent clean && npx biome check src --diagnostic-level=error && npx tsc -p tsconfig.build.json",
|
|
47
|
+
"build:fast": "npx tsc -p tsconfig.build.json",
|
|
53
48
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
54
49
|
"test:check": "npx tsc --noEmit",
|
|
55
50
|
"prepublishOnly": "npm run build",
|
|
@@ -62,7 +57,7 @@
|
|
|
62
57
|
"node": "^22 || ^24"
|
|
63
58
|
},
|
|
64
59
|
"dependencies": {
|
|
65
|
-
"@mcp-abap-adt/interfaces-auth": "^2.0.
|
|
60
|
+
"@mcp-abap-adt/interfaces-auth": "^2.0.1",
|
|
66
61
|
"@mcp-abap-adt/interfaces-auth-sap": "^1.0.1",
|
|
67
62
|
"@mcp-abap-adt/interfaces-utils": "^1.1.0",
|
|
68
63
|
"@xmldom/xmldom": "^0.9.12",
|
|
@@ -75,7 +70,7 @@
|
|
|
75
70
|
"@biomejs/biome": "^2.3.14",
|
|
76
71
|
"@jest/globals": "^30.2.0",
|
|
77
72
|
"@mcp-abap-adt/auth-mocks": "^0.3.0",
|
|
78
|
-
"@mcp-abap-adt/auth-stores": "^1.
|
|
73
|
+
"@mcp-abap-adt/auth-stores": "^1.2.1",
|
|
79
74
|
"@mcp-abap-adt/logger": "^0.4.0",
|
|
80
75
|
"@types/express": "^5.0.5",
|
|
81
76
|
"@types/jest": "^30.0.0",
|
|
@@ -87,7 +82,6 @@
|
|
|
87
82
|
"pino": "^10.3.1",
|
|
88
83
|
"pino-pretty": "^13.1.3",
|
|
89
84
|
"ts-jest": "^29.2.5",
|
|
90
|
-
"tsx": "^4.19.2",
|
|
91
85
|
"typescript": "^5.9.2"
|
|
92
86
|
}
|
|
93
87
|
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
/**
|
|
3
|
-
* Authorization Code Provider Test Command
|
|
4
|
-
*
|
|
5
|
-
* Gets tokens using authorization_code grant type
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
ABAP_AUTHORIZATION_VARS,
|
|
10
|
-
ABAP_CONNECTION_VARS,
|
|
11
|
-
} from '@mcp-abap-adt/auth-stores';
|
|
12
|
-
import { AuthorizationCodeProvider } from '../src/providers/AuthorizationCodeProvider';
|
|
13
|
-
import { browserCallbackStrategy } from '../src/strategies';
|
|
14
|
-
import {
|
|
15
|
-
getUaaCredentials,
|
|
16
|
-
parseEnvFile,
|
|
17
|
-
parseServiceKey,
|
|
18
|
-
writeEnvFile,
|
|
19
|
-
} from './utils/parseConfig';
|
|
20
|
-
|
|
21
|
-
async function main() {
|
|
22
|
-
const args = process.argv.slice(2);
|
|
23
|
-
let serviceKeyPath: string | undefined;
|
|
24
|
-
let inputEnvPath: string | undefined;
|
|
25
|
-
let outputEnvPath: string | undefined;
|
|
26
|
-
let browser: string = 'none';
|
|
27
|
-
let port: number = 3001;
|
|
28
|
-
let authorizationUrl: string | undefined;
|
|
29
|
-
|
|
30
|
-
// Parse arguments
|
|
31
|
-
for (let i = 0; i < args.length; i++) {
|
|
32
|
-
const arg = args[i];
|
|
33
|
-
if (arg === '--service-key' && args[i + 1]) {
|
|
34
|
-
serviceKeyPath = args[++i];
|
|
35
|
-
} else if (arg === '--input-env' && args[i + 1]) {
|
|
36
|
-
inputEnvPath = args[++i];
|
|
37
|
-
} else if (arg === '--output-env' && args[i + 1]) {
|
|
38
|
-
outputEnvPath = args[++i];
|
|
39
|
-
} else if (arg === '--browser' && args[i + 1]) {
|
|
40
|
-
browser = args[++i];
|
|
41
|
-
} else if (arg === '--port' && args[i + 1]) {
|
|
42
|
-
port = parseInt(args[++i], 10);
|
|
43
|
-
} else if (arg === '--auth-url' && args[i + 1]) {
|
|
44
|
-
authorizationUrl = args[++i];
|
|
45
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
46
|
-
console.log(`
|
|
47
|
-
Usage: auth-authorization-code [options]
|
|
48
|
-
|
|
49
|
-
Options:
|
|
50
|
-
--service-key <path> Path to service key JSON file
|
|
51
|
-
--input-env <path> Path to .env file for reading existing tokens (for refresh)
|
|
52
|
-
--output-env <path> Path to .env file for saving tokens (required)
|
|
53
|
-
--browser <type> Browser type (auto, system, chrome, none) [default: none]
|
|
54
|
-
--port <number> Callback server port [default: 3001]
|
|
55
|
-
--auth-url <url> Pre-built authorization URL
|
|
56
|
-
--help, -h Show this help message
|
|
57
|
-
|
|
58
|
-
Example:
|
|
59
|
-
auth-authorization-code --service-key ./service-key.json --output-env ./tokens.env --browser auto
|
|
60
|
-
auth-authorization-code --input-env ./tokens.env --output-env ./tokens.env --browser none
|
|
61
|
-
`);
|
|
62
|
-
process.exit(0);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!serviceKeyPath && !inputEnvPath) {
|
|
67
|
-
console.error('❌ Error: Either --service-key or --input-env must be provided');
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!outputEnvPath) {
|
|
72
|
-
console.error('❌ Error: --output-env is required to save tokens');
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
// Parse config
|
|
78
|
-
const serviceKey = serviceKeyPath ? parseServiceKey(serviceKeyPath) : undefined;
|
|
79
|
-
const inputEnv = inputEnvPath ? parseEnvFile(inputEnvPath) : undefined;
|
|
80
|
-
const { uaaUrl, clientId, clientSecret } = getUaaCredentials(serviceKey, inputEnv);
|
|
81
|
-
|
|
82
|
-
// Read existing tokens from input env if provided (use constants, fallback to legacy)
|
|
83
|
-
const existingRefreshToken =
|
|
84
|
-
inputEnv?.[ABAP_AUTHORIZATION_VARS.REFRESH_TOKEN] ||
|
|
85
|
-
inputEnv?.REFRESH_TOKEN;
|
|
86
|
-
|
|
87
|
-
// Build authorization URL if not provided
|
|
88
|
-
if (!authorizationUrl) {
|
|
89
|
-
const redirectUri = `http://localhost:${port}/callback`;
|
|
90
|
-
authorizationUrl = `${uaaUrl}/oauth/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Create provider. `--port` now reaches the strategy that binds the socket;
|
|
94
|
-
// it must stay the port baked into the authorization URL above, or the
|
|
95
|
-
// provider rejects the mismatch before opening anything.
|
|
96
|
-
const provider = new AuthorizationCodeProvider({
|
|
97
|
-
authorizationUrl,
|
|
98
|
-
uaaUrl,
|
|
99
|
-
clientId,
|
|
100
|
-
clientSecret,
|
|
101
|
-
authorization: browserCallbackStrategy({ browser, port }),
|
|
102
|
-
refreshToken: existingRefreshToken,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
console.log('🔐 Getting tokens using Authorization Code flow...');
|
|
106
|
-
if (serviceKeyPath) {
|
|
107
|
-
console.log(`📁 Service Key: ${serviceKeyPath}`);
|
|
108
|
-
}
|
|
109
|
-
if (inputEnvPath) {
|
|
110
|
-
console.log(`📁 Input Env: ${inputEnvPath}`);
|
|
111
|
-
}
|
|
112
|
-
console.log(`💾 Output Env: ${outputEnvPath}`);
|
|
113
|
-
console.log(`🌐 Browser: ${browser}`);
|
|
114
|
-
console.log(`🔗 Authorization URL: ${authorizationUrl}\n`);
|
|
115
|
-
|
|
116
|
-
// Get tokens
|
|
117
|
-
const result = await provider.getTokens();
|
|
118
|
-
|
|
119
|
-
// Save tokens to output env file
|
|
120
|
-
writeEnvFile(outputEnvPath, {
|
|
121
|
-
authorizationToken: result.authorizationToken,
|
|
122
|
-
refreshToken: result.refreshToken,
|
|
123
|
-
uaaUrl,
|
|
124
|
-
clientId,
|
|
125
|
-
clientSecret,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
console.log('✅ Tokens obtained and saved successfully!');
|
|
129
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
130
|
-
console.log(`🔑 Authorization Token: ${result.authorizationToken.substring(0, 50)}...`);
|
|
131
|
-
if (result.refreshToken) {
|
|
132
|
-
console.log(`🔄 Refresh Token: ${result.refreshToken.substring(0, 50)}...`);
|
|
133
|
-
}
|
|
134
|
-
console.log(`📋 Auth Type: ${result.authType}`);
|
|
135
|
-
if (result.expiresIn) {
|
|
136
|
-
console.log(`⏰ Expires In: ${result.expiresIn} seconds`);
|
|
137
|
-
}
|
|
138
|
-
console.log(`💾 Saved to: ${outputEnvPath}`);
|
|
139
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
main();
|
|
147
|
-
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
/**
|
|
3
|
-
* Client Credentials Provider Test Command
|
|
4
|
-
*
|
|
5
|
-
* Gets tokens using client_credentials grant type
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { ClientCredentialsProvider } from '../src/providers/ClientCredentialsProvider';
|
|
9
|
-
import {
|
|
10
|
-
getUaaCredentials,
|
|
11
|
-
parseEnvFile,
|
|
12
|
-
parseServiceKey,
|
|
13
|
-
writeEnvFile,
|
|
14
|
-
} from './utils/parseConfig';
|
|
15
|
-
|
|
16
|
-
async function main() {
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
let serviceKeyPath: string | undefined;
|
|
19
|
-
let inputEnvPath: string | undefined;
|
|
20
|
-
let outputEnvPath: string | undefined;
|
|
21
|
-
|
|
22
|
-
// Parse arguments
|
|
23
|
-
for (let i = 0; i < args.length; i++) {
|
|
24
|
-
const arg = args[i];
|
|
25
|
-
if (arg === '--service-key' && args[i + 1]) {
|
|
26
|
-
serviceKeyPath = args[++i];
|
|
27
|
-
} else if (arg === '--input-env' && args[i + 1]) {
|
|
28
|
-
inputEnvPath = args[++i];
|
|
29
|
-
} else if (arg === '--output-env' && args[i + 1]) {
|
|
30
|
-
outputEnvPath = args[++i];
|
|
31
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
32
|
-
console.log(`
|
|
33
|
-
Usage: auth-client-credentials [options]
|
|
34
|
-
|
|
35
|
-
Options:
|
|
36
|
-
--service-key <path> Path to service key JSON file
|
|
37
|
-
--input-env <path> Path to .env file for reading credentials
|
|
38
|
-
--output-env <path> Path to .env file for saving tokens (required)
|
|
39
|
-
--help, -h Show this help message
|
|
40
|
-
|
|
41
|
-
Example:
|
|
42
|
-
auth-client-credentials --service-key ./service-key.json --output-env ./tokens.env
|
|
43
|
-
auth-client-credentials --input-env ./tokens.env --output-env ./tokens.env
|
|
44
|
-
`);
|
|
45
|
-
process.exit(0);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!serviceKeyPath && !inputEnvPath) {
|
|
50
|
-
console.error('❌ Error: Either --service-key or --input-env must be provided');
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!outputEnvPath) {
|
|
55
|
-
console.error('❌ Error: --output-env is required to save tokens');
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
// Parse config
|
|
61
|
-
const serviceKey = serviceKeyPath ? parseServiceKey(serviceKeyPath) : undefined;
|
|
62
|
-
const inputEnv = inputEnvPath ? parseEnvFile(inputEnvPath) : undefined;
|
|
63
|
-
const { uaaUrl, clientId, clientSecret } = getUaaCredentials(serviceKey, inputEnv);
|
|
64
|
-
|
|
65
|
-
// Create provider
|
|
66
|
-
const provider = new ClientCredentialsProvider({
|
|
67
|
-
uaaUrl,
|
|
68
|
-
clientId,
|
|
69
|
-
clientSecret,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
console.log('🔐 Getting tokens using Client Credentials flow...');
|
|
73
|
-
if (serviceKeyPath) {
|
|
74
|
-
console.log(`📁 Service Key: ${serviceKeyPath}`);
|
|
75
|
-
}
|
|
76
|
-
if (inputEnvPath) {
|
|
77
|
-
console.log(`📁 Input Env: ${inputEnvPath}`);
|
|
78
|
-
}
|
|
79
|
-
console.log(`💾 Output Env: ${outputEnvPath}\n`);
|
|
80
|
-
|
|
81
|
-
// Get tokens
|
|
82
|
-
const result = await provider.getTokens();
|
|
83
|
-
|
|
84
|
-
// Save tokens to output env file
|
|
85
|
-
writeEnvFile(outputEnvPath, {
|
|
86
|
-
authorizationToken: result.authorizationToken,
|
|
87
|
-
refreshToken: result.refreshToken,
|
|
88
|
-
uaaUrl,
|
|
89
|
-
clientId,
|
|
90
|
-
clientSecret,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
console.log('✅ Tokens obtained and saved successfully!');
|
|
94
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
95
|
-
console.log(`🔑 Authorization Token: ${result.authorizationToken.substring(0, 50)}...`);
|
|
96
|
-
console.log(`📋 Auth Type: ${result.authType}`);
|
|
97
|
-
if (result.expiresIn) {
|
|
98
|
-
console.log(`⏰ Expires In: ${result.expiresIn} seconds`);
|
|
99
|
-
}
|
|
100
|
-
console.log(`💾 Saved to: ${outputEnvPath}`);
|
|
101
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
102
|
-
} catch (error) {
|
|
103
|
-
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
main();
|
|
109
|
-
|
package/bin/utils/parseConfig.ts
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for parsing service keys and env files
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
ABAP_AUTHORIZATION_VARS,
|
|
7
|
-
ABAP_CONNECTION_VARS,
|
|
8
|
-
} from '@mcp-abap-adt/auth-stores';
|
|
9
|
-
import * as fs from 'node:fs';
|
|
10
|
-
import * as path from 'node:path';
|
|
11
|
-
|
|
12
|
-
export interface ServiceKey {
|
|
13
|
-
// Format 1: ABAP Environment service key (nested uaa object)
|
|
14
|
-
uaa?: {
|
|
15
|
-
url?: string;
|
|
16
|
-
clientid?: string;
|
|
17
|
-
clientsecret?: string;
|
|
18
|
-
};
|
|
19
|
-
// Format 2: XSUAA/BTP service key (flat structure)
|
|
20
|
-
url?: string;
|
|
21
|
-
clientid?: string;
|
|
22
|
-
clientsecret?: string;
|
|
23
|
-
// ABAP service URL
|
|
24
|
-
sap_url?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface EnvConfig {
|
|
28
|
-
// ABAP Connection vars
|
|
29
|
-
[ABAP_CONNECTION_VARS.SERVICE_URL]?: string;
|
|
30
|
-
[ABAP_CONNECTION_VARS.AUTHORIZATION_TOKEN]?: string;
|
|
31
|
-
[ABAP_CONNECTION_VARS.USERNAME]?: string;
|
|
32
|
-
[ABAP_CONNECTION_VARS.PASSWORD]?: string;
|
|
33
|
-
[ABAP_CONNECTION_VARS.SAP_CLIENT]?: string;
|
|
34
|
-
[ABAP_CONNECTION_VARS.SAP_LANGUAGE]?: string;
|
|
35
|
-
// ABAP Authorization vars
|
|
36
|
-
[ABAP_AUTHORIZATION_VARS.UAA_URL]?: string;
|
|
37
|
-
[ABAP_AUTHORIZATION_VARS.UAA_CLIENT_ID]?: string;
|
|
38
|
-
[ABAP_AUTHORIZATION_VARS.UAA_CLIENT_SECRET]?: string;
|
|
39
|
-
[ABAP_AUTHORIZATION_VARS.REFRESH_TOKEN]?: string;
|
|
40
|
-
// Legacy support (for backward compatibility)
|
|
41
|
-
UAA_URL?: string;
|
|
42
|
-
UAA_CLIENT_ID?: string;
|
|
43
|
-
UAA_CLIENT_SECRET?: string;
|
|
44
|
-
SAP_URL?: string;
|
|
45
|
-
SERVICE_URL?: string;
|
|
46
|
-
AUTHORIZATION_TOKEN?: string;
|
|
47
|
-
REFRESH_TOKEN?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Parse service key from JSON file
|
|
52
|
-
*/
|
|
53
|
-
export function parseServiceKey(filePath: string): ServiceKey {
|
|
54
|
-
const fullPath = path.resolve(filePath);
|
|
55
|
-
if (!fs.existsSync(fullPath)) {
|
|
56
|
-
throw new Error(`Service key file not found: ${fullPath}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
60
|
-
try {
|
|
61
|
-
return JSON.parse(content) as ServiceKey;
|
|
62
|
-
} catch (error) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Failed to parse service key file: ${error instanceof Error ? error.message : String(error)}`,
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Parse .env file
|
|
71
|
-
*/
|
|
72
|
-
export function parseEnvFile(filePath: string): EnvConfig {
|
|
73
|
-
const fullPath = path.resolve(filePath);
|
|
74
|
-
if (!fs.existsSync(fullPath)) {
|
|
75
|
-
throw new Error(`Env file not found: ${fullPath}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
79
|
-
const config: EnvConfig = {};
|
|
80
|
-
|
|
81
|
-
for (const line of content.split('\n')) {
|
|
82
|
-
const trimmed = line.trim();
|
|
83
|
-
if (!trimmed || trimmed.startsWith('#')) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
88
|
-
if (match) {
|
|
89
|
-
const key = match[1].trim();
|
|
90
|
-
const value = match[2].trim().replace(/^["']|["']$/g, '');
|
|
91
|
-
(config as Record<string, string>)[key] = value;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return config;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Get UAA credentials from service key or env file
|
|
100
|
-
* Supports two service key formats:
|
|
101
|
-
* 1. ABAP Environment: nested uaa object (uaa.url, uaa.clientid, uaa.clientsecret)
|
|
102
|
-
* 2. XSUAA/BTP flat: top-level fields (url, clientid, clientsecret)
|
|
103
|
-
*/
|
|
104
|
-
export function getUaaCredentials(
|
|
105
|
-
serviceKey?: ServiceKey,
|
|
106
|
-
envConfig?: EnvConfig,
|
|
107
|
-
): {
|
|
108
|
-
uaaUrl: string;
|
|
109
|
-
clientId: string;
|
|
110
|
-
clientSecret: string;
|
|
111
|
-
} {
|
|
112
|
-
if (serviceKey) {
|
|
113
|
-
// Format 1: ABAP Environment service key with nested uaa object
|
|
114
|
-
if (serviceKey.uaa?.url && serviceKey.uaa?.clientid && serviceKey.uaa?.clientsecret) {
|
|
115
|
-
return {
|
|
116
|
-
uaaUrl: serviceKey.uaa.url,
|
|
117
|
-
clientId: serviceKey.uaa.clientid,
|
|
118
|
-
clientSecret: serviceKey.uaa.clientsecret,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Format 2: XSUAA/BTP flat service key (url is UAA URL, no nested uaa object)
|
|
123
|
-
if (serviceKey.url && serviceKey.clientid && serviceKey.clientsecret) {
|
|
124
|
-
return {
|
|
125
|
-
uaaUrl: serviceKey.url,
|
|
126
|
-
clientId: serviceKey.clientid,
|
|
127
|
-
clientSecret: serviceKey.clientsecret,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Try to provide helpful error message
|
|
132
|
-
const hasUaa = !!serviceKey.uaa;
|
|
133
|
-
const hasFlat = !!(serviceKey.clientid || serviceKey.clientsecret);
|
|
134
|
-
if (hasUaa) {
|
|
135
|
-
throw new Error(
|
|
136
|
-
'Service key (ABAP format) missing required UAA fields: uaa.url, uaa.clientid, uaa.clientsecret',
|
|
137
|
-
);
|
|
138
|
-
} else if (hasFlat) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
'Service key (XSUAA format) missing required fields: url, clientid, clientsecret',
|
|
141
|
-
);
|
|
142
|
-
} else {
|
|
143
|
-
throw new Error(
|
|
144
|
-
'Service key format not recognized. Expected either ABAP format (uaa.url, uaa.clientid, uaa.clientsecret) or XSUAA format (url, clientid, clientsecret)',
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (envConfig) {
|
|
150
|
-
// Use constants from auth-stores, fallback to legacy names
|
|
151
|
-
const uaaUrl =
|
|
152
|
-
envConfig[ABAP_AUTHORIZATION_VARS.UAA_URL] || envConfig.UAA_URL;
|
|
153
|
-
const clientId =
|
|
154
|
-
envConfig[ABAP_AUTHORIZATION_VARS.UAA_CLIENT_ID] ||
|
|
155
|
-
envConfig.UAA_CLIENT_ID;
|
|
156
|
-
const clientSecret =
|
|
157
|
-
envConfig[ABAP_AUTHORIZATION_VARS.UAA_CLIENT_SECRET] ||
|
|
158
|
-
envConfig.UAA_CLIENT_SECRET;
|
|
159
|
-
|
|
160
|
-
if (!uaaUrl || !clientId || !clientSecret) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
`Env file missing required UAA fields: ${ABAP_AUTHORIZATION_VARS.UAA_URL}, ${ABAP_AUTHORIZATION_VARS.UAA_CLIENT_ID}, ${ABAP_AUTHORIZATION_VARS.UAA_CLIENT_SECRET}`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return { uaaUrl, clientId, clientSecret };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
throw new Error('No service key or env config provided');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Convert ABAP URL to ABAP-Web URL for ADT reentrancy ticket
|
|
174
|
-
* @param url Original URL (may contain .abap. or .abap-web.)
|
|
175
|
-
* @returns URL with .abap-web. domain
|
|
176
|
-
*/
|
|
177
|
-
function convertToAbapWebUrl(url: string): string {
|
|
178
|
-
// Replace .abap. with .abap-web. for ADT reentrancy ticket
|
|
179
|
-
return url.replace(/\.abap\./g, '.abap-web.');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get service URL from service key or env file
|
|
184
|
-
* For ADT, converts .abap. to .abap-web. if needed
|
|
185
|
-
*/
|
|
186
|
-
export function getServiceUrl(
|
|
187
|
-
serviceKey?: ServiceKey,
|
|
188
|
-
envConfig?: EnvConfig,
|
|
189
|
-
forAdt: boolean = false,
|
|
190
|
-
): string | undefined {
|
|
191
|
-
let url: string | undefined;
|
|
192
|
-
|
|
193
|
-
if (serviceKey) {
|
|
194
|
-
url = serviceKey.url || serviceKey.sap_url;
|
|
195
|
-
} else if (envConfig) {
|
|
196
|
-
// Use constants from auth-stores, fallback to legacy names
|
|
197
|
-
url =
|
|
198
|
-
envConfig[ABAP_CONNECTION_VARS.SERVICE_URL] ||
|
|
199
|
-
envConfig.SAP_URL ||
|
|
200
|
-
envConfig.SERVICE_URL;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (url && forAdt) {
|
|
204
|
-
url = convertToAbapWebUrl(url);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return url;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Write tokens to .env file
|
|
212
|
-
*/
|
|
213
|
-
export function writeEnvFile(
|
|
214
|
-
filePath: string,
|
|
215
|
-
tokens: {
|
|
216
|
-
authorizationToken?: string;
|
|
217
|
-
refreshToken?: string;
|
|
218
|
-
uaaUrl?: string;
|
|
219
|
-
clientId?: string;
|
|
220
|
-
clientSecret?: string;
|
|
221
|
-
serviceUrl?: string;
|
|
222
|
-
},
|
|
223
|
-
): void {
|
|
224
|
-
const fullPath = path.resolve(filePath);
|
|
225
|
-
const lines: string[] = [];
|
|
226
|
-
|
|
227
|
-
// Read existing file if it exists
|
|
228
|
-
const existing: EnvConfig = fs.existsSync(fullPath)
|
|
229
|
-
? parseEnvFile(fullPath)
|
|
230
|
-
: {};
|
|
231
|
-
|
|
232
|
-
// Update with new tokens using constants from auth-stores
|
|
233
|
-
if (tokens.authorizationToken) {
|
|
234
|
-
existing[ABAP_CONNECTION_VARS.AUTHORIZATION_TOKEN] =
|
|
235
|
-
tokens.authorizationToken;
|
|
236
|
-
}
|
|
237
|
-
// Update refresh token only if provided (don't remove existing if not provided)
|
|
238
|
-
if (tokens.refreshToken) {
|
|
239
|
-
existing[ABAP_AUTHORIZATION_VARS.REFRESH_TOKEN] = tokens.refreshToken;
|
|
240
|
-
}
|
|
241
|
-
if (tokens.uaaUrl) {
|
|
242
|
-
existing[ABAP_AUTHORIZATION_VARS.UAA_URL] = tokens.uaaUrl;
|
|
243
|
-
}
|
|
244
|
-
if (tokens.clientId) {
|
|
245
|
-
existing[ABAP_AUTHORIZATION_VARS.UAA_CLIENT_ID] = tokens.clientId;
|
|
246
|
-
}
|
|
247
|
-
if (tokens.clientSecret) {
|
|
248
|
-
existing[ABAP_AUTHORIZATION_VARS.UAA_CLIENT_SECRET] = tokens.clientSecret;
|
|
249
|
-
}
|
|
250
|
-
if (tokens.serviceUrl) {
|
|
251
|
-
// Use SAP_URL as the standard name for ABAP systems
|
|
252
|
-
existing[ABAP_CONNECTION_VARS.SERVICE_URL] = tokens.serviceUrl;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Write all values
|
|
256
|
-
for (const [key, value] of Object.entries(existing)) {
|
|
257
|
-
if (value) {
|
|
258
|
-
lines.push(`${key}=${value}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Ensure directory exists
|
|
263
|
-
const dir = path.dirname(fullPath);
|
|
264
|
-
if (!fs.existsSync(dir)) {
|
|
265
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
fs.writeFileSync(fullPath, lines.join('\n') + '\n', 'utf8');
|
|
269
|
-
}
|
|
270
|
-
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration helpers for auth-providers tests
|
|
3
|
-
* Loads test configuration from test-config.yaml
|
|
4
|
-
*/
|
|
5
|
-
export interface TestConfig {
|
|
6
|
-
destination?: string;
|
|
7
|
-
destination_dir?: string;
|
|
8
|
-
service_key_path?: string;
|
|
9
|
-
session_path?: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Load test configuration from YAML
|
|
13
|
-
* Uses test-config.yaml from tests/ directory
|
|
14
|
-
*/
|
|
15
|
-
export declare function loadTestConfig(): TestConfig;
|
|
16
|
-
/**
|
|
17
|
-
* Check if test config has real values (not placeholders)
|
|
18
|
-
*/
|
|
19
|
-
export declare function hasRealConfigValue(config?: TestConfig): boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Get destination from config
|
|
22
|
-
*/
|
|
23
|
-
export declare function getDestination(config?: TestConfig): string | null;
|
|
24
|
-
/**
|
|
25
|
-
* Get service keys directory from config
|
|
26
|
-
* Uses base_dir/service-keys or default platform path
|
|
27
|
-
*/
|
|
28
|
-
export declare function getServiceKeysDir(config?: TestConfig): string;
|
|
29
|
-
/**
|
|
30
|
-
* Get sessions directory from config
|
|
31
|
-
* Uses base_dir/sessions or default platform path
|
|
32
|
-
*/
|
|
33
|
-
export declare function getSessionsDir(config?: TestConfig): string;
|
|
34
|
-
/**
|
|
35
|
-
* Get service key file path
|
|
36
|
-
* Returns full path to service key file
|
|
37
|
-
*/
|
|
38
|
-
export declare function getServiceKeyPath(config?: TestConfig): string | null;
|
|
39
|
-
/**
|
|
40
|
-
* Get session file path
|
|
41
|
-
* Returns full path to session file
|
|
42
|
-
*/
|
|
43
|
-
export declare function getSessionPath(config?: TestConfig): string | null;
|
|
44
|
-
export declare function getAbapDestination(config?: TestConfig): string | null;
|
|
45
|
-
export declare function hasRealConfig(config?: TestConfig, _section?: 'abap' | 'xsuaa'): boolean;
|
|
46
|
-
//# sourceMappingURL=configHelpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"configHelpers.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/configHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAyD3C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAO/D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGjE;AA+BD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAa7D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAa1D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAcpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAcjE;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAErE;AAED,wBAAgB,aAAa,CAC3B,MAAM,CAAC,EAAE,UAAU,EACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAC1B,OAAO,CAET"}
|