@serve.zone/interfaces 22.0.0 → 23.0.1
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 +19 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/appstore/types.d.ts +31 -16
- package/dist_ts/appstore/types.js +208 -11
- package/dist_ts/data/coremail.runtime.d.ts +3 -2
- package/dist_ts/data/coremail.runtime.js +10 -5
- package/dist_ts/data/deploymentoperation.js +6 -20
- package/dist_ts/data/deploymentpreflight.d.ts +2 -6
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/hostedapp.d.ts +40 -14
- package/dist_ts/data/hostedapp.js +90 -1
- package/dist_ts/data/immutableimage.d.ts +30 -1
- package/dist_ts/data/immutableimage.js +103 -1
- package/dist_ts/data/index.d.ts +0 -2
- package/dist_ts/data/index.js +1 -3
- package/dist_ts/data/secret.d.ts +186 -13
- package/dist_ts/data/secret.js +543 -45
- package/dist_ts/data/service.d.ts +0 -39
- package/dist_ts/data/service.js +1 -77
- package/dist_ts/data/settings.d.ts +62 -43
- package/dist_ts/data/settings.js +34 -2
- package/dist_ts/platform/storage.d.ts +13 -14
- package/dist_ts/platform/storage.js +1 -1
- package/dist_ts/platform/storagemigration.d.ts +8 -8
- package/dist_ts/platform/storagemigration.golden.d.ts +2 -2
- package/dist_ts/platform/storagemigration.golden.js +19 -88
- package/dist_ts/platform/storagemigration.js +46 -58
- package/dist_ts/platform/types.d.ts +5 -19
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/requests/hostedapp.d.ts +12 -9
- package/dist_ts/requests/hostedapp.js +38 -1
- package/dist_ts/requests/index.d.ts +1 -3
- package/dist_ts/requests/index.js +2 -4
- package/dist_ts/requests/platform.d.ts +0 -1
- package/dist_ts/requests/secret.d.ts +23 -17
- package/dist_ts/requests/secret.js +161 -2
- package/dist_ts/requests/service.d.ts +0 -13
- package/dist_ts/requests/settings.d.ts +3 -3
- package/dist_ts/runtime.d.ts +78 -26
- package/dist_ts/runtime.js +219 -19
- package/package.json +2 -1
- package/readme.md +110 -29
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/appstore/types.ts +251 -25
- package/ts/data/coremail.runtime.ts +18 -4
- package/ts/data/deploymentoperation.ts +7 -31
- package/ts/data/deploymentpreflight.ts +2 -5
- package/ts/data/hostedapp.ts +144 -18
- package/ts/data/immutableimage.ts +150 -1
- package/ts/data/index.ts +0 -2
- package/ts/data/secret.ts +775 -53
- package/ts/data/service.ts +0 -118
- package/ts/data/settings.ts +70 -80
- package/ts/platform/storage.ts +15 -16
- package/ts/platform/storagemigration.golden.ts +18 -95
- package/ts/platform/storagemigration.ts +57 -87
- package/ts/platform/types.ts +6 -19
- package/ts/plugins.ts +2 -0
- package/ts/requests/hostedapp.ts +50 -9
- package/ts/requests/index.ts +0 -4
- package/ts/requests/platform.ts +0 -1
- package/ts/requests/secret.ts +204 -9
- package/ts/requests/service.ts +0 -16
- package/ts/requests/settings.ts +3 -3
- package/ts/runtime.ts +314 -41
- package/dist_ts/data/secretbundle.d.ts +0 -57
- package/dist_ts/data/secretbundle.js +0 -2
- package/dist_ts/data/secretgroup.d.ts +0 -52
- package/dist_ts/data/secretgroup.js +0 -2
- package/dist_ts/requests/secretbundle.d.ts +0 -75
- package/dist_ts/requests/secretbundle.js +0 -4
- package/dist_ts/requests/secretgroup.d.ts +0 -52
- package/dist_ts/requests/secretgroup.js +0 -4
- package/ts/data/secretbundle.ts +0 -65
- package/ts/data/secretgroup.ts +0 -58
- package/ts/requests/secretbundle.ts +0 -103
- package/ts/requests/secretgroup.ts +0 -74
package/ts/requests/secret.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as plugins from '../plugins.js';
|
|
2
|
+
import * as secretData from '../data/secret.js';
|
|
2
3
|
import type {
|
|
3
4
|
IResolvedSecretManifestEntry,
|
|
4
5
|
ISecretMetadata,
|
|
@@ -7,19 +8,27 @@ import type {
|
|
|
7
8
|
ISecretSetMetadata,
|
|
8
9
|
ISecretVersionMetadata,
|
|
9
10
|
ISecretVersionRetentionReference,
|
|
11
|
+
TSecretIngressRecipient,
|
|
12
|
+
TSecretMutationTarget,
|
|
13
|
+
TSecretValueInput,
|
|
10
14
|
TSecretDelivery,
|
|
11
15
|
TSecretEnvironment,
|
|
12
16
|
} from '../data/secret.js';
|
|
13
17
|
import type { IIdentityCredential } from '../data/user.js';
|
|
14
18
|
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
export interface IReq_GetSecretIngressRecipient
|
|
20
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
21
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
22
|
+
IReq_GetSecretIngressRecipient
|
|
23
|
+
> {
|
|
24
|
+
method: 'getSecretIngressRecipient';
|
|
25
|
+
request: {
|
|
26
|
+
identity: IIdentityCredential;
|
|
27
|
+
};
|
|
28
|
+
response: {
|
|
29
|
+
recipient: TSecretIngressRecipient;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
23
32
|
|
|
24
33
|
export interface IReq_ListSecrets extends plugins.typedrequestInterfaces.implementsTR<
|
|
25
34
|
plugins.typedrequestInterfaces.ITypedRequest,
|
|
@@ -28,6 +37,7 @@ export interface IReq_ListSecrets extends plugins.typedrequestInterfaces.impleme
|
|
|
28
37
|
method: 'listSecrets';
|
|
29
38
|
request: {
|
|
30
39
|
identity: IIdentityCredential;
|
|
40
|
+
mutationId: string;
|
|
31
41
|
target: TSecretMutationTarget;
|
|
32
42
|
environment?: TSecretEnvironment;
|
|
33
43
|
};
|
|
@@ -45,6 +55,7 @@ export interface IReq_GetSecretMetadata extends plugins.typedrequestInterfaces.i
|
|
|
45
55
|
method: 'getSecretMetadata';
|
|
46
56
|
request: {
|
|
47
57
|
identity: IIdentityCredential;
|
|
58
|
+
mutationId: string;
|
|
48
59
|
secretId: string;
|
|
49
60
|
};
|
|
50
61
|
response: {
|
|
@@ -60,6 +71,7 @@ export interface IReq_CreateSecret extends plugins.typedrequestInterfaces.implem
|
|
|
60
71
|
method: 'createSecret';
|
|
61
72
|
request: {
|
|
62
73
|
identity: IIdentityCredential;
|
|
74
|
+
mutationId: string;
|
|
63
75
|
target: TSecretMutationTarget;
|
|
64
76
|
key: string;
|
|
65
77
|
environment: TSecretEnvironment;
|
|
@@ -85,14 +97,18 @@ export interface IReq_RotateSecret extends plugins.typedrequestInterfaces.implem
|
|
|
85
97
|
method: 'rotateSecret';
|
|
86
98
|
request: {
|
|
87
99
|
identity: IIdentityCredential;
|
|
100
|
+
mutationId: string;
|
|
88
101
|
secretId: string;
|
|
89
102
|
valueInput: TSecretValueInput;
|
|
90
103
|
expectedSecretRevision: number;
|
|
91
|
-
expectedActiveVersionId
|
|
104
|
+
expectedActiveVersionId: string;
|
|
105
|
+
/** Aggregate owner fence incremented by every secret mutation. */
|
|
106
|
+
expectedTargetSecretsRevision: number;
|
|
92
107
|
};
|
|
93
108
|
response: {
|
|
94
109
|
secret: ISecretMetadata;
|
|
95
110
|
version: ISecretVersionMetadata;
|
|
111
|
+
targetSecretsRevision: number;
|
|
96
112
|
};
|
|
97
113
|
}
|
|
98
114
|
|
|
@@ -106,9 +122,11 @@ export interface IReq_ChangeSecretLifecycle extends plugins.typedrequestInterfac
|
|
|
106
122
|
secretId: string;
|
|
107
123
|
action: 'retire' | 'revoke' | 'delete' | 'request-purge';
|
|
108
124
|
expectedSecretRevision: number;
|
|
125
|
+
expectedTargetSecretsRevision: number;
|
|
109
126
|
};
|
|
110
127
|
response: {
|
|
111
128
|
secret: ISecretMetadata;
|
|
129
|
+
targetSecretsRevision: number;
|
|
112
130
|
};
|
|
113
131
|
}
|
|
114
132
|
|
|
@@ -187,6 +205,8 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
187
205
|
secretSetId: string;
|
|
188
206
|
action: 'retire' | 'delete';
|
|
189
207
|
expectedRevision: number;
|
|
208
|
+
/** Prevents lifecycle changes racing any secret mutation in this set. */
|
|
209
|
+
expectedSecretsRevision: number;
|
|
190
210
|
};
|
|
191
211
|
response: {
|
|
192
212
|
secretSet: ISecretSetMetadata;
|
|
@@ -254,3 +274,178 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
254
274
|
collisions: ISecretResolutionPreviewCollision[];
|
|
255
275
|
};
|
|
256
276
|
}
|
|
277
|
+
|
|
278
|
+
const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
|
|
279
|
+
Boolean(valueArg) && typeof valueArg === 'object' && !Array.isArray(valueArg)
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
export const validateCreateSecretRequest = async (
|
|
283
|
+
requestArg: unknown,
|
|
284
|
+
activeIngressRecipientArg: TSecretIngressRecipient,
|
|
285
|
+
): Promise<string[]> => {
|
|
286
|
+
if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
|
|
287
|
+
return ['create secret request must be an object'];
|
|
288
|
+
}
|
|
289
|
+
const request = requestArg as Record<string, unknown>;
|
|
290
|
+
const errors = secretData.validateSecretMutationId(request.mutationId);
|
|
291
|
+
const allowedKeys = new Set([
|
|
292
|
+
'identity',
|
|
293
|
+
'mutationId',
|
|
294
|
+
'target',
|
|
295
|
+
'key',
|
|
296
|
+
'environment',
|
|
297
|
+
'name',
|
|
298
|
+
'description',
|
|
299
|
+
'tags',
|
|
300
|
+
'delivery',
|
|
301
|
+
'valueInput',
|
|
302
|
+
'expectedTargetSecretsRevision',
|
|
303
|
+
]);
|
|
304
|
+
if (Object.keys(request).some((keyArg) => !allowedKeys.has(keyArg))) {
|
|
305
|
+
errors.push('create secret request contains fields outside its schema');
|
|
306
|
+
}
|
|
307
|
+
if (!request.identity || typeof request.identity !== 'object'
|
|
308
|
+
|| typeof (request.identity as Record<string, unknown>).jwt !== 'string') {
|
|
309
|
+
errors.push('create secret identity must be a JWT credential');
|
|
310
|
+
}
|
|
311
|
+
if (!request.target || typeof request.target !== 'object' || Array.isArray(request.target)) {
|
|
312
|
+
errors.push('create secret target must be an owner object');
|
|
313
|
+
} else {
|
|
314
|
+
errors.push(...secretData.validateSecretOwner(request.target));
|
|
315
|
+
}
|
|
316
|
+
if (typeof request.key !== 'string' || !/^[A-Z_][A-Z0-9_]{0,252}$/.test(request.key)) {
|
|
317
|
+
errors.push('create secret key must be canonical');
|
|
318
|
+
}
|
|
319
|
+
if (typeof request.environment !== 'string'
|
|
320
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(request.environment)) {
|
|
321
|
+
errors.push('create secret environment must be canonical');
|
|
322
|
+
}
|
|
323
|
+
if (typeof request.name !== 'string'
|
|
324
|
+
|| request.name.trim() !== request.name
|
|
325
|
+
|| request.name.length < 1
|
|
326
|
+
|| request.name.length > 200) {
|
|
327
|
+
errors.push('create secret name must be a bounded non-empty string');
|
|
328
|
+
}
|
|
329
|
+
if (request.description !== undefined
|
|
330
|
+
&& (typeof request.description !== 'string' || request.description.length > 2000)) {
|
|
331
|
+
errors.push('create secret description must be a bounded string');
|
|
332
|
+
}
|
|
333
|
+
if (request.tags !== undefined) {
|
|
334
|
+
if (!Array.isArray(request.tags) || request.tags.length > 64) {
|
|
335
|
+
errors.push('create secret tags must be a bounded array');
|
|
336
|
+
} else {
|
|
337
|
+
const tagKeys: string[] = [];
|
|
338
|
+
request.tags.forEach((tagArg, indexArg) => {
|
|
339
|
+
if (!isRecord(tagArg)
|
|
340
|
+
|| JSON.stringify(Object.keys(tagArg).sort()) !== JSON.stringify(['key', 'value'])
|
|
341
|
+
|| typeof tagArg.key !== 'string'
|
|
342
|
+
|| tagArg.key.length < 1
|
|
343
|
+
|| tagArg.key.length > 100
|
|
344
|
+
|| typeof tagArg.value !== 'string'
|
|
345
|
+
|| tagArg.value.length > 500) {
|
|
346
|
+
errors.push(`create secret tags[${indexArg}] must use the bounded key/value schema`);
|
|
347
|
+
} else {
|
|
348
|
+
tagKeys.push(tagArg.key);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
if (new Set(tagKeys).size !== tagKeys.length) {
|
|
352
|
+
errors.push('create secret tag keys must be unique');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const deliveryErrors = secretData.validateSecretDelivery(request.delivery);
|
|
357
|
+
errors.push(...deliveryErrors);
|
|
358
|
+
if (!Number.isSafeInteger(request.expectedTargetSecretsRevision)
|
|
359
|
+
|| (request.expectedTargetSecretsRevision as number) < 0) {
|
|
360
|
+
errors.push('create secret target revision must be a non-negative integer');
|
|
361
|
+
}
|
|
362
|
+
const canBuildContext = secretData.validateSecretMutationId(request.mutationId).length === 0
|
|
363
|
+
&& secretData.validateSecretOwner(request.target).length === 0
|
|
364
|
+
&& typeof request.key === 'string'
|
|
365
|
+
&& /^[A-Z_][A-Z0-9_]{0,252}$/.test(request.key)
|
|
366
|
+
&& typeof request.environment === 'string'
|
|
367
|
+
&& /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(request.environment)
|
|
368
|
+
&& deliveryErrors.length === 0
|
|
369
|
+
&& Number.isSafeInteger(request.expectedTargetSecretsRevision)
|
|
370
|
+
&& (request.expectedTargetSecretsRevision as number) >= 0;
|
|
371
|
+
const expectedContext = canBuildContext
|
|
372
|
+
? secretData.createSecretCreateEnvelopeContext({
|
|
373
|
+
mutationId: request.mutationId as string,
|
|
374
|
+
target: request.target as TSecretMutationTarget,
|
|
375
|
+
key: request.key as string,
|
|
376
|
+
environment: request.environment as TSecretEnvironment,
|
|
377
|
+
delivery: request.delivery as TSecretDelivery,
|
|
378
|
+
expectedTargetSecretsRevision: request.expectedTargetSecretsRevision as number,
|
|
379
|
+
})
|
|
380
|
+
: new Uint8Array();
|
|
381
|
+
errors.push(...await secretData.validateSecretValueInputForRecipient(
|
|
382
|
+
request.valueInput,
|
|
383
|
+
activeIngressRecipientArg,
|
|
384
|
+
expectedContext,
|
|
385
|
+
));
|
|
386
|
+
return errors;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
export const validateRotateSecretRequest = async (
|
|
390
|
+
requestArg: unknown,
|
|
391
|
+
activeIngressRecipientArg: TSecretIngressRecipient,
|
|
392
|
+
): Promise<string[]> => {
|
|
393
|
+
if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
|
|
394
|
+
return ['rotate secret request must be an object'];
|
|
395
|
+
}
|
|
396
|
+
const request = requestArg as Record<string, unknown>;
|
|
397
|
+
const errors = secretData.validateSecretMutationId(request.mutationId);
|
|
398
|
+
if (JSON.stringify(Object.keys(request).sort()) !== JSON.stringify([
|
|
399
|
+
'expectedActiveVersionId',
|
|
400
|
+
'expectedSecretRevision',
|
|
401
|
+
'expectedTargetSecretsRevision',
|
|
402
|
+
'identity',
|
|
403
|
+
'mutationId',
|
|
404
|
+
'secretId',
|
|
405
|
+
'valueInput',
|
|
406
|
+
])) {
|
|
407
|
+
errors.push('rotate secret request must use its exact schema');
|
|
408
|
+
}
|
|
409
|
+
if (!request.identity || typeof request.identity !== 'object'
|
|
410
|
+
|| typeof (request.identity as Record<string, unknown>).jwt !== 'string') {
|
|
411
|
+
errors.push('rotate secret identity must be a JWT credential');
|
|
412
|
+
}
|
|
413
|
+
for (const field of ['secretId', 'expectedActiveVersionId'] as const) {
|
|
414
|
+
if (typeof request[field] !== 'string'
|
|
415
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(request[field] as string)) {
|
|
416
|
+
errors.push(`rotate secret ${field} must be canonical`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!Number.isSafeInteger(request.expectedSecretRevision)
|
|
420
|
+
|| (request.expectedSecretRevision as number) < 1) {
|
|
421
|
+
errors.push('rotate secret revision must be a positive integer');
|
|
422
|
+
}
|
|
423
|
+
if (!Number.isSafeInteger(request.expectedTargetSecretsRevision)
|
|
424
|
+
|| (request.expectedTargetSecretsRevision as number) < 0) {
|
|
425
|
+
errors.push('rotate secret target revision must be a non-negative integer');
|
|
426
|
+
}
|
|
427
|
+
const canBuildContext = secretData.validateSecretMutationId(request.mutationId).length === 0
|
|
428
|
+
&& typeof request.secretId === 'string'
|
|
429
|
+
&& /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(request.secretId)
|
|
430
|
+
&& typeof request.expectedActiveVersionId === 'string'
|
|
431
|
+
&& /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(request.expectedActiveVersionId)
|
|
432
|
+
&& Number.isSafeInteger(request.expectedSecretRevision)
|
|
433
|
+
&& (request.expectedSecretRevision as number) >= 1
|
|
434
|
+
&& Number.isSafeInteger(request.expectedTargetSecretsRevision)
|
|
435
|
+
&& (request.expectedTargetSecretsRevision as number) >= 0;
|
|
436
|
+
const expectedContext = canBuildContext
|
|
437
|
+
? secretData.createSecretRotateEnvelopeContext({
|
|
438
|
+
mutationId: request.mutationId as string,
|
|
439
|
+
secretId: request.secretId as string,
|
|
440
|
+
expectedSecretRevision: request.expectedSecretRevision as number,
|
|
441
|
+
expectedActiveVersionId: request.expectedActiveVersionId as string,
|
|
442
|
+
expectedTargetSecretsRevision: request.expectedTargetSecretsRevision as number,
|
|
443
|
+
})
|
|
444
|
+
: new Uint8Array();
|
|
445
|
+
errors.push(...await secretData.validateSecretValueInputForRecipient(
|
|
446
|
+
request.valueInput,
|
|
447
|
+
activeIngressRecipientArg,
|
|
448
|
+
expectedContext,
|
|
449
|
+
));
|
|
450
|
+
return errors;
|
|
451
|
+
};
|
package/ts/requests/service.ts
CHANGED
|
@@ -368,19 +368,3 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
368
368
|
success: boolean;
|
|
369
369
|
};
|
|
370
370
|
}
|
|
371
|
-
|
|
372
|
-
export interface IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject
|
|
373
|
-
extends plugins.typedrequestInterfaces.implementsTR<
|
|
374
|
-
plugins.typedrequestInterfaces.ITypedRequest,
|
|
375
|
-
IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject
|
|
376
|
-
> {
|
|
377
|
-
method: 'getServiceSecretBundlesAsFlatObject';
|
|
378
|
-
request: {
|
|
379
|
-
identity: IIdentity;
|
|
380
|
-
serviceId: string;
|
|
381
|
-
environment: string;
|
|
382
|
-
};
|
|
383
|
-
response: {
|
|
384
|
-
flatKeyValueObject: {[key: string]: string};
|
|
385
|
-
};
|
|
386
|
-
}
|
package/ts/requests/settings.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import type { ICloudlySettings
|
|
2
|
+
import type { ICloudlySettings } from '../data/settings.js';
|
|
3
3
|
import type { IIdentity } from '../data/user.js';
|
|
4
4
|
|
|
5
5
|
// Get Settings
|
|
@@ -9,7 +9,7 @@ export interface IRequest_GetSettings extends plugins.typedrequestInterfaces.ITy
|
|
|
9
9
|
identity?: IIdentity;
|
|
10
10
|
};
|
|
11
11
|
response: {
|
|
12
|
-
settings:
|
|
12
|
+
settings: ICloudlySettings;
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -61,6 +61,6 @@ export interface IRequest_GetSetting extends plugins.typedrequestInterfaces.ITyp
|
|
|
61
61
|
key: keyof ICloudlySettings;
|
|
62
62
|
};
|
|
63
63
|
response: {
|
|
64
|
-
value:
|
|
64
|
+
value: ICloudlySettings[keyof ICloudlySettings] | undefined;
|
|
65
65
|
};
|
|
66
66
|
}
|