@robinmordasiewicz/f5xc-api-mcp 1.0.82-2512312028 → 1.0.82-2601012247
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/README.md +45 -0
- package/dist/auth/credential-manager.d.ts +55 -29
- package/dist/auth/credential-manager.d.ts.map +1 -1
- package/dist/auth/credential-manager.js +178 -100
- package/dist/auth/credential-manager.js.map +1 -1
- package/dist/auth/http-client.d.ts +5 -0
- package/dist/auth/http-client.d.ts.map +1 -1
- package/dist/auth/http-client.js +63 -11
- package/dist/auth/http-client.js.map +1 -1
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +5 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/config/paths.d.ts +34 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +67 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -11
- package/dist/index.js.map +1 -1
- package/dist/profile/index.d.ts +9 -0
- package/dist/profile/index.d.ts.map +1 -0
- package/dist/profile/index.js +8 -0
- package/dist/profile/index.js.map +1 -0
- package/dist/profile/manager.d.ts +75 -0
- package/dist/profile/manager.d.ts.map +1 -0
- package/dist/profile/manager.js +327 -0
- package/dist/profile/manager.js.map +1 -0
- package/dist/profile/types.d.ts +55 -0
- package/dist/profile/types.d.ts.map +1 -0
- package/dist/profile/types.js +8 -0
- package/dist/profile/types.js.map +1 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -2
- package/dist/server.js.map +1 -1
- package/dist/utils/error-handling.d.ts +25 -0
- package/dist/utils/error-handling.d.ts.map +1 -1
- package/dist/utils/error-handling.js +127 -0
- package/dist/utils/error-handling.js.map +1 -1
- package/package.json +1 -1
- package/dist/config/config-manager.d.ts +0 -72
- package/dist/config/config-manager.d.ts.map +0 -1
- package/dist/config/config-manager.js +0 -247
- package/dist/config/config-manager.js.map +0 -1
- package/dist/config/index.d.ts +0 -7
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -7
- package/dist/config/index.js.map +0 -1
- package/dist/config/schema.d.ts +0 -74
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -75
- package/dist/config/schema.js.map +0 -1
- package/dist/config/types.d.ts +0 -77
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js +0 -31
- package/dist/config/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -91,6 +91,8 @@ Add to your MCP settings:
|
|
|
91
91
|
| `F5XC_P12_FILE` | For cert auth | Path to P12 certificate file |
|
|
92
92
|
| `F5XC_P12_PASSWORD` | For cert auth | Password for P12 certificate |
|
|
93
93
|
| `F5XC_PROFILE` | No | Profile name to use (default: `defaultProfile` from config) |
|
|
94
|
+
| `F5XC_TLS_INSECURE` | No | Disable SSL verification (staging only, set to `true`) |
|
|
95
|
+
| `F5XC_CA_BUNDLE` | No | Path to custom CA certificate bundle |
|
|
94
96
|
| `LOG_LEVEL` | No | Logging verbosity (debug, info, warn, error) |
|
|
95
97
|
|
|
96
98
|
## Profile-Based Configuration
|
|
@@ -365,6 +367,49 @@ The server automatically normalizes various URL formats:
|
|
|
365
367
|
| `tenant.console.ves.volterra.io` | `tenant.console.ves.volterra.io/api` |
|
|
366
368
|
| `https://tenant.volterra.us/` | `https://tenant.console.ves.volterra.io/api` |
|
|
367
369
|
|
|
370
|
+
## SSL/TLS Configuration
|
|
371
|
+
|
|
372
|
+
### Staging Environment Certificate Issue
|
|
373
|
+
|
|
374
|
+
F5 XC staging environments use URLs like `tenant.staging.console.ves.volterra.io`, but the SSL
|
|
375
|
+
certificate only covers `*.console.ves.volterra.io`. This causes SSL validation failures because
|
|
376
|
+
wildcards only match a single subdomain level, not two levels (`tenant.staging`).
|
|
377
|
+
|
|
378
|
+
**Error Example:**
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
Hostname/IP does not match certificate's altnames:
|
|
382
|
+
Host: tenant.staging.console.ves.volterra.io
|
|
383
|
+
Cert covers: DNS:*.console.ves.volterra.io, DNS:console.ves.volterra.io
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Solutions
|
|
387
|
+
|
|
388
|
+
#### Option 1: Custom CA Bundle (Recommended)
|
|
389
|
+
|
|
390
|
+
If your organization uses a custom CA:
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
export F5XC_CA_BUNDLE=/path/to/your/ca-bundle.crt
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
#### Option 2: Disable Verification (Development Only)
|
|
397
|
+
|
|
398
|
+
**WARNING: Never use in production!**
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
export F5XC_TLS_INSECURE=true
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Troubleshooting SSL Errors
|
|
405
|
+
|
|
406
|
+
| Error | Cause | Solution |
|
|
407
|
+
|-------|-------|----------|
|
|
408
|
+
| `Hostname/IP does not match certificate's altnames` | Staging URL mismatch | Use `F5XC_TLS_INSECURE=true` or custom CA |
|
|
409
|
+
| `self signed certificate` | Custom CA not trusted | Set `F5XC_CA_BUNDLE` |
|
|
410
|
+
| `certificate has expired` | Expired certificate | Contact F5 XC admin |
|
|
411
|
+
| `unable to verify the first certificate` | Missing intermediate CA | Add intermediates to CA bundle |
|
|
412
|
+
|
|
368
413
|
## Development
|
|
369
414
|
|
|
370
415
|
### Prerequisites
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Handles authentication configuration and URL normalization.
|
|
5
5
|
* Supports dual-mode operation:
|
|
6
6
|
* - Documentation mode: No credentials required
|
|
7
|
-
* - Execution mode: API token or P12
|
|
7
|
+
* - Execution mode: API token or P12/Certificate authentication
|
|
8
|
+
*
|
|
9
|
+
* Cross-compatible with f5xc-xcsh CLI profiles.
|
|
8
10
|
*/
|
|
9
|
-
import { ConfigManager } from "../config/index.js";
|
|
10
11
|
/**
|
|
11
12
|
* Authentication modes supported by the server
|
|
12
13
|
*/
|
|
@@ -20,13 +21,17 @@ export declare enum AuthMode {
|
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* Environment variable names for authentication
|
|
24
|
+
* These take priority over profile settings
|
|
23
25
|
*/
|
|
24
26
|
export declare const AUTH_ENV_VARS: {
|
|
25
27
|
readonly API_URL: "F5XC_API_URL";
|
|
26
28
|
readonly API_TOKEN: "F5XC_API_TOKEN";
|
|
27
|
-
readonly
|
|
28
|
-
readonly
|
|
29
|
-
readonly
|
|
29
|
+
readonly P12_BUNDLE: "F5XC_P12_BUNDLE";
|
|
30
|
+
readonly CERT: "F5XC_CERT";
|
|
31
|
+
readonly KEY: "F5XC_KEY";
|
|
32
|
+
readonly NAMESPACE: "F5XC_NAMESPACE";
|
|
33
|
+
readonly TLS_INSECURE: "F5XC_TLS_INSECURE";
|
|
34
|
+
readonly CA_BUNDLE: "F5XC_CA_BUNDLE";
|
|
30
35
|
};
|
|
31
36
|
/**
|
|
32
37
|
* Credential configuration for API access
|
|
@@ -40,8 +45,16 @@ export interface Credentials {
|
|
|
40
45
|
token: string | null;
|
|
41
46
|
/** P12 certificate buffer (for cert auth) */
|
|
42
47
|
p12Certificate: Buffer | null;
|
|
43
|
-
/**
|
|
44
|
-
|
|
48
|
+
/** Certificate content (for mTLS) */
|
|
49
|
+
cert: string | null;
|
|
50
|
+
/** Private key content (for mTLS) */
|
|
51
|
+
key: string | null;
|
|
52
|
+
/** Default namespace */
|
|
53
|
+
namespace: string | null;
|
|
54
|
+
/** Disable TLS certificate verification (staging/development only) */
|
|
55
|
+
tlsInsecure: boolean;
|
|
56
|
+
/** Custom CA bundle for TLS verification */
|
|
57
|
+
caBundle: Buffer | null;
|
|
45
58
|
}
|
|
46
59
|
/**
|
|
47
60
|
* Normalize F5XC tenant URL to standard API endpoint format
|
|
@@ -67,41 +80,37 @@ export declare function extractTenantFromUrl(url: string): string | null;
|
|
|
67
80
|
* Credential Manager
|
|
68
81
|
*
|
|
69
82
|
* Manages authentication credentials for F5 Distributed Cloud API.
|
|
70
|
-
* Supports
|
|
83
|
+
* Supports credential loading with priority:
|
|
71
84
|
* 1. Environment variables (highest priority - overrides all)
|
|
72
|
-
* 2.
|
|
85
|
+
* 2. Active profile from ~/.config/xcsh/ (cross-compatible with xcsh CLI)
|
|
73
86
|
* 3. No credentials (documentation mode - lowest priority)
|
|
74
87
|
*/
|
|
75
88
|
export declare class CredentialManager {
|
|
76
89
|
private credentials;
|
|
77
|
-
private
|
|
78
|
-
private
|
|
79
|
-
constructor(
|
|
80
|
-
/**
|
|
81
|
-
* Load credentials from environment variables
|
|
82
|
-
*/
|
|
83
|
-
private loadFromEnvironment;
|
|
90
|
+
private activeProfileName;
|
|
91
|
+
private initialized;
|
|
92
|
+
constructor();
|
|
84
93
|
/**
|
|
85
|
-
*
|
|
94
|
+
* Initialize credentials asynchronously
|
|
95
|
+
* Must be called before using credentials
|
|
86
96
|
*/
|
|
87
|
-
|
|
97
|
+
initialize(): Promise<void>;
|
|
88
98
|
/**
|
|
89
|
-
*
|
|
99
|
+
* Load credentials from environment variables
|
|
90
100
|
*/
|
|
91
|
-
private
|
|
101
|
+
private loadFromEnvironment;
|
|
92
102
|
/**
|
|
93
|
-
*
|
|
94
|
-
* Environment variables override profile settings
|
|
103
|
+
* Load credentials from active profile
|
|
95
104
|
*/
|
|
96
|
-
private
|
|
105
|
+
private loadFromProfile;
|
|
97
106
|
/**
|
|
98
|
-
* Build credentials object from
|
|
107
|
+
* Build credentials object from profile data
|
|
99
108
|
*/
|
|
100
109
|
private buildCredentials;
|
|
101
110
|
/**
|
|
102
111
|
* Load credentials with priority order:
|
|
103
112
|
* 1. Environment variables (highest)
|
|
104
|
-
* 2.
|
|
113
|
+
* 2. Active profile from ~/.config/xcsh/
|
|
105
114
|
* 3. No credentials - documentation mode (lowest)
|
|
106
115
|
*/
|
|
107
116
|
private loadCredentials;
|
|
@@ -135,17 +144,34 @@ export declare class CredentialManager {
|
|
|
135
144
|
*/
|
|
136
145
|
getP12Certificate(): Buffer | null;
|
|
137
146
|
/**
|
|
138
|
-
* Get
|
|
147
|
+
* Get certificate content (for mTLS)
|
|
148
|
+
*/
|
|
149
|
+
getCert(): string | null;
|
|
150
|
+
/**
|
|
151
|
+
* Get private key content (for mTLS)
|
|
152
|
+
*/
|
|
153
|
+
getKey(): string | null;
|
|
154
|
+
/**
|
|
155
|
+
* Get default namespace
|
|
156
|
+
*/
|
|
157
|
+
getNamespace(): string | null;
|
|
158
|
+
/**
|
|
159
|
+
* Check if TLS certificate verification is disabled
|
|
160
|
+
* WARNING: Only use for staging/development environments
|
|
161
|
+
*/
|
|
162
|
+
getTlsInsecure(): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Get custom CA bundle for TLS verification
|
|
139
165
|
*/
|
|
140
|
-
|
|
166
|
+
getCaBundle(): Buffer | null;
|
|
141
167
|
/**
|
|
142
168
|
* Get full credentials object
|
|
143
169
|
*/
|
|
144
170
|
getCredentials(): Readonly<Credentials>;
|
|
145
171
|
/**
|
|
146
|
-
* Reload credentials from environment
|
|
172
|
+
* Reload credentials from environment/profile
|
|
147
173
|
* Useful for testing or when credentials change
|
|
148
174
|
*/
|
|
149
|
-
reload(): void
|
|
175
|
+
reload(): Promise<void>;
|
|
150
176
|
}
|
|
151
177
|
//# sourceMappingURL=credential-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-manager.d.ts","sourceRoot":"","sources":["../../src/auth/credential-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"credential-manager.d.ts","sourceRoot":"","sources":["../../src/auth/credential-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;GAEG;AACH,oBAAY,QAAQ;IAClB,kDAAkD;IAClD,IAAI,SAAS;IACb,+BAA+B;IAC/B,KAAK,UAAU;IACf,4CAA4C;IAC5C,WAAW,gBAAgB;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;CAUhB,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0BAA0B;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,yBAAyB;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iCAAiC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qCAAqC;IACrC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qCAAqC;IACrC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,wBAAwB;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,WAAW,CAAS;;IAiB5B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;OAEG;YACW,eAAe;IAmB7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+FxB;;;;;OAKG;YACW,eAAe;IA4C7B;;;OAGG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC;;OAEG;IACH,WAAW,IAAI,QAAQ;IAIvB;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAIlC;;OAEG;IACH,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB;;OAEG;IACH,MAAM,IAAI,MAAM,GAAG,IAAI;IAIvB;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B;;;OAGG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACH,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B;;OAEG;IACH,cAAc,IAAI,QAAQ,CAAC,WAAW,CAAC;IAIvC;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* Handles authentication configuration and URL normalization.
|
|
5
5
|
* Supports dual-mode operation:
|
|
6
6
|
* - Documentation mode: No credentials required
|
|
7
|
-
* - Execution mode: API token or P12
|
|
7
|
+
* - Execution mode: API token or P12/Certificate authentication
|
|
8
|
+
*
|
|
9
|
+
* Cross-compatible with f5xc-xcsh CLI profiles.
|
|
8
10
|
*/
|
|
9
11
|
import { readFileSync } from "fs";
|
|
10
12
|
import { logger } from "../utils/logging.js";
|
|
11
|
-
import {
|
|
13
|
+
import { getProfileManager } from "../profile/index.js";
|
|
12
14
|
/**
|
|
13
15
|
* Authentication modes supported by the server
|
|
14
16
|
*/
|
|
@@ -23,13 +25,18 @@ export var AuthMode;
|
|
|
23
25
|
})(AuthMode || (AuthMode = {}));
|
|
24
26
|
/**
|
|
25
27
|
* Environment variable names for authentication
|
|
28
|
+
* These take priority over profile settings
|
|
26
29
|
*/
|
|
27
30
|
export const AUTH_ENV_VARS = {
|
|
28
31
|
API_URL: "F5XC_API_URL",
|
|
29
32
|
API_TOKEN: "F5XC_API_TOKEN",
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
P12_BUNDLE: "F5XC_P12_BUNDLE",
|
|
34
|
+
CERT: "F5XC_CERT",
|
|
35
|
+
KEY: "F5XC_KEY",
|
|
36
|
+
NAMESPACE: "F5XC_NAMESPACE",
|
|
37
|
+
// TLS configuration
|
|
38
|
+
TLS_INSECURE: "F5XC_TLS_INSECURE",
|
|
39
|
+
CA_BUNDLE: "F5XC_CA_BUNDLE",
|
|
33
40
|
};
|
|
34
41
|
/**
|
|
35
42
|
* URL normalization patterns
|
|
@@ -88,118 +95,132 @@ export function extractTenantFromUrl(url) {
|
|
|
88
95
|
* Credential Manager
|
|
89
96
|
*
|
|
90
97
|
* Manages authentication credentials for F5 Distributed Cloud API.
|
|
91
|
-
* Supports
|
|
98
|
+
* Supports credential loading with priority:
|
|
92
99
|
* 1. Environment variables (highest priority - overrides all)
|
|
93
|
-
* 2.
|
|
100
|
+
* 2. Active profile from ~/.config/xcsh/ (cross-compatible with xcsh CLI)
|
|
94
101
|
* 3. No credentials (documentation mode - lowest priority)
|
|
95
102
|
*/
|
|
96
103
|
export class CredentialManager {
|
|
97
104
|
credentials;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
constructor(
|
|
101
|
-
|
|
102
|
-
this.credentials =
|
|
105
|
+
activeProfileName = null;
|
|
106
|
+
initialized = false;
|
|
107
|
+
constructor() {
|
|
108
|
+
// Initialize with empty credentials - will be loaded async
|
|
109
|
+
this.credentials = {
|
|
110
|
+
mode: AuthMode.NONE,
|
|
111
|
+
apiUrl: null,
|
|
112
|
+
token: null,
|
|
113
|
+
p12Certificate: null,
|
|
114
|
+
cert: null,
|
|
115
|
+
key: null,
|
|
116
|
+
namespace: null,
|
|
117
|
+
tlsInsecure: false,
|
|
118
|
+
caBundle: null,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Initialize credentials asynchronously
|
|
123
|
+
* Must be called before using credentials
|
|
124
|
+
*/
|
|
125
|
+
async initialize() {
|
|
126
|
+
if (this.initialized)
|
|
127
|
+
return;
|
|
128
|
+
this.credentials = await this.loadCredentials();
|
|
129
|
+
this.initialized = true;
|
|
103
130
|
}
|
|
104
131
|
/**
|
|
105
132
|
* Load credentials from environment variables
|
|
106
133
|
*/
|
|
107
134
|
loadFromEnvironment() {
|
|
135
|
+
const apiUrl = process.env[AUTH_ENV_VARS.API_URL];
|
|
136
|
+
const apiToken = process.env[AUTH_ENV_VARS.API_TOKEN];
|
|
137
|
+
const p12Bundle = process.env[AUTH_ENV_VARS.P12_BUNDLE];
|
|
138
|
+
const cert = process.env[AUTH_ENV_VARS.CERT];
|
|
139
|
+
const key = process.env[AUTH_ENV_VARS.KEY];
|
|
140
|
+
const defaultNamespace = process.env[AUTH_ENV_VARS.NAMESPACE];
|
|
141
|
+
const tlsInsecure = process.env[AUTH_ENV_VARS.TLS_INSECURE]?.toLowerCase() === "true";
|
|
142
|
+
const caBundle = process.env[AUTH_ENV_VARS.CA_BUNDLE];
|
|
143
|
+
const hasAuth = !!(apiToken || p12Bundle || (cert && key));
|
|
108
144
|
return {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
145
|
+
name: "__env__",
|
|
146
|
+
apiUrl: apiUrl || "",
|
|
147
|
+
apiToken,
|
|
148
|
+
p12Bundle,
|
|
149
|
+
cert,
|
|
150
|
+
key,
|
|
151
|
+
defaultNamespace,
|
|
152
|
+
hasAuth,
|
|
153
|
+
tlsInsecure,
|
|
154
|
+
caBundle,
|
|
113
155
|
};
|
|
114
156
|
}
|
|
115
157
|
/**
|
|
116
|
-
* Load credentials from
|
|
158
|
+
* Load credentials from active profile
|
|
117
159
|
*/
|
|
118
|
-
|
|
160
|
+
async loadFromProfile() {
|
|
119
161
|
try {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!profileName) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
const profile = config.profiles[profileName];
|
|
129
|
-
if (!profile) {
|
|
130
|
-
return null;
|
|
162
|
+
const profileManager = getProfileManager();
|
|
163
|
+
const profile = await profileManager.getActiveProfile();
|
|
164
|
+
if (profile) {
|
|
165
|
+
this.activeProfileName = profile.name;
|
|
166
|
+
return profile;
|
|
131
167
|
}
|
|
132
|
-
|
|
133
|
-
// Touch the profile to update lastUsedAt (async, but don't block)
|
|
134
|
-
this.configManager.touchProfile(profileName).catch(() => {
|
|
135
|
-
// Silently ignore touch errors
|
|
136
|
-
});
|
|
137
|
-
return {
|
|
138
|
-
apiUrl: profile.apiUrl,
|
|
139
|
-
token: profile.apiToken,
|
|
140
|
-
p12File: profile.p12File,
|
|
141
|
-
p12Password: profile.p12Password,
|
|
142
|
-
};
|
|
168
|
+
return null;
|
|
143
169
|
}
|
|
144
170
|
catch (error) {
|
|
145
|
-
logger.debug("Failed to load credentials from
|
|
171
|
+
logger.debug("Failed to load credentials from profile", {
|
|
146
172
|
error: error instanceof Error ? error.message : String(error),
|
|
147
173
|
});
|
|
148
174
|
return null;
|
|
149
175
|
}
|
|
150
176
|
}
|
|
151
177
|
/**
|
|
152
|
-
*
|
|
178
|
+
* Build credentials object from profile data
|
|
153
179
|
*/
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const envProfile = process.env[AUTH_ENV_VARS.PROFILE];
|
|
157
|
-
if (envProfile && config.profiles[envProfile]) {
|
|
158
|
-
return envProfile;
|
|
159
|
-
}
|
|
160
|
-
// Fall back to default profile if set
|
|
161
|
-
return config.defaultProfile ?? null;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Merge raw credentials from environment and config
|
|
165
|
-
* Environment variables override profile settings
|
|
166
|
-
*/
|
|
167
|
-
mergeCredentials(envCreds, profileCreds) {
|
|
168
|
-
return {
|
|
169
|
-
apiUrl: envCreds.apiUrl ?? profileCreds.apiUrl,
|
|
170
|
-
token: envCreds.token ?? profileCreds.token,
|
|
171
|
-
p12File: envCreds.p12File ?? profileCreds.p12File,
|
|
172
|
-
p12Password: envCreds.p12Password ?? profileCreds.p12Password,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Build credentials object from raw credentials
|
|
177
|
-
*/
|
|
178
|
-
buildCredentials(rawCreds) {
|
|
179
|
-
const apiUrl = rawCreds.apiUrl;
|
|
180
|
-
const token = rawCreds.token;
|
|
181
|
-
const p12File = rawCreds.p12File;
|
|
182
|
-
const p12Password = rawCreds.p12Password;
|
|
180
|
+
buildCredentials(profile) {
|
|
181
|
+
const apiUrl = profile.apiUrl;
|
|
183
182
|
// Determine authentication mode
|
|
184
183
|
let mode = AuthMode.NONE;
|
|
185
184
|
let normalizedUrl = null;
|
|
186
185
|
let p12Certificate = null;
|
|
186
|
+
let cert = null;
|
|
187
|
+
let key = null;
|
|
188
|
+
// TLS configuration
|
|
189
|
+
const tlsInsecure = profile.tlsInsecure ?? false;
|
|
190
|
+
let caBundle = null;
|
|
191
|
+
// Load CA bundle if specified
|
|
192
|
+
if (profile.caBundle) {
|
|
193
|
+
try {
|
|
194
|
+
caBundle = readFileSync(profile.caBundle);
|
|
195
|
+
logger.info("Loaded CA bundle", { file: profile.caBundle });
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
logger.warn("Failed to load CA bundle", {
|
|
199
|
+
file: profile.caBundle,
|
|
200
|
+
error: error instanceof Error ? error.message : String(error),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Log TLS insecure mode warning
|
|
205
|
+
if (tlsInsecure) {
|
|
206
|
+
logger.warn("TLS certificate verification is DISABLED. This is insecure and should only be used for staging/development environments.");
|
|
207
|
+
}
|
|
187
208
|
if (apiUrl) {
|
|
188
209
|
normalizedUrl = normalizeApiUrl(apiUrl);
|
|
189
|
-
if (
|
|
190
|
-
//
|
|
210
|
+
if (profile.p12Bundle) {
|
|
211
|
+
// P12 certificate authentication
|
|
191
212
|
mode = AuthMode.CERTIFICATE;
|
|
192
213
|
try {
|
|
193
|
-
p12Certificate = readFileSync(
|
|
194
|
-
logger.info("Loaded P12 certificate", { file:
|
|
214
|
+
p12Certificate = readFileSync(profile.p12Bundle);
|
|
215
|
+
logger.info("Loaded P12 certificate", { file: profile.p12Bundle });
|
|
195
216
|
}
|
|
196
217
|
catch (error) {
|
|
197
218
|
logger.error("Failed to load P12 certificate", {
|
|
198
|
-
file:
|
|
219
|
+
file: profile.p12Bundle,
|
|
199
220
|
error: error instanceof Error ? error.message : String(error),
|
|
200
221
|
});
|
|
201
222
|
// Fall back to token auth if certificate load fails
|
|
202
|
-
if (
|
|
223
|
+
if (profile.apiToken) {
|
|
203
224
|
mode = AuthMode.TOKEN;
|
|
204
225
|
logger.info("Falling back to token authentication");
|
|
205
226
|
}
|
|
@@ -208,48 +229,74 @@ export class CredentialManager {
|
|
|
208
229
|
}
|
|
209
230
|
}
|
|
210
231
|
}
|
|
211
|
-
else if (
|
|
232
|
+
else if (profile.cert && profile.key) {
|
|
233
|
+
// Certificate + key authentication
|
|
234
|
+
mode = AuthMode.CERTIFICATE;
|
|
235
|
+
try {
|
|
236
|
+
cert = readFileSync(profile.cert, "utf-8");
|
|
237
|
+
key = readFileSync(profile.key, "utf-8");
|
|
238
|
+
logger.info("Loaded certificate and key", {
|
|
239
|
+
cert: profile.cert,
|
|
240
|
+
key: profile.key,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
logger.error("Failed to load certificate/key", {
|
|
245
|
+
error: error instanceof Error ? error.message : String(error),
|
|
246
|
+
});
|
|
247
|
+
if (profile.apiToken) {
|
|
248
|
+
mode = AuthMode.TOKEN;
|
|
249
|
+
logger.info("Falling back to token authentication");
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
mode = AuthMode.NONE;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else if (profile.apiToken) {
|
|
212
257
|
mode = AuthMode.TOKEN;
|
|
213
258
|
}
|
|
214
259
|
}
|
|
215
260
|
return {
|
|
216
261
|
mode,
|
|
217
262
|
apiUrl: normalizedUrl,
|
|
218
|
-
token:
|
|
263
|
+
token: profile.apiToken ?? null,
|
|
219
264
|
p12Certificate,
|
|
220
|
-
|
|
265
|
+
cert,
|
|
266
|
+
key,
|
|
267
|
+
namespace: profile.defaultNamespace ?? null,
|
|
268
|
+
tlsInsecure,
|
|
269
|
+
caBundle,
|
|
221
270
|
};
|
|
222
271
|
}
|
|
223
272
|
/**
|
|
224
273
|
* Load credentials with priority order:
|
|
225
274
|
* 1. Environment variables (highest)
|
|
226
|
-
* 2.
|
|
275
|
+
* 2. Active profile from ~/.config/xcsh/
|
|
227
276
|
* 3. No credentials - documentation mode (lowest)
|
|
228
277
|
*/
|
|
229
|
-
loadCredentials() {
|
|
230
|
-
// Step 1:
|
|
278
|
+
async loadCredentials() {
|
|
279
|
+
// Step 1: Check environment variables first (highest priority)
|
|
231
280
|
const envCreds = this.loadFromEnvironment();
|
|
232
|
-
if (envCreds.apiUrl &&
|
|
281
|
+
if (envCreds.apiUrl && envCreds.hasAuth) {
|
|
233
282
|
const credentials = this.buildCredentials(envCreds);
|
|
234
283
|
const tenant = credentials.apiUrl ? extractTenantFromUrl(credentials.apiUrl) : null;
|
|
235
284
|
logger.info("Credentials loaded from environment variables", {
|
|
236
285
|
mode: credentials.mode,
|
|
237
286
|
tenant,
|
|
238
|
-
profile: this.activeProfile,
|
|
239
287
|
});
|
|
240
288
|
return credentials;
|
|
241
289
|
}
|
|
242
|
-
// Step 2: Try
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
245
|
-
const
|
|
246
|
-
const credentials = this.buildCredentials(merged);
|
|
290
|
+
// Step 2: Try active profile from ~/.config/xcsh/
|
|
291
|
+
const profile = await this.loadFromProfile();
|
|
292
|
+
if (profile) {
|
|
293
|
+
const credentials = this.buildCredentials(profile);
|
|
247
294
|
if (credentials.mode !== AuthMode.NONE) {
|
|
248
295
|
const tenant = credentials.apiUrl ? extractTenantFromUrl(credentials.apiUrl) : null;
|
|
249
|
-
logger.info("Credentials loaded from
|
|
296
|
+
logger.info("Credentials loaded from profile", {
|
|
250
297
|
mode: credentials.mode,
|
|
251
298
|
tenant,
|
|
252
|
-
profile: this.
|
|
299
|
+
profile: this.activeProfileName,
|
|
253
300
|
});
|
|
254
301
|
return credentials;
|
|
255
302
|
}
|
|
@@ -261,7 +308,11 @@ export class CredentialManager {
|
|
|
261
308
|
apiUrl: null,
|
|
262
309
|
token: null,
|
|
263
310
|
p12Certificate: null,
|
|
264
|
-
|
|
311
|
+
cert: null,
|
|
312
|
+
key: null,
|
|
313
|
+
namespace: null,
|
|
314
|
+
tlsInsecure: false,
|
|
315
|
+
caBundle: null,
|
|
265
316
|
};
|
|
266
317
|
}
|
|
267
318
|
/**
|
|
@@ -269,7 +320,7 @@ export class CredentialManager {
|
|
|
269
320
|
* Returns null if credentials are from environment variables or no profile is active
|
|
270
321
|
*/
|
|
271
322
|
getActiveProfile() {
|
|
272
|
-
return this.
|
|
323
|
+
return this.activeProfileName;
|
|
273
324
|
}
|
|
274
325
|
/**
|
|
275
326
|
* Get the current authentication mode
|
|
@@ -308,10 +359,35 @@ export class CredentialManager {
|
|
|
308
359
|
return this.credentials.p12Certificate;
|
|
309
360
|
}
|
|
310
361
|
/**
|
|
311
|
-
* Get
|
|
362
|
+
* Get certificate content (for mTLS)
|
|
363
|
+
*/
|
|
364
|
+
getCert() {
|
|
365
|
+
return this.credentials.cert;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Get private key content (for mTLS)
|
|
369
|
+
*/
|
|
370
|
+
getKey() {
|
|
371
|
+
return this.credentials.key;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Get default namespace
|
|
375
|
+
*/
|
|
376
|
+
getNamespace() {
|
|
377
|
+
return this.credentials.namespace;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Check if TLS certificate verification is disabled
|
|
381
|
+
* WARNING: Only use for staging/development environments
|
|
382
|
+
*/
|
|
383
|
+
getTlsInsecure() {
|
|
384
|
+
return this.credentials.tlsInsecure;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get custom CA bundle for TLS verification
|
|
312
388
|
*/
|
|
313
|
-
|
|
314
|
-
return this.credentials.
|
|
389
|
+
getCaBundle() {
|
|
390
|
+
return this.credentials.caBundle;
|
|
315
391
|
}
|
|
316
392
|
/**
|
|
317
393
|
* Get full credentials object
|
|
@@ -320,11 +396,13 @@ export class CredentialManager {
|
|
|
320
396
|
return Object.freeze({ ...this.credentials });
|
|
321
397
|
}
|
|
322
398
|
/**
|
|
323
|
-
* Reload credentials from environment
|
|
399
|
+
* Reload credentials from environment/profile
|
|
324
400
|
* Useful for testing or when credentials change
|
|
325
401
|
*/
|
|
326
|
-
reload() {
|
|
327
|
-
this.
|
|
402
|
+
async reload() {
|
|
403
|
+
this.initialized = false;
|
|
404
|
+
this.activeProfileName = null;
|
|
405
|
+
await this.initialize();
|
|
328
406
|
}
|
|
329
407
|
}
|
|
330
408
|
//# sourceMappingURL=credential-manager.js.map
|