@qnsp/tenant-sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +2 -2
- package/dist/index.d.ts +106 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +223 -7
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/index.test.ts +5 -0
- package/src/index.ts +377 -8
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qnsp/tenant-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -23,16 +23,16 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@opentelemetry/api": "^1.9.0",
|
|
26
|
-
"@opentelemetry/exporter-metrics-otlp-http": "^0.
|
|
27
|
-
"@opentelemetry/resources": "^2.
|
|
28
|
-
"@opentelemetry/sdk-metrics": "^2.
|
|
29
|
-
"undici": "^7.
|
|
30
|
-
"zod": "^4.
|
|
26
|
+
"@opentelemetry/exporter-metrics-otlp-http": "^0.210.0",
|
|
27
|
+
"@opentelemetry/resources": "^2.4.0",
|
|
28
|
+
"@opentelemetry/sdk-metrics": "^2.4.0",
|
|
29
|
+
"undici": "^7.18.2",
|
|
30
|
+
"zod": "^4.3.5"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "^
|
|
33
|
+
"@types/node": "^25.0.9",
|
|
34
34
|
"tsx": "^4.21.0",
|
|
35
|
-
"vitest": "4.0.
|
|
35
|
+
"vitest": "4.0.17"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": "24.12.0"
|
package/src/index.test.ts
CHANGED
|
@@ -13,6 +13,7 @@ describe("TenantClient Security Tests", () => {
|
|
|
13
13
|
expect(() => {
|
|
14
14
|
new TenantClient({
|
|
15
15
|
baseUrl: "http://example.com",
|
|
16
|
+
apiKey: "test-api-key",
|
|
16
17
|
});
|
|
17
18
|
}).toThrow("baseUrl must use HTTPS in production");
|
|
18
19
|
|
|
@@ -26,6 +27,7 @@ describe("TenantClient Security Tests", () => {
|
|
|
26
27
|
expect(() => {
|
|
27
28
|
new TenantClient({
|
|
28
29
|
baseUrl: "http://localhost:3000",
|
|
30
|
+
apiKey: "test-api-key",
|
|
29
31
|
});
|
|
30
32
|
}).not.toThrow();
|
|
31
33
|
|
|
@@ -36,6 +38,7 @@ describe("TenantClient Security Tests", () => {
|
|
|
36
38
|
describe("Input Validation", () => {
|
|
37
39
|
const client = new TenantClient({
|
|
38
40
|
baseUrl: "https://api.example.com",
|
|
41
|
+
apiKey: "test-api-key",
|
|
39
42
|
});
|
|
40
43
|
|
|
41
44
|
it("should reject invalid UUIDs", async () => {
|
|
@@ -58,6 +61,7 @@ describe("TenantClient Security Tests", () => {
|
|
|
58
61
|
describe("Error Message Sanitization", () => {
|
|
59
62
|
const client = new TenantClient({
|
|
60
63
|
baseUrl: "https://api.example.com",
|
|
64
|
+
apiKey: "test-api-key",
|
|
61
65
|
});
|
|
62
66
|
|
|
63
67
|
it("should not expose sensitive data in error messages", async () => {
|
|
@@ -98,6 +102,7 @@ describe("TenantClient Security Tests", () => {
|
|
|
98
102
|
describe("Rate Limiting", () => {
|
|
99
103
|
const client = new TenantClient({
|
|
100
104
|
baseUrl: "https://api.example.com",
|
|
105
|
+
apiKey: "test-api-key",
|
|
101
106
|
maxRetries: 2,
|
|
102
107
|
retryDelayMs: 10,
|
|
103
108
|
});
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { validateUUID } from "./validation.js";
|
|
|
17
17
|
|
|
18
18
|
export interface TenantClientConfig {
|
|
19
19
|
readonly baseUrl: string;
|
|
20
|
-
readonly apiKey
|
|
20
|
+
readonly apiKey: string;
|
|
21
21
|
readonly timeoutMs?: number;
|
|
22
22
|
readonly telemetry?: TenantClientTelemetry | TenantClientTelemetryConfig;
|
|
23
23
|
readonly maxRetries?: number;
|
|
@@ -34,12 +34,102 @@ type InternalTenantClientConfig = {
|
|
|
34
34
|
|
|
35
35
|
export type TenantStatus = "active" | "suspended" | "pending" | "deleted";
|
|
36
36
|
|
|
37
|
+
export type HsmMode = "none" | "supported" | "required";
|
|
38
|
+
|
|
37
39
|
/**
|
|
38
40
|
* Crypto policy tier determines which PQC algorithms are allowed.
|
|
39
41
|
* Maps to tenant_crypto_policies.policy_tier in tenant-service.
|
|
40
42
|
*/
|
|
41
43
|
export type CryptoPolicyTier = "default" | "strict" | "maximum" | "government";
|
|
42
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Crypto policy v1 uses profiles + tiers (evidence-first model).
|
|
47
|
+
*/
|
|
48
|
+
export type QnspTier =
|
|
49
|
+
| "TIER0_LEGACY"
|
|
50
|
+
| "TIER1_APPROVED"
|
|
51
|
+
| "TIER2_HIGH_ASSURANCE"
|
|
52
|
+
| "TIER3_DIVERSITY"
|
|
53
|
+
| "TIER4_EXPERIMENTAL";
|
|
54
|
+
|
|
55
|
+
export type TenantType =
|
|
56
|
+
| "FREE_FOREVER"
|
|
57
|
+
| "DEV_STARTER"
|
|
58
|
+
| "DEV_PRO"
|
|
59
|
+
| "DEV_ELITE"
|
|
60
|
+
| "BUSINESS_TEAM"
|
|
61
|
+
| "BUSINESS_ADVANCED"
|
|
62
|
+
| "BUSINESS_ELITE"
|
|
63
|
+
| "ENTERPRISE_STANDARD"
|
|
64
|
+
| "ENTERPRISE_PRO"
|
|
65
|
+
| "ENTERPRISE_ELITE"
|
|
66
|
+
| "PUBLIC_SECTOR";
|
|
67
|
+
|
|
68
|
+
export type CryptoProfileId =
|
|
69
|
+
| "gov-high-assurance"
|
|
70
|
+
| "defense-long-life-data"
|
|
71
|
+
| "financial-hybrid-pqc"
|
|
72
|
+
| "research-eval";
|
|
73
|
+
|
|
74
|
+
export type TenantCryptoPolicyV1Action = "CREATE" | "UPDATE" | "ROLLBACK";
|
|
75
|
+
|
|
76
|
+
export interface CryptoPolicyV1 {
|
|
77
|
+
readonly version: "v1";
|
|
78
|
+
readonly tenantType: TenantType;
|
|
79
|
+
readonly profile: CryptoProfileId;
|
|
80
|
+
readonly enabledTiers: readonly QnspTier[];
|
|
81
|
+
readonly tier0Expiry?: string;
|
|
82
|
+
readonly tier4Acknowledgement?: {
|
|
83
|
+
readonly nonCompliant: true;
|
|
84
|
+
readonly noProductionSecrets: true;
|
|
85
|
+
readonly approvedBy: string;
|
|
86
|
+
readonly approvedAt: string;
|
|
87
|
+
};
|
|
88
|
+
readonly overrides?: {
|
|
89
|
+
readonly allowFalcon?: boolean;
|
|
90
|
+
};
|
|
91
|
+
readonly requirements: {
|
|
92
|
+
readonly fipsAligned: boolean;
|
|
93
|
+
readonly evidenceRequired: boolean;
|
|
94
|
+
readonly cryptoAgilityMetadataRequired: boolean;
|
|
95
|
+
readonly statefulLifecycleGuards: boolean;
|
|
96
|
+
readonly downgradeDetection: boolean;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface TenantCryptoPolicyV1Record {
|
|
101
|
+
readonly id: string;
|
|
102
|
+
readonly tenantId: string;
|
|
103
|
+
readonly version: "v1";
|
|
104
|
+
readonly policy: CryptoPolicyV1;
|
|
105
|
+
readonly policyHash: string;
|
|
106
|
+
readonly etag: string;
|
|
107
|
+
readonly createdAt: string;
|
|
108
|
+
readonly createdByPrincipal: string;
|
|
109
|
+
readonly createdByIp: string | null;
|
|
110
|
+
readonly updatedAt: string;
|
|
111
|
+
readonly updatedByPrincipal: string;
|
|
112
|
+
readonly updatedByIp: string | null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface TenantCryptoPolicyV1HistoryRecord {
|
|
116
|
+
readonly id: string;
|
|
117
|
+
readonly tenantId: string;
|
|
118
|
+
readonly version: "v1";
|
|
119
|
+
readonly policy: CryptoPolicyV1;
|
|
120
|
+
readonly policyHash: string;
|
|
121
|
+
readonly action: TenantCryptoPolicyV1Action;
|
|
122
|
+
readonly reason: string | null;
|
|
123
|
+
readonly changedAt: string;
|
|
124
|
+
readonly changedByPrincipal: string;
|
|
125
|
+
readonly changedByIp: string | null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface TenantCryptoPolicyV1HistoryResponse {
|
|
129
|
+
readonly tenantId: string;
|
|
130
|
+
readonly items: readonly TenantCryptoPolicyV1HistoryRecord[];
|
|
131
|
+
}
|
|
132
|
+
|
|
43
133
|
/**
|
|
44
134
|
* Tenant crypto policy configuration.
|
|
45
135
|
*/
|
|
@@ -109,19 +199,116 @@ export const CRYPTO_POLICY_ALGORITHMS: Record<CryptoPolicyTier, TierAlgorithmCon
|
|
|
109
199
|
};
|
|
110
200
|
|
|
111
201
|
/**
|
|
112
|
-
* Mapping from internal algorithm names to NIST
|
|
202
|
+
* Mapping from internal algorithm names to NIST/standards display names.
|
|
203
|
+
* Covers all 90 PQC algorithms supported by QNSP.
|
|
204
|
+
* Canonical source: @qnsp/cryptography pqc-standards.ts ALGORITHM_NIST_NAMES
|
|
113
205
|
*/
|
|
114
206
|
export const ALGORITHM_TO_NIST: Record<string, string> = {
|
|
207
|
+
// FIPS 203 — ML-KEM
|
|
115
208
|
"kyber-512": "ML-KEM-512",
|
|
116
209
|
"kyber-768": "ML-KEM-768",
|
|
117
210
|
"kyber-1024": "ML-KEM-1024",
|
|
211
|
+
// FIPS 204 — ML-DSA
|
|
118
212
|
"dilithium-2": "ML-DSA-44",
|
|
119
213
|
"dilithium-3": "ML-DSA-65",
|
|
120
214
|
"dilithium-5": "ML-DSA-87",
|
|
121
|
-
|
|
122
|
-
"
|
|
215
|
+
// FIPS 205 — SLH-DSA (SHA-2 variants)
|
|
216
|
+
"sphincs-sha2-128f-simple": "SLH-DSA-SHA2-128f",
|
|
217
|
+
"sphincs-sha2-128s-simple": "SLH-DSA-SHA2-128s",
|
|
218
|
+
"sphincs-sha2-192f-simple": "SLH-DSA-SHA2-192f",
|
|
219
|
+
"sphincs-sha2-192s-simple": "SLH-DSA-SHA2-192s",
|
|
220
|
+
"sphincs-sha2-256f-simple": "SLH-DSA-SHA2-256f",
|
|
221
|
+
"sphincs-sha2-256s-simple": "SLH-DSA-SHA2-256s",
|
|
222
|
+
// FIPS 205 — SLH-DSA (SHAKE variants)
|
|
123
223
|
"sphincs-shake-128f-simple": "SLH-DSA-SHAKE-128f",
|
|
224
|
+
"sphincs-shake-128s-simple": "SLH-DSA-SHAKE-128s",
|
|
225
|
+
"sphincs-shake-192f-simple": "SLH-DSA-SHAKE-192f",
|
|
226
|
+
"sphincs-shake-192s-simple": "SLH-DSA-SHAKE-192s",
|
|
124
227
|
"sphincs-shake-256f-simple": "SLH-DSA-SHAKE-256f",
|
|
228
|
+
"sphincs-shake-256s-simple": "SLH-DSA-SHAKE-256s",
|
|
229
|
+
// FN-DSA (FIPS 206 draft)
|
|
230
|
+
"falcon-512": "FN-DSA-512",
|
|
231
|
+
"falcon-1024": "FN-DSA-1024",
|
|
232
|
+
// HQC (NIST selected March 2025)
|
|
233
|
+
"hqc-128": "HQC-128",
|
|
234
|
+
"hqc-192": "HQC-192",
|
|
235
|
+
"hqc-256": "HQC-256",
|
|
236
|
+
// BIKE (NIST Round 4)
|
|
237
|
+
"bike-l1": "BIKE-L1",
|
|
238
|
+
"bike-l3": "BIKE-L3",
|
|
239
|
+
"bike-l5": "BIKE-L5",
|
|
240
|
+
// Classic McEliece (ISO standard)
|
|
241
|
+
"mceliece-348864": "Classic-McEliece-348864",
|
|
242
|
+
"mceliece-460896": "Classic-McEliece-460896",
|
|
243
|
+
"mceliece-6688128": "Classic-McEliece-6688128",
|
|
244
|
+
"mceliece-6960119": "Classic-McEliece-6960119",
|
|
245
|
+
"mceliece-8192128": "Classic-McEliece-8192128",
|
|
246
|
+
// FrodoKEM (ISO standard)
|
|
247
|
+
"frodokem-640-aes": "FrodoKEM-640-AES",
|
|
248
|
+
"frodokem-640-shake": "FrodoKEM-640-SHAKE",
|
|
249
|
+
"frodokem-976-aes": "FrodoKEM-976-AES",
|
|
250
|
+
"frodokem-976-shake": "FrodoKEM-976-SHAKE",
|
|
251
|
+
"frodokem-1344-aes": "FrodoKEM-1344-AES",
|
|
252
|
+
"frodokem-1344-shake": "FrodoKEM-1344-SHAKE",
|
|
253
|
+
// NTRU (lattice-based, re-added in liboqs 0.15)
|
|
254
|
+
"ntru-hps-2048-509": "NTRU-HPS-2048-509",
|
|
255
|
+
"ntru-hps-2048-677": "NTRU-HPS-2048-677",
|
|
256
|
+
"ntru-hps-4096-821": "NTRU-HPS-4096-821",
|
|
257
|
+
"ntru-hps-4096-1229": "NTRU-HPS-4096-1229",
|
|
258
|
+
"ntru-hrss-701": "NTRU-HRSS-701",
|
|
259
|
+
"ntru-hrss-1373": "NTRU-HRSS-1373",
|
|
260
|
+
// NTRU-Prime
|
|
261
|
+
sntrup761: "sntrup761",
|
|
262
|
+
// MAYO (NIST Additional Signatures Round 2)
|
|
263
|
+
"mayo-1": "MAYO-1",
|
|
264
|
+
"mayo-2": "MAYO-2",
|
|
265
|
+
"mayo-3": "MAYO-3",
|
|
266
|
+
"mayo-5": "MAYO-5",
|
|
267
|
+
// CROSS (NIST Additional Signatures Round 2)
|
|
268
|
+
"cross-rsdp-128-balanced": "CROSS-RSDP-128-balanced",
|
|
269
|
+
"cross-rsdp-128-fast": "CROSS-RSDP-128-fast",
|
|
270
|
+
"cross-rsdp-128-small": "CROSS-RSDP-128-small",
|
|
271
|
+
"cross-rsdp-192-balanced": "CROSS-RSDP-192-balanced",
|
|
272
|
+
"cross-rsdp-192-fast": "CROSS-RSDP-192-fast",
|
|
273
|
+
"cross-rsdp-192-small": "CROSS-RSDP-192-small",
|
|
274
|
+
"cross-rsdp-256-balanced": "CROSS-RSDP-256-balanced",
|
|
275
|
+
"cross-rsdp-256-fast": "CROSS-RSDP-256-fast",
|
|
276
|
+
"cross-rsdp-256-small": "CROSS-RSDP-256-small",
|
|
277
|
+
"cross-rsdpg-128-balanced": "CROSS-RSDPG-128-balanced",
|
|
278
|
+
"cross-rsdpg-128-fast": "CROSS-RSDPG-128-fast",
|
|
279
|
+
"cross-rsdpg-128-small": "CROSS-RSDPG-128-small",
|
|
280
|
+
"cross-rsdpg-192-balanced": "CROSS-RSDPG-192-balanced",
|
|
281
|
+
"cross-rsdpg-192-fast": "CROSS-RSDPG-192-fast",
|
|
282
|
+
"cross-rsdpg-192-small": "CROSS-RSDPG-192-small",
|
|
283
|
+
"cross-rsdpg-256-balanced": "CROSS-RSDPG-256-balanced",
|
|
284
|
+
"cross-rsdpg-256-fast": "CROSS-RSDPG-256-fast",
|
|
285
|
+
"cross-rsdpg-256-small": "CROSS-RSDPG-256-small",
|
|
286
|
+
// UOV (NIST Additional Signatures Round 2)
|
|
287
|
+
"ov-Is": "UOV-Is",
|
|
288
|
+
"ov-Ip": "UOV-Ip",
|
|
289
|
+
"ov-III": "UOV-III",
|
|
290
|
+
"ov-V": "UOV-V",
|
|
291
|
+
"ov-Is-pkc": "UOV-Is-pkc",
|
|
292
|
+
"ov-Ip-pkc": "UOV-Ip-pkc",
|
|
293
|
+
"ov-III-pkc": "UOV-III-pkc",
|
|
294
|
+
"ov-V-pkc": "UOV-V-pkc",
|
|
295
|
+
"ov-Is-pkc-skc": "UOV-Is-pkc-skc",
|
|
296
|
+
"ov-Ip-pkc-skc": "UOV-Ip-pkc-skc",
|
|
297
|
+
"ov-III-pkc-skc": "UOV-III-pkc-skc",
|
|
298
|
+
"ov-V-pkc-skc": "UOV-V-pkc-skc",
|
|
299
|
+
// SNOVA (NIST Additional Signatures Round 2, liboqs 0.14+)
|
|
300
|
+
"snova-24-5-4": "SNOVA-24-5-4",
|
|
301
|
+
"snova-24-5-4-shake": "SNOVA-24-5-4-SHAKE",
|
|
302
|
+
"snova-24-5-4-esk": "SNOVA-24-5-4-ESK",
|
|
303
|
+
"snova-24-5-4-shake-esk": "SNOVA-24-5-4-SHAKE-ESK",
|
|
304
|
+
"snova-25-8-3": "SNOVA-25-8-3",
|
|
305
|
+
"snova-37-17-2": "SNOVA-37-17-2",
|
|
306
|
+
"snova-37-8-4": "SNOVA-37-8-4",
|
|
307
|
+
"snova-24-5-5": "SNOVA-24-5-5",
|
|
308
|
+
"snova-56-25-2": "SNOVA-56-25-2",
|
|
309
|
+
"snova-49-11-3": "SNOVA-49-11-3",
|
|
310
|
+
"snova-60-10-4": "SNOVA-60-10-4",
|
|
311
|
+
"snova-29-6-5": "SNOVA-29-6-5",
|
|
125
312
|
};
|
|
126
313
|
|
|
127
314
|
/**
|
|
@@ -174,6 +361,7 @@ export interface Tenant {
|
|
|
174
361
|
readonly plan: string;
|
|
175
362
|
readonly region: string;
|
|
176
363
|
readonly complianceTags: readonly string[];
|
|
364
|
+
readonly hsmMode: HsmMode;
|
|
177
365
|
readonly metadata: Record<string, unknown>;
|
|
178
366
|
readonly security: TenantSecurityEnvelope;
|
|
179
367
|
readonly domains: readonly TenantDomain[];
|
|
@@ -187,6 +375,7 @@ export interface CreateTenantRequest {
|
|
|
187
375
|
readonly plan?: string;
|
|
188
376
|
readonly region?: string;
|
|
189
377
|
readonly complianceTags?: readonly string[];
|
|
378
|
+
readonly hsmMode?: HsmMode;
|
|
190
379
|
readonly metadata?: Record<string, unknown>;
|
|
191
380
|
readonly domains?: readonly {
|
|
192
381
|
readonly domain: string;
|
|
@@ -200,6 +389,7 @@ export interface UpdateTenantRequest {
|
|
|
200
389
|
readonly plan?: string;
|
|
201
390
|
readonly status?: TenantStatus;
|
|
202
391
|
readonly complianceTags?: readonly string[];
|
|
392
|
+
readonly hsmMode?: HsmMode;
|
|
203
393
|
readonly metadata?: Record<string, unknown>;
|
|
204
394
|
readonly security: TenantSecurityEnvelope;
|
|
205
395
|
readonly signature?: TenantSignature;
|
|
@@ -225,6 +415,15 @@ export class TenantClient {
|
|
|
225
415
|
private readonly targetService: string;
|
|
226
416
|
|
|
227
417
|
constructor(config: TenantClientConfig) {
|
|
418
|
+
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
419
|
+
throw new Error(
|
|
420
|
+
"QNSP Tenant SDK: apiKey is required. " +
|
|
421
|
+
"Get your free API key at https://cloud.qnsp.cuilabs.io/signup — " +
|
|
422
|
+
"no credit card required (FREE tier: 5 GB storage, 2,000 API calls/month). " +
|
|
423
|
+
"Docs: https://docs.qnsp.cuilabs.io/sdk/tenant-sdk",
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
228
427
|
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
229
428
|
|
|
230
429
|
// Enforce HTTPS in production (allow HTTP only for localhost in development)
|
|
@@ -242,7 +441,7 @@ export class TenantClient {
|
|
|
242
441
|
|
|
243
442
|
this.config = {
|
|
244
443
|
baseUrl,
|
|
245
|
-
apiKey: config.apiKey
|
|
444
|
+
apiKey: config.apiKey,
|
|
246
445
|
timeoutMs: config.timeoutMs ?? 30_000,
|
|
247
446
|
maxRetries: config.maxRetries ?? 3,
|
|
248
447
|
retryDelayMs: config.retryDelayMs ?? 1_000,
|
|
@@ -277,9 +476,7 @@ export class TenantClient {
|
|
|
277
476
|
...options?.headers,
|
|
278
477
|
};
|
|
279
478
|
|
|
280
|
-
|
|
281
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
282
|
-
}
|
|
479
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
283
480
|
|
|
284
481
|
const controller = new AbortController();
|
|
285
482
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
@@ -394,6 +591,7 @@ export class TenantClient {
|
|
|
394
591
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
395
592
|
...(request.region !== undefined ? { region: request.region } : {}),
|
|
396
593
|
...(request.complianceTags !== undefined ? { complianceTags: request.complianceTags } : {}),
|
|
594
|
+
...(request.hsmMode !== undefined ? { hsmMode: request.hsmMode } : {}),
|
|
397
595
|
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
398
596
|
...(request.domains !== undefined ? { domains: request.domains } : {}),
|
|
399
597
|
security: request.security,
|
|
@@ -415,6 +613,7 @@ export class TenantClient {
|
|
|
415
613
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
416
614
|
...(request.status !== undefined ? { status: request.status } : {}),
|
|
417
615
|
...(request.complianceTags !== undefined ? { complianceTags: request.complianceTags } : {}),
|
|
616
|
+
...(request.hsmMode !== undefined ? { hsmMode: request.hsmMode } : {}),
|
|
418
617
|
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
419
618
|
security: request.security,
|
|
420
619
|
...(request.signature !== undefined ? { signature: request.signature } : {}),
|
|
@@ -471,6 +670,44 @@ export class TenantClient {
|
|
|
471
670
|
});
|
|
472
671
|
}
|
|
473
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Get the v1 crypto policy for a tenant (profiles + tiers model).
|
|
675
|
+
* If no policy exists, a default policy is created and returned.
|
|
676
|
+
*/
|
|
677
|
+
async getTenantCryptoPolicyV1(tenantId: string): Promise<TenantCryptoPolicyV1Record> {
|
|
678
|
+
validateUUID(tenantId, "tenantId");
|
|
679
|
+
|
|
680
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
681
|
+
"GET",
|
|
682
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1`,
|
|
683
|
+
{
|
|
684
|
+
operation: "getTenantCryptoPolicyV1",
|
|
685
|
+
},
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* List v1 crypto policy history entries.
|
|
691
|
+
*/
|
|
692
|
+
async listTenantCryptoPolicyV1History(
|
|
693
|
+
tenantId: string,
|
|
694
|
+
options?: { readonly limit?: number },
|
|
695
|
+
): Promise<TenantCryptoPolicyV1HistoryResponse> {
|
|
696
|
+
validateUUID(tenantId, "tenantId");
|
|
697
|
+
const params = new URLSearchParams();
|
|
698
|
+
if (options?.limit !== undefined) {
|
|
699
|
+
params.set("limit", String(options.limit));
|
|
700
|
+
}
|
|
701
|
+
const query = params.toString();
|
|
702
|
+
const path = query
|
|
703
|
+
? `/tenant/v1/tenants/${tenantId}/crypto-policy-v1/history?${query}`
|
|
704
|
+
: `/tenant/v1/tenants/${tenantId}/crypto-policy-v1/history`;
|
|
705
|
+
|
|
706
|
+
return this.request<TenantCryptoPolicyV1HistoryResponse>("GET", path, {
|
|
707
|
+
operation: "listTenantCryptoPolicyV1History",
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
474
711
|
/**
|
|
475
712
|
* Create or update the crypto policy for a tenant.
|
|
476
713
|
* Sets the policy tier and optional custom algorithm restrictions.
|
|
@@ -499,6 +736,138 @@ export class TenantClient {
|
|
|
499
736
|
});
|
|
500
737
|
}
|
|
501
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Update the v1 crypto policy for a tenant (requires If-Match with current ETag).
|
|
741
|
+
*/
|
|
742
|
+
async updateTenantCryptoPolicyV1(
|
|
743
|
+
tenantId: string,
|
|
744
|
+
policy: CryptoPolicyV1,
|
|
745
|
+
etag: string,
|
|
746
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
747
|
+
validateUUID(tenantId, "tenantId");
|
|
748
|
+
if (!etag) {
|
|
749
|
+
throw new Error("etag is required for updateTenantCryptoPolicyV1");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
753
|
+
"PUT",
|
|
754
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1`,
|
|
755
|
+
{
|
|
756
|
+
body: { policy },
|
|
757
|
+
headers: {
|
|
758
|
+
"If-Match": etag,
|
|
759
|
+
},
|
|
760
|
+
operation: "updateTenantCryptoPolicyV1",
|
|
761
|
+
},
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Enable Tier0 legacy algorithms (time-bounded) for a tenant.
|
|
767
|
+
*/
|
|
768
|
+
async enableTier0Legacy(
|
|
769
|
+
tenantId: string,
|
|
770
|
+
input: { readonly expiry: string },
|
|
771
|
+
etag: string,
|
|
772
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
773
|
+
validateUUID(tenantId, "tenantId");
|
|
774
|
+
if (!etag) {
|
|
775
|
+
throw new Error("etag is required for enableTier0Legacy");
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
779
|
+
"POST",
|
|
780
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier0/enable`,
|
|
781
|
+
{
|
|
782
|
+
body: { expiry: input.expiry },
|
|
783
|
+
headers: {
|
|
784
|
+
"If-Match": etag,
|
|
785
|
+
},
|
|
786
|
+
operation: "enableTier0Legacy",
|
|
787
|
+
},
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Disable Tier0 legacy algorithms for a tenant.
|
|
793
|
+
*/
|
|
794
|
+
async disableTier0Legacy(tenantId: string, etag: string): Promise<TenantCryptoPolicyV1Record> {
|
|
795
|
+
validateUUID(tenantId, "tenantId");
|
|
796
|
+
if (!etag) {
|
|
797
|
+
throw new Error("etag is required for disableTier0Legacy");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
801
|
+
"POST",
|
|
802
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier0/disable`,
|
|
803
|
+
{
|
|
804
|
+
body: {},
|
|
805
|
+
headers: {
|
|
806
|
+
"If-Match": etag,
|
|
807
|
+
},
|
|
808
|
+
operation: "disableTier0Legacy",
|
|
809
|
+
},
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Enable Tier4 experimental algorithms with acknowledgement.
|
|
815
|
+
*/
|
|
816
|
+
async enableTier4Experimental(
|
|
817
|
+
tenantId: string,
|
|
818
|
+
input: { readonly approvedBy: string },
|
|
819
|
+
etag: string,
|
|
820
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
821
|
+
validateUUID(tenantId, "tenantId");
|
|
822
|
+
if (!etag) {
|
|
823
|
+
throw new Error("etag is required for enableTier4Experimental");
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
827
|
+
"POST",
|
|
828
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier4/enable`,
|
|
829
|
+
{
|
|
830
|
+
body: { approvedBy: input.approvedBy },
|
|
831
|
+
headers: {
|
|
832
|
+
"If-Match": etag,
|
|
833
|
+
},
|
|
834
|
+
operation: "enableTier4Experimental",
|
|
835
|
+
},
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Roll back the v1 crypto policy to a previous history record or policy hash.
|
|
841
|
+
*/
|
|
842
|
+
async rollbackTenantCryptoPolicyV1(
|
|
843
|
+
tenantId: string,
|
|
844
|
+
input: { readonly historyId?: string; readonly policyHash?: string },
|
|
845
|
+
etag: string,
|
|
846
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
847
|
+
validateUUID(tenantId, "tenantId");
|
|
848
|
+
if (!etag) {
|
|
849
|
+
throw new Error("etag is required for rollbackTenantCryptoPolicyV1");
|
|
850
|
+
}
|
|
851
|
+
if (!input.historyId && !input.policyHash) {
|
|
852
|
+
throw new Error("historyId or policyHash is required for rollbackTenantCryptoPolicyV1");
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
856
|
+
"POST",
|
|
857
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/rollback`,
|
|
858
|
+
{
|
|
859
|
+
body: {
|
|
860
|
+
...(input.historyId ? { historyId: input.historyId } : {}),
|
|
861
|
+
...(input.policyHash ? { policyHash: input.policyHash } : {}),
|
|
862
|
+
},
|
|
863
|
+
headers: {
|
|
864
|
+
"If-Match": etag,
|
|
865
|
+
},
|
|
866
|
+
operation: "rollbackTenantCryptoPolicyV1",
|
|
867
|
+
},
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
502
871
|
/**
|
|
503
872
|
* Get the allowed KEM algorithms for a tenant based on their crypto policy.
|
|
504
873
|
* Convenience method that fetches the policy and returns the allowed algorithms.
|