@grantjs/schema 1.20.2 → 1.22.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/package.json +1 -1
- package/src/generated/graphql.ts +87 -1
- package/src/generated/resolvers.ts +49 -0
- package/src/generated/schema-types.ts +116 -7
- package/src/operations/project-users/exportProjectUserCredentials.graphql +6 -0
- package/src/operations/projects/project-sync-job.graphql +1 -0
- package/src/operations/projects/set-project-exportable-api-key-secrets.graphql +7 -0
- package/src/schema/project-users/queries/export-project-user-credentials.graphql +8 -5
- package/src/schema/project-users/types/credential-kind.graphql +5 -0
- package/src/schema/project-users/types/project-user-api-key-credential.graphql +21 -0
- package/src/schema/project-users/types/project-user-credential-export.graphql +29 -2
- package/src/schema/projects/inputs/set-project-exportable-api-key-secrets.graphql +12 -0
- package/src/schema/projects/inputs/sync-project-cdm.graphql +5 -0
- package/src/schema/projects/mutations/set-project-exportable-api-key-secrets.graphql +11 -0
- package/src/schema/projects/types/project.graphql +7 -0
- package/src/schema/projects/types/sync-project-result.graphql +5 -0
- package/src/schema/users/types/user.graphql +7 -0
- package/src/sdl-contract.test.ts +2 -1
package/package.json
CHANGED
package/src/generated/graphql.ts
CHANGED
|
@@ -1316,7 +1316,16 @@ export type ExportProjectUserCredentialsQueryVariables = Exact<{
|
|
|
1316
1316
|
|
|
1317
1317
|
export type ExportProjectUserCredentialsQuery = {
|
|
1318
1318
|
exportProjectUserCredentials: {
|
|
1319
|
-
users: Array<{
|
|
1319
|
+
users: Array<{
|
|
1320
|
+
userId: string;
|
|
1321
|
+
password: { algorithm: string; hash: string } | null;
|
|
1322
|
+
apiKeySecrets: Array<{
|
|
1323
|
+
apiKeyId: string;
|
|
1324
|
+
clientId: string;
|
|
1325
|
+
fingerprint: string | null;
|
|
1326
|
+
secret: string | null;
|
|
1327
|
+
}> | null;
|
|
1328
|
+
}>;
|
|
1320
1329
|
};
|
|
1321
1330
|
};
|
|
1322
1331
|
|
|
@@ -1491,6 +1500,7 @@ export type ProjectSyncJobQuery = {
|
|
|
1491
1500
|
passwordsUpdated: number;
|
|
1492
1501
|
passwordsUnchanged: number;
|
|
1493
1502
|
passwordsSkippedExisting: number;
|
|
1503
|
+
usersSkippedTombstoned: number;
|
|
1494
1504
|
warnings: Array<string>;
|
|
1495
1505
|
} | null;
|
|
1496
1506
|
};
|
|
@@ -1547,6 +1557,18 @@ export type RequestProjectSyncUploadUrlMutation = {
|
|
|
1547
1557
|
};
|
|
1548
1558
|
};
|
|
1549
1559
|
|
|
1560
|
+
export type SetProjectExportableApiKeySecretsMutationVariables = Exact<{
|
|
1561
|
+
input: Types.SetProjectExportableApiKeySecretsInput;
|
|
1562
|
+
}>;
|
|
1563
|
+
|
|
1564
|
+
export type SetProjectExportableApiKeySecretsMutation = {
|
|
1565
|
+
setProjectExportableApiKeySecrets: {
|
|
1566
|
+
id: string;
|
|
1567
|
+
exportableApiKeySecrets: boolean;
|
|
1568
|
+
updatedAt: Date;
|
|
1569
|
+
};
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1550
1572
|
export type StartProjectExportMutationVariables = Exact<{
|
|
1551
1573
|
id: string | number;
|
|
1552
1574
|
scope: Types.Scope;
|
|
@@ -7545,6 +7567,19 @@ export const ExportProjectUserCredentialsDocument = {
|
|
|
7545
7567
|
],
|
|
7546
7568
|
},
|
|
7547
7569
|
},
|
|
7570
|
+
{
|
|
7571
|
+
kind: 'Field',
|
|
7572
|
+
name: { kind: 'Name', value: 'apiKeySecrets' },
|
|
7573
|
+
selectionSet: {
|
|
7574
|
+
kind: 'SelectionSet',
|
|
7575
|
+
selections: [
|
|
7576
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'apiKeyId' } },
|
|
7577
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'clientId' } },
|
|
7578
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'fingerprint' } },
|
|
7579
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'secret' } },
|
|
7580
|
+
],
|
|
7581
|
+
},
|
|
7582
|
+
},
|
|
7548
7583
|
],
|
|
7549
7584
|
},
|
|
7550
7585
|
},
|
|
@@ -8165,6 +8200,7 @@ export const ProjectSyncJobDocument = {
|
|
|
8165
8200
|
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsUpdated' } },
|
|
8166
8201
|
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsUnchanged' } },
|
|
8167
8202
|
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsSkippedExisting' } },
|
|
8203
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'usersSkippedTombstoned' } },
|
|
8168
8204
|
{ kind: 'Field', name: { kind: 'Name', value: 'warnings' } },
|
|
8169
8205
|
],
|
|
8170
8206
|
},
|
|
@@ -8405,6 +8441,56 @@ export const RequestProjectSyncUploadUrlDocument = {
|
|
|
8405
8441
|
RequestProjectSyncUploadUrlMutation,
|
|
8406
8442
|
RequestProjectSyncUploadUrlMutationVariables
|
|
8407
8443
|
>;
|
|
8444
|
+
export const SetProjectExportableApiKeySecretsDocument = {
|
|
8445
|
+
kind: 'Document',
|
|
8446
|
+
definitions: [
|
|
8447
|
+
{
|
|
8448
|
+
kind: 'OperationDefinition',
|
|
8449
|
+
operation: 'mutation',
|
|
8450
|
+
name: { kind: 'Name', value: 'SetProjectExportableApiKeySecrets' },
|
|
8451
|
+
variableDefinitions: [
|
|
8452
|
+
{
|
|
8453
|
+
kind: 'VariableDefinition',
|
|
8454
|
+
variable: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
|
|
8455
|
+
type: {
|
|
8456
|
+
kind: 'NonNullType',
|
|
8457
|
+
type: {
|
|
8458
|
+
kind: 'NamedType',
|
|
8459
|
+
name: { kind: 'Name', value: 'SetProjectExportableApiKeySecretsInput' },
|
|
8460
|
+
},
|
|
8461
|
+
},
|
|
8462
|
+
},
|
|
8463
|
+
],
|
|
8464
|
+
selectionSet: {
|
|
8465
|
+
kind: 'SelectionSet',
|
|
8466
|
+
selections: [
|
|
8467
|
+
{
|
|
8468
|
+
kind: 'Field',
|
|
8469
|
+
name: { kind: 'Name', value: 'setProjectExportableApiKeySecrets' },
|
|
8470
|
+
arguments: [
|
|
8471
|
+
{
|
|
8472
|
+
kind: 'Argument',
|
|
8473
|
+
name: { kind: 'Name', value: 'input' },
|
|
8474
|
+
value: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
|
|
8475
|
+
},
|
|
8476
|
+
],
|
|
8477
|
+
selectionSet: {
|
|
8478
|
+
kind: 'SelectionSet',
|
|
8479
|
+
selections: [
|
|
8480
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
|
8481
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'exportableApiKeySecrets' } },
|
|
8482
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'updatedAt' } },
|
|
8483
|
+
],
|
|
8484
|
+
},
|
|
8485
|
+
},
|
|
8486
|
+
],
|
|
8487
|
+
},
|
|
8488
|
+
},
|
|
8489
|
+
],
|
|
8490
|
+
} as unknown as DocumentNode<
|
|
8491
|
+
SetProjectExportableApiKeySecretsMutation,
|
|
8492
|
+
SetProjectExportableApiKeySecretsMutationVariables
|
|
8493
|
+
>;
|
|
8408
8494
|
export const StartProjectExportDocument = {
|
|
8409
8495
|
kind: 'Document',
|
|
8410
8496
|
definitions: [
|
|
@@ -283,6 +283,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
283
283
|
ExchangeApiKeyInput: Types.ExchangeApiKeyInput;
|
|
284
284
|
ExchangeApiKeyResult: ResolverTypeWrapper<Types.ExchangeApiKeyResult>;
|
|
285
285
|
ExportProjectUserCredentialsInput: Types.ExportProjectUserCredentialsInput;
|
|
286
|
+
ExportedApiKeySecret: ResolverTypeWrapper<Types.ExportedApiKeySecret>;
|
|
286
287
|
ExportedPasswordCredential: ResolverTypeWrapper<Types.ExportedPasswordCredential>;
|
|
287
288
|
ExportedProjectUserCredential: ResolverTypeWrapper<Types.ExportedProjectUserCredential>;
|
|
288
289
|
GenerateMyMfaRecoveryCodesInput: Types.GenerateMyMfaRecoveryCodesInput;
|
|
@@ -389,6 +390,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
389
390
|
ProjectTag: ResolverTypeWrapper<Types.ProjectTag>;
|
|
390
391
|
ProjectUser: ResolverTypeWrapper<Types.ProjectUser>;
|
|
391
392
|
ProjectUserApiKey: ResolverTypeWrapper<Types.ProjectUserApiKey>;
|
|
393
|
+
ProjectUserApiKeyCredential: ResolverTypeWrapper<Types.ProjectUserApiKeyCredential>;
|
|
392
394
|
ProjectUserCredentialExport: ResolverTypeWrapper<Types.ProjectUserCredentialExport>;
|
|
393
395
|
ProjectUserGroup: ResolverTypeWrapper<Types.ProjectUserGroup>;
|
|
394
396
|
ProjectUserPasswordCredential: ResolverTypeWrapper<Types.ProjectUserPasswordCredential>;
|
|
@@ -507,6 +509,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
507
509
|
SessionExportData: ResolverTypeWrapper<Types.SessionExportData>;
|
|
508
510
|
SetMyNotificationPreferenceInput: Types.SetMyNotificationPreferenceInput;
|
|
509
511
|
SetMyPrimaryMfaDeviceInput: Types.SetMyPrimaryMfaDeviceInput;
|
|
512
|
+
SetProjectExportableApiKeySecretsInput: Types.SetProjectExportableApiKeySecretsInput;
|
|
510
513
|
SetProjectUserEmailInput: Types.SetProjectUserEmailInput;
|
|
511
514
|
SetProjectUserPasswordInput: Types.SetProjectUserPasswordInput;
|
|
512
515
|
SigningKey: ResolverTypeWrapper<Types.SigningKey>;
|
|
@@ -700,6 +703,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
700
703
|
ExchangeApiKeyInput: Types.ExchangeApiKeyInput;
|
|
701
704
|
ExchangeApiKeyResult: Types.ExchangeApiKeyResult;
|
|
702
705
|
ExportProjectUserCredentialsInput: Types.ExportProjectUserCredentialsInput;
|
|
706
|
+
ExportedApiKeySecret: Types.ExportedApiKeySecret;
|
|
703
707
|
ExportedPasswordCredential: Types.ExportedPasswordCredential;
|
|
704
708
|
ExportedProjectUserCredential: Types.ExportedProjectUserCredential;
|
|
705
709
|
GenerateMyMfaRecoveryCodesInput: Types.GenerateMyMfaRecoveryCodesInput;
|
|
@@ -782,6 +786,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
782
786
|
ProjectTag: Types.ProjectTag;
|
|
783
787
|
ProjectUser: Types.ProjectUser;
|
|
784
788
|
ProjectUserApiKey: Types.ProjectUserApiKey;
|
|
789
|
+
ProjectUserApiKeyCredential: Types.ProjectUserApiKeyCredential;
|
|
785
790
|
ProjectUserCredentialExport: Types.ProjectUserCredentialExport;
|
|
786
791
|
ProjectUserGroup: Types.ProjectUserGroup;
|
|
787
792
|
ProjectUserPasswordCredential: Types.ProjectUserPasswordCredential;
|
|
@@ -896,6 +901,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
896
901
|
SessionExportData: Types.SessionExportData;
|
|
897
902
|
SetMyNotificationPreferenceInput: Types.SetMyNotificationPreferenceInput;
|
|
898
903
|
SetMyPrimaryMfaDeviceInput: Types.SetMyPrimaryMfaDeviceInput;
|
|
904
|
+
SetProjectExportableApiKeySecretsInput: Types.SetProjectExportableApiKeySecretsInput;
|
|
899
905
|
SetProjectUserEmailInput: Types.SetProjectUserEmailInput;
|
|
900
906
|
SetProjectUserPasswordInput: Types.SetProjectUserPasswordInput;
|
|
901
907
|
SigningKey: Types.SigningKey;
|
|
@@ -1338,6 +1344,17 @@ export type ExchangeApiKeyResultResolvers<
|
|
|
1338
1344
|
expiresIn?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
1339
1345
|
}>;
|
|
1340
1346
|
|
|
1347
|
+
export type ExportedApiKeySecretResolvers<
|
|
1348
|
+
ContextType = any,
|
|
1349
|
+
ParentType extends ResolversParentTypes['ExportedApiKeySecret'] =
|
|
1350
|
+
ResolversParentTypes['ExportedApiKeySecret'],
|
|
1351
|
+
> = ResolversObject<{
|
|
1352
|
+
apiKeyId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
1353
|
+
clientId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1354
|
+
fingerprint?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1355
|
+
secret?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1356
|
+
}>;
|
|
1357
|
+
|
|
1341
1358
|
export type ExportedPasswordCredentialResolvers<
|
|
1342
1359
|
ContextType = any,
|
|
1343
1360
|
ParentType extends ResolversParentTypes['ExportedPasswordCredential'] =
|
|
@@ -1352,6 +1369,11 @@ export type ExportedProjectUserCredentialResolvers<
|
|
|
1352
1369
|
ParentType extends ResolversParentTypes['ExportedProjectUserCredential'] =
|
|
1353
1370
|
ResolversParentTypes['ExportedProjectUserCredential'],
|
|
1354
1371
|
> = ResolversObject<{
|
|
1372
|
+
apiKeySecrets?: Resolver<
|
|
1373
|
+
Types.Maybe<Array<ResolversTypes['ExportedApiKeySecret']>>,
|
|
1374
|
+
ParentType,
|
|
1375
|
+
ContextType
|
|
1376
|
+
>;
|
|
1355
1377
|
password?: Resolver<
|
|
1356
1378
|
Types.Maybe<ResolversTypes['ExportedPasswordCredential']>,
|
|
1357
1379
|
ParentType,
|
|
@@ -1997,6 +2019,12 @@ export type MutationResolvers<
|
|
|
1997
2019
|
ContextType,
|
|
1998
2020
|
RequireFields<Types.MutationSetMyPrimaryMfaDeviceArgs, 'input'>
|
|
1999
2021
|
>;
|
|
2022
|
+
setProjectExportableApiKeySecrets?: Resolver<
|
|
2023
|
+
ResolversTypes['Project'],
|
|
2024
|
+
ParentType,
|
|
2025
|
+
ContextType,
|
|
2026
|
+
RequireFields<Types.MutationSetProjectExportableApiKeySecretsArgs, 'input'>
|
|
2027
|
+
>;
|
|
2000
2028
|
setProjectUserEmail?: Resolver<
|
|
2001
2029
|
ResolversTypes['User'],
|
|
2002
2030
|
ParentType,
|
|
@@ -2577,6 +2605,7 @@ export type ProjectResolvers<
|
|
|
2577
2605
|
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
|
2578
2606
|
deletedAt?: Resolver<Types.Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
|
2579
2607
|
description?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2608
|
+
exportableApiKeySecrets?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
|
2580
2609
|
groups?: Resolver<Types.Maybe<Array<ResolversTypes['Group']>>, ParentType, ContextType>;
|
|
2581
2610
|
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2582
2611
|
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2887,6 +2916,18 @@ export type ProjectUserApiKeyResolvers<
|
|
|
2887
2916
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2888
2917
|
}>;
|
|
2889
2918
|
|
|
2919
|
+
export type ProjectUserApiKeyCredentialResolvers<
|
|
2920
|
+
ContextType = any,
|
|
2921
|
+
ParentType extends ResolversParentTypes['ProjectUserApiKeyCredential'] =
|
|
2922
|
+
ResolversParentTypes['ProjectUserApiKeyCredential'],
|
|
2923
|
+
> = ResolversObject<{
|
|
2924
|
+
apiKeyId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2925
|
+
clientId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2926
|
+
exportable?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
|
2927
|
+
fingerprint?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2928
|
+
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
|
2929
|
+
}>;
|
|
2930
|
+
|
|
2890
2931
|
export type ProjectUserCredentialExportResolvers<
|
|
2891
2932
|
ContextType = any,
|
|
2892
2933
|
ParentType extends ResolversParentTypes['ProjectUserCredentialExport'] =
|
|
@@ -3404,6 +3445,7 @@ export type SyncProjectResultResolvers<
|
|
|
3404
3445
|
userRolesAssigned?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3405
3446
|
userTagsLinked?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3406
3447
|
usersCreated?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3448
|
+
usersSkippedTombstoned?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3407
3449
|
warnings?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
|
|
3408
3450
|
}>;
|
|
3409
3451
|
|
|
@@ -3500,6 +3542,11 @@ export type UserResolvers<
|
|
|
3500
3542
|
ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User'],
|
|
3501
3543
|
> = ResolversObject<{
|
|
3502
3544
|
accounts?: Resolver<Types.Maybe<Array<ResolversTypes['Account']>>, ParentType, ContextType>;
|
|
3545
|
+
apiKeyCredentials?: Resolver<
|
|
3546
|
+
Types.Maybe<Array<ResolversTypes['ProjectUserApiKeyCredential']>>,
|
|
3547
|
+
ParentType,
|
|
3548
|
+
ContextType
|
|
3549
|
+
>;
|
|
3503
3550
|
authenticationMethods?: Resolver<
|
|
3504
3551
|
Types.Maybe<Array<ResolversTypes['UserAuthenticationMethod']>>,
|
|
3505
3552
|
ParentType,
|
|
@@ -3836,6 +3883,7 @@ export type Resolvers<ContextType = any> = ResolversObject<{
|
|
|
3836
3883
|
CredentialExportData?: CredentialExportDataResolvers<ContextType>;
|
|
3837
3884
|
Date?: GraphQLScalarType;
|
|
3838
3885
|
ExchangeApiKeyResult?: ExchangeApiKeyResultResolvers<ContextType>;
|
|
3886
|
+
ExportedApiKeySecret?: ExportedApiKeySecretResolvers<ContextType>;
|
|
3839
3887
|
ExportedPasswordCredential?: ExportedPasswordCredentialResolvers<ContextType>;
|
|
3840
3888
|
ExportedProjectUserCredential?: ExportedProjectUserCredentialResolvers<ContextType>;
|
|
3841
3889
|
Group?: GroupResolvers<ContextType>;
|
|
@@ -3896,6 +3944,7 @@ export type Resolvers<ContextType = any> = ResolversObject<{
|
|
|
3896
3944
|
ProjectTag?: ProjectTagResolvers<ContextType>;
|
|
3897
3945
|
ProjectUser?: ProjectUserResolvers<ContextType>;
|
|
3898
3946
|
ProjectUserApiKey?: ProjectUserApiKeyResolvers<ContextType>;
|
|
3947
|
+
ProjectUserApiKeyCredential?: ProjectUserApiKeyCredentialResolvers<ContextType>;
|
|
3899
3948
|
ProjectUserCredentialExport?: ProjectUserCredentialExportResolvers<ContextType>;
|
|
3900
3949
|
ProjectUserGroup?: ProjectUserGroupResolvers<ContextType>;
|
|
3901
3950
|
ProjectUserPasswordCredential?: ProjectUserPasswordCredentialResolvers<ContextType>;
|
|
@@ -453,6 +453,11 @@ export type CdmKeyResolverInput = {
|
|
|
453
453
|
* import/export.
|
|
454
454
|
*/
|
|
455
455
|
findBy?: InputMaybe<CdmFindBy>;
|
|
456
|
+
/**
|
|
457
|
+
* Only with `findBy: key`: an existing, live Grant user this key is, reused instead of creating one and marked
|
|
458
|
+
* with the key for this project. Refused for a missing, deleted or already-imported-under-another-key user.
|
|
459
|
+
*/
|
|
460
|
+
grantUserId?: InputMaybe<Scalars['ID']['input']>;
|
|
456
461
|
ifMissing?: InputMaybe<CdmIfMissing>;
|
|
457
462
|
/**
|
|
458
463
|
* Identifier value. For `findBy: email`, Grant normalizes this to lowercase
|
|
@@ -813,6 +818,11 @@ export type CredentialExportData = {
|
|
|
813
818
|
* not as a second export.
|
|
814
819
|
*/
|
|
815
820
|
export enum CredentialKind {
|
|
821
|
+
/**
|
|
822
|
+
* The API keys of the project user, with the plaintext secret of each key the project keeps an
|
|
823
|
+
* exportable copy of (a project that opted in to exportable API-key secrets).
|
|
824
|
+
*/
|
|
825
|
+
ApiKeySecret = 'API_KEY_SECRET',
|
|
816
826
|
/** The project password credential (bcrypt hash). */
|
|
817
827
|
Password = 'PASSWORD',
|
|
818
828
|
}
|
|
@@ -871,6 +881,26 @@ export type ExportProjectUserCredentialsInput = {
|
|
|
871
881
|
userIds: Array<Scalars['ID']['input']>;
|
|
872
882
|
};
|
|
873
883
|
|
|
884
|
+
/** One API key of a project user in an export. `secret` is a credential: handle it like one. */
|
|
885
|
+
export type ExportedApiKeySecret = {
|
|
886
|
+
__typename?: 'ExportedApiKeySecret';
|
|
887
|
+
apiKeyId: Scalars['ID']['output'];
|
|
888
|
+
/** The key's public client id. */
|
|
889
|
+
clientId: Scalars['String']['output'];
|
|
890
|
+
/**
|
|
891
|
+
* Lowercase hex SHA-256 of the secret string as issued, to compare with `sha256(secret)` of the
|
|
892
|
+
* value a receiver holds. `null` for a key created before the fingerprint existed and not rotated
|
|
893
|
+
* since.
|
|
894
|
+
*/
|
|
895
|
+
fingerprint?: Maybe<Scalars['String']['output']>;
|
|
896
|
+
/**
|
|
897
|
+
* The plaintext secret: 88 characters, standard base64 of 64 random bytes. `null` for a key the
|
|
898
|
+
* project holds no copy of (created before the project opted in, created by a CDM import, or
|
|
899
|
+
* rotated by a CDM replace): Grant cannot recover it.
|
|
900
|
+
*/
|
|
901
|
+
secret?: Maybe<Scalars['String']['output']>;
|
|
902
|
+
};
|
|
903
|
+
|
|
874
904
|
/** A stored password hash in CDM shape. Secret-equivalent: handle like a credential. */
|
|
875
905
|
export type ExportedPasswordCredential = {
|
|
876
906
|
__typename?: 'ExportedPasswordCredential';
|
|
@@ -882,7 +912,12 @@ export type ExportedPasswordCredential = {
|
|
|
882
912
|
|
|
883
913
|
export type ExportedProjectUserCredential = {
|
|
884
914
|
__typename?: 'ExportedProjectUserCredential';
|
|
885
|
-
/**
|
|
915
|
+
/**
|
|
916
|
+
* The member's live (not revoked, not deleted) project-user API keys, oldest first. `null` when the
|
|
917
|
+
* `API_KEY_SECRET` kind was not requested; `[]` for a member with no key.
|
|
918
|
+
*/
|
|
919
|
+
apiKeySecrets?: Maybe<Array<ExportedApiKeySecret>>;
|
|
920
|
+
/** `null` when the member has no project password credential, and when the `PASSWORD` kind was not requested. */
|
|
886
921
|
password?: Maybe<ExportedPasswordCredential>;
|
|
887
922
|
userId: Scalars['ID']['output'];
|
|
888
923
|
};
|
|
@@ -1324,6 +1359,15 @@ export type Mutation = {
|
|
|
1324
1359
|
setMyNotificationPreference: NotificationPreference;
|
|
1325
1360
|
setMyPrimaryAuthenticationMethod: UserAuthenticationMethod;
|
|
1326
1361
|
setMyPrimaryMfaDevice: MfaDevice;
|
|
1362
|
+
/**
|
|
1363
|
+
* Opt a project in or out of keeping an exportable, encrypted copy of a project user's API-key
|
|
1364
|
+
* secret. Project scopes only. Requires `Project:SetExportableApiKeySecrets`, held by the Owner and
|
|
1365
|
+
* Admin roles and not by Dev. Enabling fails with a configuration error while
|
|
1366
|
+
* `API_KEY_EXPORTABLE_SECRET_ENCRYPTION_KEY` is not set; disabling never needs it. Each change is
|
|
1367
|
+
* audited on the project with the old and new value; the same value is a no-op. Nothing is
|
|
1368
|
+
* back-filled: keys that exist before the opt-in keep only their hash.
|
|
1369
|
+
*/
|
|
1370
|
+
setProjectExportableApiKeySecrets: Project;
|
|
1327
1371
|
/**
|
|
1328
1372
|
* Set or replace a project member's Email authentication method as unverified.
|
|
1329
1373
|
* `project.user_email_changed` carries ids only, never the address.
|
|
@@ -1699,6 +1743,10 @@ export type MutationSetMyPrimaryMfaDeviceArgs = {
|
|
|
1699
1743
|
input: SetMyPrimaryMfaDeviceInput;
|
|
1700
1744
|
};
|
|
1701
1745
|
|
|
1746
|
+
export type MutationSetProjectExportableApiKeySecretsArgs = {
|
|
1747
|
+
input: SetProjectExportableApiKeySecretsInput;
|
|
1748
|
+
};
|
|
1749
|
+
|
|
1702
1750
|
export type MutationSetProjectUserEmailArgs = {
|
|
1703
1751
|
input: SetProjectUserEmailInput;
|
|
1704
1752
|
};
|
|
@@ -2236,6 +2284,13 @@ export type Project = Auditable & {
|
|
|
2236
2284
|
createdAt: Scalars['Date']['output'];
|
|
2237
2285
|
deletedAt?: Maybe<Scalars['Date']['output']>;
|
|
2238
2286
|
description?: Maybe<Scalars['String']['output']>;
|
|
2287
|
+
/**
|
|
2288
|
+
* Whether this project keeps an exportable, encrypted copy of a project user's API-key secret at
|
|
2289
|
+
* create and rotate. Off by default; set with `setProjectExportableApiKeySecrets`. A plain
|
|
2290
|
+
* configuration flag: it says nothing about any key, and nothing is back-filled for keys that
|
|
2291
|
+
* already exist.
|
|
2292
|
+
*/
|
|
2293
|
+
exportableApiKeySecrets: Scalars['Boolean']['output'];
|
|
2239
2294
|
groups?: Maybe<Array<Group>>;
|
|
2240
2295
|
id: Scalars['ID']['output'];
|
|
2241
2296
|
name: Scalars['String']['output'];
|
|
@@ -2580,7 +2635,33 @@ export type ProjectUserApiKey = Auditable & {
|
|
|
2580
2635
|
userId: Scalars['ID']['output'];
|
|
2581
2636
|
};
|
|
2582
2637
|
|
|
2583
|
-
/**
|
|
2638
|
+
/**
|
|
2639
|
+
* Non-secret facts about one of a project user's API keys. Never contains the secret, the hash or the
|
|
2640
|
+
* encrypted copy.
|
|
2641
|
+
*/
|
|
2642
|
+
export type ProjectUserApiKeyCredential = {
|
|
2643
|
+
__typename?: 'ProjectUserApiKeyCredential';
|
|
2644
|
+
apiKeyId: Scalars['ID']['output'];
|
|
2645
|
+
/** The key's public client id. */
|
|
2646
|
+
clientId: Scalars['String']['output'];
|
|
2647
|
+
/** An encrypted copy of the key's current secret exists (only in a project that opted in). */
|
|
2648
|
+
exportable: Scalars['Boolean']['output'];
|
|
2649
|
+
/**
|
|
2650
|
+
* Lowercase hex SHA-256 of the secret string as issued (the text the client received, never the
|
|
2651
|
+
* stored hash): a change detector a holder of the secret can compare against. `null` for a key
|
|
2652
|
+
* created before this field existed and not rotated since. Grant issues secrets of 32 or 64 random
|
|
2653
|
+
* bytes, so the digest cannot be guessed offline; a secret chosen by an importer is only as strong
|
|
2654
|
+
* as the importer made it.
|
|
2655
|
+
*/
|
|
2656
|
+
fingerprint?: Maybe<Scalars['String']['output']>;
|
|
2657
|
+
/** When the key last changed (created, rotated or edited). */
|
|
2658
|
+
updatedAt: Scalars['Date']['output'];
|
|
2659
|
+
};
|
|
2660
|
+
|
|
2661
|
+
/**
|
|
2662
|
+
* Result of `exportProjectUserCredentials`. This is the one place a credential hash, and the plaintext
|
|
2663
|
+
* secret of an exportable API key, leaves Grant.
|
|
2664
|
+
*/
|
|
2584
2665
|
export type ProjectUserCredentialExport = {
|
|
2585
2666
|
__typename?: 'ProjectUserCredentialExport';
|
|
2586
2667
|
users: Array<ExportedProjectUserCredential>;
|
|
@@ -2629,11 +2710,14 @@ export type Query = {
|
|
|
2629
2710
|
/** Public signup policy. No authentication required. */
|
|
2630
2711
|
authSignupPolicy: AuthSignupPolicy;
|
|
2631
2712
|
/**
|
|
2632
|
-
* Export project members'
|
|
2633
|
-
*
|
|
2634
|
-
*
|
|
2635
|
-
*
|
|
2636
|
-
*
|
|
2713
|
+
* Export project members' credentials for a downstream identity mirror: password hashes in CDM
|
|
2714
|
+
* shape (`PASSWORD`) and the plaintext secrets of their API keys (`API_KEY_SECRET`, only for keys
|
|
2715
|
+
* of a project that opted in to exportable API-key secrets). This is the one audited exception to
|
|
2716
|
+
* "APIs expose only whether a password exists" and to "an API-key secret is shown once": it
|
|
2717
|
+
* requires `ProjectUser:ExportCredentials` (held by no standard group), every call is audited
|
|
2718
|
+
* (one summary row, plus one row per exported API-key secret), and it returns credentials. Any
|
|
2719
|
+
* non-member in `userIds` fails the whole call; members without a project credential are listed
|
|
2720
|
+
* with `password: null`, and a key without a copy with `secret: null`.
|
|
2637
2721
|
*/
|
|
2638
2722
|
exportProjectUserCredentials: ProjectUserCredentialExport;
|
|
2639
2723
|
groups: GroupPage;
|
|
@@ -3633,6 +3717,19 @@ export type SetMyPrimaryMfaDeviceInput = {
|
|
|
3633
3717
|
factorId: Scalars['ID']['input'];
|
|
3634
3718
|
};
|
|
3635
3719
|
|
|
3720
|
+
export type SetProjectExportableApiKeySecretsInput = {
|
|
3721
|
+
/**
|
|
3722
|
+
* `true` opts the project in; `false` opts it out and deletes every exportable copy the project
|
|
3723
|
+
* holds. Setting the value the project already has changes nothing and is not audited.
|
|
3724
|
+
*/
|
|
3725
|
+
enabled: Scalars['Boolean']['input'];
|
|
3726
|
+
/**
|
|
3727
|
+
* The project to change, as the scope the request is authorized in: `accountProject` or
|
|
3728
|
+
* `organizationProject` only.
|
|
3729
|
+
*/
|
|
3730
|
+
scope: Scope;
|
|
3731
|
+
};
|
|
3732
|
+
|
|
3636
3733
|
export type SetProjectUserEmailInput = {
|
|
3637
3734
|
/**
|
|
3638
3735
|
* Unverified Email authentication method. Project-scope only.
|
|
@@ -3797,6 +3894,11 @@ export type SyncProjectResult = {
|
|
|
3797
3894
|
userRolesAssigned: Scalars['Int']['output'];
|
|
3798
3895
|
userTagsLinked: Scalars['Int']['output'];
|
|
3799
3896
|
usersCreated: Scalars['Int']['output'];
|
|
3897
|
+
/**
|
|
3898
|
+
* Document users not created because the person deleted this imported identity; their
|
|
3899
|
+
* membership, credential and API keys were not written either.
|
|
3900
|
+
*/
|
|
3901
|
+
usersSkippedTombstoned: Scalars['Int']['output'];
|
|
3800
3902
|
warnings: Array<Scalars['String']['output']>;
|
|
3801
3903
|
};
|
|
3802
3904
|
|
|
@@ -4217,6 +4319,13 @@ export type UpsertProjectOAuthConnectionInput = {
|
|
|
4217
4319
|
export type User = Auditable & {
|
|
4218
4320
|
__typename?: 'User';
|
|
4219
4321
|
accounts?: Maybe<Array<Account>>;
|
|
4322
|
+
/**
|
|
4323
|
+
* Non-secret facts about the user's live (not revoked, not deleted) project-user API keys in the
|
|
4324
|
+
* request's project scope, oldest first. `null` outside a project scope or when the caller lacks
|
|
4325
|
+
* `ProjectUser:ExportCredentials` (never an error); `[]` for a member with no key. The secret, the
|
|
4326
|
+
* hash and the encrypted copy are never returned here.
|
|
4327
|
+
*/
|
|
4328
|
+
apiKeyCredentials?: Maybe<Array<ProjectUserApiKeyCredential>>;
|
|
4220
4329
|
authenticationMethods?: Maybe<Array<UserAuthenticationMethod>>;
|
|
4221
4330
|
createdAt: Scalars['Date']['output'];
|
|
4222
4331
|
deletedAt?: Maybe<Scalars['Date']['output']>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
extend type Query {
|
|
2
2
|
"""
|
|
3
|
-
Export project members'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Export project members' credentials for a downstream identity mirror: password hashes in CDM
|
|
4
|
+
shape (`PASSWORD`) and the plaintext secrets of their API keys (`API_KEY_SECRET`, only for keys
|
|
5
|
+
of a project that opted in to exportable API-key secrets). This is the one audited exception to
|
|
6
|
+
"APIs expose only whether a password exists" and to "an API-key secret is shown once": it
|
|
7
|
+
requires `ProjectUser:ExportCredentials` (held by no standard group), every call is audited
|
|
8
|
+
(one summary row, plus one row per exported API-key secret), and it returns credentials. Any
|
|
9
|
+
non-member in `userIds` fails the whole call; members without a project credential are listed
|
|
10
|
+
with `password: null`, and a key without a copy with `secret: null`.
|
|
8
11
|
"""
|
|
9
12
|
exportProjectUserCredentials(
|
|
10
13
|
input: ExportProjectUserCredentialsInput!
|
|
@@ -5,4 +5,9 @@ not as a second export.
|
|
|
5
5
|
enum CredentialKind {
|
|
6
6
|
"The project password credential (bcrypt hash)."
|
|
7
7
|
PASSWORD
|
|
8
|
+
"""
|
|
9
|
+
The API keys of the project user, with the plaintext secret of each key the project keeps an
|
|
10
|
+
exportable copy of (a project that opted in to exportable API-key secrets).
|
|
11
|
+
"""
|
|
12
|
+
API_KEY_SECRET
|
|
8
13
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Non-secret facts about one of a project user's API keys. Never contains the secret, the hash or the
|
|
3
|
+
encrypted copy.
|
|
4
|
+
"""
|
|
5
|
+
type ProjectUserApiKeyCredential {
|
|
6
|
+
apiKeyId: ID!
|
|
7
|
+
"The key's public client id."
|
|
8
|
+
clientId: String!
|
|
9
|
+
"""
|
|
10
|
+
Lowercase hex SHA-256 of the secret string as issued (the text the client received, never the
|
|
11
|
+
stored hash): a change detector a holder of the secret can compare against. `null` for a key
|
|
12
|
+
created before this field existed and not rotated since. Grant issues secrets of 32 or 64 random
|
|
13
|
+
bytes, so the digest cannot be guessed offline; a secret chosen by an importer is only as strong
|
|
14
|
+
as the importer made it.
|
|
15
|
+
"""
|
|
16
|
+
fingerprint: String
|
|
17
|
+
"An encrypted copy of the key's current secret exists (only in a project that opted in)."
|
|
18
|
+
exportable: Boolean!
|
|
19
|
+
"When the key last changed (created, rotated or edited)."
|
|
20
|
+
updatedAt: Date!
|
|
21
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Result of `exportProjectUserCredentials`. This is the one place a credential hash
|
|
2
|
+
Result of `exportProjectUserCredentials`. This is the one place a credential hash, and the plaintext
|
|
3
|
+
secret of an exportable API key, leaves Grant.
|
|
3
4
|
"""
|
|
4
5
|
type ProjectUserCredentialExport {
|
|
5
6
|
users: [ExportedProjectUserCredential!]!
|
|
@@ -7,8 +8,34 @@ type ProjectUserCredentialExport {
|
|
|
7
8
|
|
|
8
9
|
type ExportedProjectUserCredential {
|
|
9
10
|
userId: ID!
|
|
10
|
-
"`null` when the member has no project password credential."
|
|
11
|
+
"`null` when the member has no project password credential, and when the `PASSWORD` kind was not requested."
|
|
11
12
|
password: ExportedPasswordCredential
|
|
13
|
+
"""
|
|
14
|
+
The member's live (not revoked, not deleted) project-user API keys, oldest first. `null` when the
|
|
15
|
+
`API_KEY_SECRET` kind was not requested; `[]` for a member with no key.
|
|
16
|
+
"""
|
|
17
|
+
apiKeySecrets: [ExportedApiKeySecret!]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
One API key of a project user in an export. `secret` is a credential: handle it like one.
|
|
22
|
+
"""
|
|
23
|
+
type ExportedApiKeySecret {
|
|
24
|
+
apiKeyId: ID!
|
|
25
|
+
"The key's public client id."
|
|
26
|
+
clientId: String!
|
|
27
|
+
"""
|
|
28
|
+
Lowercase hex SHA-256 of the secret string as issued, to compare with `sha256(secret)` of the
|
|
29
|
+
value a receiver holds. `null` for a key created before the fingerprint existed and not rotated
|
|
30
|
+
since.
|
|
31
|
+
"""
|
|
32
|
+
fingerprint: String
|
|
33
|
+
"""
|
|
34
|
+
The plaintext secret: 88 characters, standard base64 of 64 random bytes. `null` for a key the
|
|
35
|
+
project holds no copy of (created before the project opted in, created by a CDM import, or
|
|
36
|
+
rotated by a CDM replace): Grant cannot recover it.
|
|
37
|
+
"""
|
|
38
|
+
secret: String
|
|
12
39
|
}
|
|
13
40
|
|
|
14
41
|
"""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
input SetProjectExportableApiKeySecretsInput {
|
|
2
|
+
"""
|
|
3
|
+
The project to change, as the scope the request is authorized in: `accountProject` or
|
|
4
|
+
`organizationProject` only.
|
|
5
|
+
"""
|
|
6
|
+
scope: Scope!
|
|
7
|
+
"""
|
|
8
|
+
`true` opts the project in; `false` opts it out and deletes every exportable copy the project
|
|
9
|
+
holds. Setting the value the project already has changes nothing and is not audited.
|
|
10
|
+
"""
|
|
11
|
+
enabled: Boolean!
|
|
12
|
+
}
|
|
@@ -53,6 +53,11 @@ input CdmKeyResolverInput {
|
|
|
53
53
|
"""
|
|
54
54
|
findBy: CdmFindBy
|
|
55
55
|
ifMissing: CdmIfMissing
|
|
56
|
+
"""
|
|
57
|
+
Only with `findBy: key`: an existing, live Grant user this key is, reused instead of creating one and marked
|
|
58
|
+
with the key for this project. Refused for a missing, deleted or already-imported-under-another-key user.
|
|
59
|
+
"""
|
|
60
|
+
grantUserId: ID
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
"""
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
extend type Mutation {
|
|
2
|
+
"""
|
|
3
|
+
Opt a project in or out of keeping an exportable, encrypted copy of a project user's API-key
|
|
4
|
+
secret. Project scopes only. Requires `Project:SetExportableApiKeySecrets`, held by the Owner and
|
|
5
|
+
Admin roles and not by Dev. Enabling fails with a configuration error while
|
|
6
|
+
`API_KEY_EXPORTABLE_SECRET_ENCRYPTION_KEY` is not set; disabling never needs it. Each change is
|
|
7
|
+
audited on the project with the old and new value; the same value is a no-op. Nothing is
|
|
8
|
+
back-filled: keys that exist before the opt-in keep only their hash.
|
|
9
|
+
"""
|
|
10
|
+
setProjectExportableApiKeySecrets(input: SetProjectExportableApiKeySecretsInput!): Project!
|
|
11
|
+
}
|
|
@@ -12,6 +12,13 @@ type Project implements Auditable {
|
|
|
12
12
|
Whether the OAuth help panel is shown. Null uses Grant's default (shown).
|
|
13
13
|
"""
|
|
14
14
|
showHelpPanel: Boolean
|
|
15
|
+
"""
|
|
16
|
+
Whether this project keeps an exportable, encrypted copy of a project user's API-key secret at
|
|
17
|
+
create and rotate. Off by default; set with `setProjectExportableApiKeySecrets`. A plain
|
|
18
|
+
configuration flag: it says nothing about any key, and nothing is back-filled for keys that
|
|
19
|
+
already exist.
|
|
20
|
+
"""
|
|
21
|
+
exportableApiKeySecrets: Boolean!
|
|
15
22
|
roles: [Role!]
|
|
16
23
|
groups: [Group!]
|
|
17
24
|
permissions: [Permission!]
|
|
@@ -51,6 +51,11 @@ type SyncProjectResult {
|
|
|
51
51
|
reset in Grant, not imported.
|
|
52
52
|
"""
|
|
53
53
|
passwordsSkippedExisting: Int!
|
|
54
|
+
"""
|
|
55
|
+
Document users not created because the person deleted this imported identity; their
|
|
56
|
+
membership, credential and API keys were not written either.
|
|
57
|
+
"""
|
|
58
|
+
usersSkippedTombstoned: Int!
|
|
54
59
|
resourcesCreated: Int!
|
|
55
60
|
permissionsCreated: Int!
|
|
56
61
|
tagsCreated: Int!
|
|
@@ -21,6 +21,13 @@ type User implements Auditable {
|
|
|
21
21
|
stored hash string: a change detector, not a secret. The password and the hash are never returned here.
|
|
22
22
|
"""
|
|
23
23
|
passwordCredential: ProjectUserPasswordCredential
|
|
24
|
+
"""
|
|
25
|
+
Non-secret facts about the user's live (not revoked, not deleted) project-user API keys in the
|
|
26
|
+
request's project scope, oldest first. `null` outside a project scope or when the caller lacks
|
|
27
|
+
`ProjectUser:ExportCredentials` (never an error); `[]` for a member with no key. The secret, the
|
|
28
|
+
hash and the encrypted copy are never returned here.
|
|
29
|
+
"""
|
|
30
|
+
apiKeyCredentials: [ProjectUserApiKeyCredential!]
|
|
24
31
|
createdAt: Date!
|
|
25
32
|
updatedAt: Date!
|
|
26
33
|
deletedAt: Date
|
package/src/sdl-contract.test.ts
CHANGED
|
@@ -53,10 +53,11 @@ describe('operation documents', () => {
|
|
|
53
53
|
// 137 after CDM imports by reference: RequestProjectSyncUploadUrlDocument.
|
|
54
54
|
// 138 after operator-set-email: SetProjectUserEmailDocument.
|
|
55
55
|
// 140 after operator-set-password: SetProjectUserPasswordDocument, ExportProjectUserCredentialsDocument.
|
|
56
|
+
// 141 after exportable-api-key-secret opt-in: SetProjectExportableApiKeySecretsDocument.
|
|
56
57
|
// Updating this number is the point of the pin, not a nuisance: every change
|
|
57
58
|
// to it should be a line in a commit message.
|
|
58
59
|
it('finds every document under src/operations', () => {
|
|
59
|
-
expect(files).toHaveLength(
|
|
60
|
+
expect(files).toHaveLength(141);
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
it.each(files.map((f) => [relative(process.cwd(), f), f]))('%s validates', (_label, file) => {
|