@grantjs/schema 1.22.0 → 1.23.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grantjs/schema",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -154,6 +154,7 @@ export const EVENT_TYPES = [
154
154
  'project.user_email_changed',
155
155
  'project.user_password_set',
156
156
  'api_key.deleted',
157
+ 'api_key.updated',
157
158
  'tag.created',
158
159
  'tag.updated',
159
160
  'tag.deleted',
@@ -232,6 +233,7 @@ export const EVENT_CATALOG: Readonly<Record<EventType, EventCatalogEntry>> = {
232
233
  // Every project-credential write that changes the stored hash, any source. data.after = { projectId, userId, source }: never a hash or a password.
233
234
  'project.user_password_set': webhookOnly('membership'),
234
235
  'api_key.deleted': webhookOnly('security'),
236
+ 'api_key.updated': webhookOnly('security'),
235
237
  'tag.created': webhookOnly('iam'),
236
238
  'tag.updated': webhookOnly('iam'),
237
239
  'tag.deleted': webhookOnly('iam'),
@@ -50,6 +50,51 @@ export type ExchangeApiKeyMutationVariables = Exact<{
50
50
 
51
51
  export type ExchangeApiKeyMutation = { exchangeApiKey: { accessToken: string; expiresIn: number } };
52
52
 
53
+ export type GetApiKeyAttachableGrantsQueryVariables = Exact<{
54
+ scope: Types.Scope;
55
+ }>;
56
+
57
+ export type GetApiKeyAttachableGrantsQuery = {
58
+ apiKeyAttachableGrants: {
59
+ permissions: Array<{
60
+ machineOnly: boolean;
61
+ permission: {
62
+ id: string;
63
+ name: string;
64
+ action: string;
65
+ description: string | null;
66
+ resource: { id: string; slug: string; name: string } | null;
67
+ };
68
+ }>;
69
+ roles: Array<{ id: string; name: string; description: string | null }>;
70
+ };
71
+ };
72
+
73
+ export type GetApiKeyRolePermissionsQueryVariables = Exact<{
74
+ scope: Types.Scope;
75
+ ids?: Array<string | number> | string | number | null | undefined;
76
+ }>;
77
+
78
+ export type GetApiKeyRolePermissionsQuery = {
79
+ roles: {
80
+ roles: Array<{
81
+ id: string;
82
+ name: string;
83
+ groups: Array<{
84
+ id: string;
85
+ permissions: Array<{
86
+ id: string;
87
+ name: string;
88
+ action: string;
89
+ description: string | null;
90
+ resource: { id: string; slug: string; name: string } | null;
91
+ }> | null;
92
+ }> | null;
93
+ rolePermissions: Array<{ id: string; permissionId: string }> | null;
94
+ }>;
95
+ };
96
+ };
97
+
53
98
  export type GetApiKeysQueryVariables = Exact<{
54
99
  scope: Types.Scope;
55
100
  page?: number | null | undefined;
@@ -78,6 +123,13 @@ export type GetApiKeysQuery = {
78
123
  updatedAt: Date;
79
124
  deletedAt: Date | null;
80
125
  role: { id: string; name: string } | null;
126
+ permissions: Array<{
127
+ id: string;
128
+ name: string;
129
+ action: string;
130
+ description: string | null;
131
+ resource: { id: string; slug: string; name: string } | null;
132
+ }> | null;
81
133
  createdByUser: { id: string; name: string; pictureUrl: string | null } | null;
82
134
  revokedByUser: { id: string; name: string; pictureUrl: string | null } | null;
83
135
  }>;
@@ -122,6 +174,31 @@ export type RotateApiKeyMutation = {
122
174
  };
123
175
  };
124
176
 
177
+ export type SetApiKeyPermissionsMutationVariables = Exact<{
178
+ input: Types.SetApiKeyPermissionsInput;
179
+ }>;
180
+
181
+ export type SetApiKeyPermissionsMutation = {
182
+ setApiKeyPermissions: {
183
+ id: string;
184
+ clientId: string;
185
+ name: string | null;
186
+ description: string | null;
187
+ expiresAt: Date | null;
188
+ lastUsedAt: Date | null;
189
+ isRevoked: boolean;
190
+ updatedAt: Date;
191
+ role: { id: string; name: string } | null;
192
+ permissions: Array<{
193
+ id: string;
194
+ name: string;
195
+ action: string;
196
+ description: string | null;
197
+ resource: { id: string; slug: string; name: string } | null;
198
+ }> | null;
199
+ };
200
+ };
201
+
125
202
  export type AuthSignupPolicyQueryVariables = Exact<{ [key: string]: never }>;
126
203
 
127
204
  export type AuthSignupPolicyQuery = {
@@ -2530,6 +2607,221 @@ export const ExchangeApiKeyDocument = {
2530
2607
  },
2531
2608
  ],
2532
2609
  } as unknown as DocumentNode<ExchangeApiKeyMutation, ExchangeApiKeyMutationVariables>;
2610
+ export const GetApiKeyAttachableGrantsDocument = {
2611
+ kind: 'Document',
2612
+ definitions: [
2613
+ {
2614
+ kind: 'OperationDefinition',
2615
+ operation: 'query',
2616
+ name: { kind: 'Name', value: 'GetApiKeyAttachableGrants' },
2617
+ variableDefinitions: [
2618
+ {
2619
+ kind: 'VariableDefinition',
2620
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'scope' } },
2621
+ type: {
2622
+ kind: 'NonNullType',
2623
+ type: { kind: 'NamedType', name: { kind: 'Name', value: 'Scope' } },
2624
+ },
2625
+ },
2626
+ ],
2627
+ selectionSet: {
2628
+ kind: 'SelectionSet',
2629
+ selections: [
2630
+ {
2631
+ kind: 'Field',
2632
+ name: { kind: 'Name', value: 'apiKeyAttachableGrants' },
2633
+ arguments: [
2634
+ {
2635
+ kind: 'Argument',
2636
+ name: { kind: 'Name', value: 'scope' },
2637
+ value: { kind: 'Variable', name: { kind: 'Name', value: 'scope' } },
2638
+ },
2639
+ ],
2640
+ selectionSet: {
2641
+ kind: 'SelectionSet',
2642
+ selections: [
2643
+ {
2644
+ kind: 'Field',
2645
+ name: { kind: 'Name', value: 'permissions' },
2646
+ selectionSet: {
2647
+ kind: 'SelectionSet',
2648
+ selections: [
2649
+ { kind: 'Field', name: { kind: 'Name', value: 'machineOnly' } },
2650
+ {
2651
+ kind: 'Field',
2652
+ name: { kind: 'Name', value: 'permission' },
2653
+ selectionSet: {
2654
+ kind: 'SelectionSet',
2655
+ selections: [
2656
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2657
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2658
+ { kind: 'Field', name: { kind: 'Name', value: 'action' } },
2659
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
2660
+ {
2661
+ kind: 'Field',
2662
+ name: { kind: 'Name', value: 'resource' },
2663
+ selectionSet: {
2664
+ kind: 'SelectionSet',
2665
+ selections: [
2666
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2667
+ { kind: 'Field', name: { kind: 'Name', value: 'slug' } },
2668
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2669
+ ],
2670
+ },
2671
+ },
2672
+ ],
2673
+ },
2674
+ },
2675
+ ],
2676
+ },
2677
+ },
2678
+ {
2679
+ kind: 'Field',
2680
+ name: { kind: 'Name', value: 'roles' },
2681
+ selectionSet: {
2682
+ kind: 'SelectionSet',
2683
+ selections: [
2684
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2685
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2686
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
2687
+ ],
2688
+ },
2689
+ },
2690
+ ],
2691
+ },
2692
+ },
2693
+ ],
2694
+ },
2695
+ },
2696
+ ],
2697
+ } as unknown as DocumentNode<
2698
+ GetApiKeyAttachableGrantsQuery,
2699
+ GetApiKeyAttachableGrantsQueryVariables
2700
+ >;
2701
+ export const GetApiKeyRolePermissionsDocument = {
2702
+ kind: 'Document',
2703
+ definitions: [
2704
+ {
2705
+ kind: 'OperationDefinition',
2706
+ operation: 'query',
2707
+ name: { kind: 'Name', value: 'GetApiKeyRolePermissions' },
2708
+ variableDefinitions: [
2709
+ {
2710
+ kind: 'VariableDefinition',
2711
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'scope' } },
2712
+ type: {
2713
+ kind: 'NonNullType',
2714
+ type: { kind: 'NamedType', name: { kind: 'Name', value: 'Scope' } },
2715
+ },
2716
+ },
2717
+ {
2718
+ kind: 'VariableDefinition',
2719
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'ids' } },
2720
+ type: {
2721
+ kind: 'ListType',
2722
+ type: {
2723
+ kind: 'NonNullType',
2724
+ type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
2725
+ },
2726
+ },
2727
+ },
2728
+ ],
2729
+ selectionSet: {
2730
+ kind: 'SelectionSet',
2731
+ selections: [
2732
+ {
2733
+ kind: 'Field',
2734
+ name: { kind: 'Name', value: 'roles' },
2735
+ arguments: [
2736
+ {
2737
+ kind: 'Argument',
2738
+ name: { kind: 'Name', value: 'scope' },
2739
+ value: { kind: 'Variable', name: { kind: 'Name', value: 'scope' } },
2740
+ },
2741
+ {
2742
+ kind: 'Argument',
2743
+ name: { kind: 'Name', value: 'ids' },
2744
+ value: { kind: 'Variable', name: { kind: 'Name', value: 'ids' } },
2745
+ },
2746
+ {
2747
+ kind: 'Argument',
2748
+ name: { kind: 'Name', value: 'limit' },
2749
+ value: { kind: 'IntValue', value: '1' },
2750
+ },
2751
+ {
2752
+ kind: 'Argument',
2753
+ name: { kind: 'Name', value: 'page' },
2754
+ value: { kind: 'IntValue', value: '1' },
2755
+ },
2756
+ ],
2757
+ selectionSet: {
2758
+ kind: 'SelectionSet',
2759
+ selections: [
2760
+ {
2761
+ kind: 'Field',
2762
+ name: { kind: 'Name', value: 'roles' },
2763
+ selectionSet: {
2764
+ kind: 'SelectionSet',
2765
+ selections: [
2766
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2767
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2768
+ {
2769
+ kind: 'Field',
2770
+ name: { kind: 'Name', value: 'groups' },
2771
+ selectionSet: {
2772
+ kind: 'SelectionSet',
2773
+ selections: [
2774
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2775
+ {
2776
+ kind: 'Field',
2777
+ name: { kind: 'Name', value: 'permissions' },
2778
+ selectionSet: {
2779
+ kind: 'SelectionSet',
2780
+ selections: [
2781
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2782
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2783
+ { kind: 'Field', name: { kind: 'Name', value: 'action' } },
2784
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
2785
+ {
2786
+ kind: 'Field',
2787
+ name: { kind: 'Name', value: 'resource' },
2788
+ selectionSet: {
2789
+ kind: 'SelectionSet',
2790
+ selections: [
2791
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2792
+ { kind: 'Field', name: { kind: 'Name', value: 'slug' } },
2793
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2794
+ ],
2795
+ },
2796
+ },
2797
+ ],
2798
+ },
2799
+ },
2800
+ ],
2801
+ },
2802
+ },
2803
+ {
2804
+ kind: 'Field',
2805
+ name: { kind: 'Name', value: 'rolePermissions' },
2806
+ selectionSet: {
2807
+ kind: 'SelectionSet',
2808
+ selections: [
2809
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2810
+ { kind: 'Field', name: { kind: 'Name', value: 'permissionId' } },
2811
+ ],
2812
+ },
2813
+ },
2814
+ ],
2815
+ },
2816
+ },
2817
+ ],
2818
+ },
2819
+ },
2820
+ ],
2821
+ },
2822
+ },
2823
+ ],
2824
+ } as unknown as DocumentNode<GetApiKeyRolePermissionsQuery, GetApiKeyRolePermissionsQueryVariables>;
2533
2825
  export const GetApiKeysDocument = {
2534
2826
  kind: 'Document',
2535
2827
  definitions: [
@@ -2649,6 +2941,31 @@ export const GetApiKeysDocument = {
2649
2941
  ],
2650
2942
  },
2651
2943
  },
2944
+ {
2945
+ kind: 'Field',
2946
+ name: { kind: 'Name', value: 'permissions' },
2947
+ selectionSet: {
2948
+ kind: 'SelectionSet',
2949
+ selections: [
2950
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2951
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2952
+ { kind: 'Field', name: { kind: 'Name', value: 'action' } },
2953
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
2954
+ {
2955
+ kind: 'Field',
2956
+ name: { kind: 'Name', value: 'resource' },
2957
+ selectionSet: {
2958
+ kind: 'SelectionSet',
2959
+ selections: [
2960
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
2961
+ { kind: 'Field', name: { kind: 'Name', value: 'slug' } },
2962
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
2963
+ ],
2964
+ },
2965
+ },
2966
+ ],
2967
+ },
2968
+ },
2652
2969
  {
2653
2970
  kind: 'Field',
2654
2971
  name: { kind: 'Name', value: 'createdByUser' },
@@ -2788,6 +3105,91 @@ export const RotateApiKeyDocument = {
2788
3105
  },
2789
3106
  ],
2790
3107
  } as unknown as DocumentNode<RotateApiKeyMutation, RotateApiKeyMutationVariables>;
3108
+ export const SetApiKeyPermissionsDocument = {
3109
+ kind: 'Document',
3110
+ definitions: [
3111
+ {
3112
+ kind: 'OperationDefinition',
3113
+ operation: 'mutation',
3114
+ name: { kind: 'Name', value: 'SetApiKeyPermissions' },
3115
+ variableDefinitions: [
3116
+ {
3117
+ kind: 'VariableDefinition',
3118
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
3119
+ type: {
3120
+ kind: 'NonNullType',
3121
+ type: { kind: 'NamedType', name: { kind: 'Name', value: 'SetApiKeyPermissionsInput' } },
3122
+ },
3123
+ },
3124
+ ],
3125
+ selectionSet: {
3126
+ kind: 'SelectionSet',
3127
+ selections: [
3128
+ {
3129
+ kind: 'Field',
3130
+ name: { kind: 'Name', value: 'setApiKeyPermissions' },
3131
+ arguments: [
3132
+ {
3133
+ kind: 'Argument',
3134
+ name: { kind: 'Name', value: 'input' },
3135
+ value: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
3136
+ },
3137
+ ],
3138
+ selectionSet: {
3139
+ kind: 'SelectionSet',
3140
+ selections: [
3141
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
3142
+ { kind: 'Field', name: { kind: 'Name', value: 'clientId' } },
3143
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
3144
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
3145
+ { kind: 'Field', name: { kind: 'Name', value: 'expiresAt' } },
3146
+ { kind: 'Field', name: { kind: 'Name', value: 'lastUsedAt' } },
3147
+ { kind: 'Field', name: { kind: 'Name', value: 'isRevoked' } },
3148
+ { kind: 'Field', name: { kind: 'Name', value: 'updatedAt' } },
3149
+ {
3150
+ kind: 'Field',
3151
+ name: { kind: 'Name', value: 'role' },
3152
+ selectionSet: {
3153
+ kind: 'SelectionSet',
3154
+ selections: [
3155
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
3156
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
3157
+ ],
3158
+ },
3159
+ },
3160
+ {
3161
+ kind: 'Field',
3162
+ name: { kind: 'Name', value: 'permissions' },
3163
+ selectionSet: {
3164
+ kind: 'SelectionSet',
3165
+ selections: [
3166
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
3167
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
3168
+ { kind: 'Field', name: { kind: 'Name', value: 'action' } },
3169
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
3170
+ {
3171
+ kind: 'Field',
3172
+ name: { kind: 'Name', value: 'resource' },
3173
+ selectionSet: {
3174
+ kind: 'SelectionSet',
3175
+ selections: [
3176
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
3177
+ { kind: 'Field', name: { kind: 'Name', value: 'slug' } },
3178
+ { kind: 'Field', name: { kind: 'Name', value: 'name' } },
3179
+ ],
3180
+ },
3181
+ },
3182
+ ],
3183
+ },
3184
+ },
3185
+ ],
3186
+ },
3187
+ },
3188
+ ],
3189
+ },
3190
+ },
3191
+ ],
3192
+ } as unknown as DocumentNode<SetApiKeyPermissionsMutation, SetApiKeyPermissionsMutationVariables>;
2791
3193
  export const AuthSignupPolicyDocument = {
2792
3194
  kind: 'Document',
2793
3195
  definitions: [
@@ -219,6 +219,8 @@ export type ResolversTypes = ResolversObject<{
219
219
  AddUserRoleInput: Types.AddUserRoleInput;
220
220
  AddUserTagInput: Types.AddUserTagInput;
221
221
  ApiKey: ResolverTypeWrapper<Types.ApiKey>;
222
+ ApiKeyAttachableGrants: ResolverTypeWrapper<Types.ApiKeyAttachableGrants>;
223
+ ApiKeyAttachablePermission: ResolverTypeWrapper<Types.ApiKeyAttachablePermission>;
222
224
  ApiKeyPage: ResolverTypeWrapper<Types.ApiKeyPage>;
223
225
  ApiKeySearchableField: Types.ApiKeySearchableField;
224
226
  ApiKeySortInput: Types.ApiKeySortInput;
@@ -507,6 +509,7 @@ export type ResolversTypes = ResolversObject<{
507
509
  Scope: Types.Scope;
508
510
  Searchable: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['Searchable']>;
509
511
  SessionExportData: ResolverTypeWrapper<Types.SessionExportData>;
512
+ SetApiKeyPermissionsInput: Types.SetApiKeyPermissionsInput;
510
513
  SetMyNotificationPreferenceInput: Types.SetMyNotificationPreferenceInput;
511
514
  SetMyPrimaryMfaDeviceInput: Types.SetMyPrimaryMfaDeviceInput;
512
515
  SetProjectExportableApiKeySecretsInput: Types.SetProjectExportableApiKeySecretsInput;
@@ -649,6 +652,8 @@ export type ResolversParentTypes = ResolversObject<{
649
652
  AddUserRoleInput: Types.AddUserRoleInput;
650
653
  AddUserTagInput: Types.AddUserTagInput;
651
654
  ApiKey: Types.ApiKey;
655
+ ApiKeyAttachableGrants: Types.ApiKeyAttachableGrants;
656
+ ApiKeyAttachablePermission: Types.ApiKeyAttachablePermission;
652
657
  ApiKeyPage: Types.ApiKeyPage;
653
658
  ApiKeySortInput: Types.ApiKeySortInput;
654
659
  AssignRolePermissionInput: Types.AssignRolePermissionInput;
@@ -899,6 +904,7 @@ export type ResolversParentTypes = ResolversObject<{
899
904
  Scope: Types.Scope;
900
905
  Searchable: ResolversInterfaceTypes<ResolversParentTypes>['Searchable'];
901
906
  SessionExportData: Types.SessionExportData;
907
+ SetApiKeyPermissionsInput: Types.SetApiKeyPermissionsInput;
902
908
  SetMyNotificationPreferenceInput: Types.SetMyNotificationPreferenceInput;
903
909
  SetMyPrimaryMfaDeviceInput: Types.SetMyPrimaryMfaDeviceInput;
904
910
  SetProjectExportableApiKeySecretsInput: Types.SetProjectExportableApiKeySecretsInput;
@@ -1136,6 +1142,7 @@ export type ApiKeyResolvers<
1136
1142
  isRevoked?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
1137
1143
  lastUsedAt?: Resolver<Types.Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
1138
1144
  name?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
1145
+ permissions?: Resolver<Types.Maybe<Array<ResolversTypes['Permission']>>, ParentType, ContextType>;
1139
1146
  revokedAt?: Resolver<Types.Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
1140
1147
  revokedBy?: Resolver<Types.Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
1141
1148
  revokedByUser?: Resolver<Types.Maybe<ResolversTypes['User']>, ParentType, ContextType>;
@@ -1144,6 +1151,28 @@ export type ApiKeyResolvers<
1144
1151
  __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
1145
1152
  }>;
1146
1153
 
1154
+ export type ApiKeyAttachableGrantsResolvers<
1155
+ ContextType = any,
1156
+ ParentType extends ResolversParentTypes['ApiKeyAttachableGrants'] =
1157
+ ResolversParentTypes['ApiKeyAttachableGrants'],
1158
+ > = ResolversObject<{
1159
+ permissions?: Resolver<
1160
+ Array<ResolversTypes['ApiKeyAttachablePermission']>,
1161
+ ParentType,
1162
+ ContextType
1163
+ >;
1164
+ roles?: Resolver<Array<ResolversTypes['Role']>, ParentType, ContextType>;
1165
+ }>;
1166
+
1167
+ export type ApiKeyAttachablePermissionResolvers<
1168
+ ContextType = any,
1169
+ ParentType extends ResolversParentTypes['ApiKeyAttachablePermission'] =
1170
+ ResolversParentTypes['ApiKeyAttachablePermission'],
1171
+ > = ResolversObject<{
1172
+ machineOnly?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
1173
+ permission?: Resolver<ResolversTypes['Permission'], ParentType, ContextType>;
1174
+ }>;
1175
+
1147
1176
  export type ApiKeyPageResolvers<
1148
1177
  ContextType = any,
1149
1178
  ParentType extends ResolversParentTypes['ApiKeyPage'] = ResolversParentTypes['ApiKeyPage'],
@@ -2001,6 +2030,12 @@ export type MutationResolvers<
2001
2030
  ContextType,
2002
2031
  RequireFields<Types.MutationRotateWebhookSubscriptionSecretArgs, 'input'>
2003
2032
  >;
2033
+ setApiKeyPermissions?: Resolver<
2034
+ ResolversTypes['ApiKey'],
2035
+ ParentType,
2036
+ ContextType,
2037
+ RequireFields<Types.MutationSetApiKeyPermissionsArgs, 'input'>
2038
+ >;
2004
2039
  setMyNotificationPreference?: Resolver<
2005
2040
  ResolversTypes['NotificationPreference'],
2006
2041
  ParentType,
@@ -2981,6 +3016,12 @@ export type QueryResolvers<
2981
3016
  ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query'],
2982
3017
  > = ResolversObject<{
2983
3018
  _empty?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
3019
+ apiKeyAttachableGrants?: Resolver<
3020
+ ResolversTypes['ApiKeyAttachableGrants'],
3021
+ ParentType,
3022
+ ContextType,
3023
+ RequireFields<Types.QueryApiKeyAttachableGrantsArgs, 'scope'>
3024
+ >;
2984
3025
  apiKeys?: Resolver<
2985
3026
  ResolversTypes['ApiKeyPage'],
2986
3027
  ParentType,
@@ -3870,6 +3911,8 @@ export type Resolvers<ContextType = any> = ResolversObject<{
3870
3911
  AccountRole?: AccountRoleResolvers<ContextType>;
3871
3912
  AccountTag?: AccountTagResolvers<ContextType>;
3872
3913
  ApiKey?: ApiKeyResolvers<ContextType>;
3914
+ ApiKeyAttachableGrants?: ApiKeyAttachableGrantsResolvers<ContextType>;
3915
+ ApiKeyAttachablePermission?: ApiKeyAttachablePermissionResolvers<ContextType>;
3873
3916
  ApiKeyPage?: ApiKeyPageResolvers<ContextType>;
3874
3917
  Auditable?: AuditableResolvers<ContextType>;
3875
3918
  AuthSignupPolicy?: AuthSignupPolicyResolvers<ContextType>;
@@ -340,14 +340,29 @@ export type ApiKey = Auditable & {
340
340
  isRevoked: Scalars['Boolean']['output'];
341
341
  lastUsedAt?: Maybe<Scalars['Date']['output']>;
342
342
  name?: Maybe<Scalars['String']['output']>;
343
+ /** Permissions this API key carries instead of a role (project-level keys only). Null for user-scoped keys and for role keys. */
344
+ permissions?: Maybe<Array<Permission>>;
343
345
  revokedAt?: Maybe<Scalars['Date']['output']>;
344
346
  revokedBy?: Maybe<Scalars['ID']['output']>;
345
347
  revokedByUser?: Maybe<User>;
346
- /** Role bound to this API key (project-level keys only). Null for user-scoped keys. */
348
+ /** Standard role bound to this API key (project-level keys only). Null for user-scoped keys and for keys that carry permissions. */
347
349
  role?: Maybe<Role>;
348
350
  updatedAt: Scalars['Date']['output'];
349
351
  };
350
352
 
353
+ export type ApiKeyAttachableGrants = {
354
+ __typename?: 'ApiKeyAttachableGrants';
355
+ permissions: Array<ApiKeyAttachablePermission>;
356
+ roles: Array<Role>;
357
+ };
358
+
359
+ export type ApiKeyAttachablePermission = {
360
+ __typename?: 'ApiKeyAttachablePermission';
361
+ /** True for a permission meant for credentials only (such as ProjectUser:ExportCredentials): it can be given to a key by a holder of its gate permission, without holding it. */
362
+ machineOnly: Scalars['Boolean']['output'];
363
+ permission: Permission;
364
+ };
365
+
351
366
  export type ApiKeyPage = PaginatedResults & {
352
367
  __typename?: 'ApiKeyPage';
353
368
  apiKeys: Array<ApiKey>;
@@ -610,7 +625,9 @@ export type CreateApiKeyInput = {
610
625
  description?: InputMaybe<Scalars['String']['input']>;
611
626
  expiresAt?: InputMaybe<Scalars['Date']['input']>;
612
627
  name?: InputMaybe<Scalars['String']['input']>;
613
- /** Required when scope.tenant is accountProject or organizationProject: the parent-tenant role the key impersonates. */
628
+ /** For a project key: the permissions the key carries instead of a role. Give exactly one of `roleId` or `permissionIds`. Each must be covered by what you can do in this scope, except machine-only permissions, which a holder of their gate permission may attach. Not allowed for other scopes. */
629
+ permissionIds?: InputMaybe<Array<Scalars['ID']['input']>>;
630
+ /** For a project key (scope.tenant is accountProject or organizationProject): the standard role the key carries. Give exactly one of `roleId` or `permissionIds`. An account project key with neither carries the creator's account role. The role's permissions must be covered by what you can do in this scope. */
614
631
  roleId?: InputMaybe<Scalars['ID']['input']>;
615
632
  scope: Scope;
616
633
  };
@@ -1356,6 +1373,8 @@ export type Mutation = {
1356
1373
  rotateSigningKey: SigningKey;
1357
1374
  /** Rotate the signing secret. The new secret is returned once. */
1358
1375
  rotateWebhookSubscriptionSecret: WebhookSubscriptionWithSecret;
1376
+ /** Replace the permissions a project API key carries. Applied to a role key it converts the key to permissions; the conversion is one way. The key keeps its id, clientId and secret, and the change applies to its next request. Refuses a revoked key. */
1377
+ setApiKeyPermissions: ApiKey;
1359
1378
  setMyNotificationPreference: NotificationPreference;
1360
1379
  setMyPrimaryAuthenticationMethod: UserAuthenticationMethod;
1361
1380
  setMyPrimaryMfaDevice: MfaDevice;
@@ -1731,6 +1750,10 @@ export type MutationRotateWebhookSubscriptionSecretArgs = {
1731
1750
  input: RotateWebhookSubscriptionSecretInput;
1732
1751
  };
1733
1752
 
1753
+ export type MutationSetApiKeyPermissionsArgs = {
1754
+ input: SetApiKeyPermissionsInput;
1755
+ };
1756
+
1734
1757
  export type MutationSetMyNotificationPreferenceArgs = {
1735
1758
  input: SetMyNotificationPreferenceInput;
1736
1759
  };
@@ -2706,6 +2729,8 @@ export type ProjectUserPermission = Auditable & {
2706
2729
  export type Query = {
2707
2730
  __typename?: 'Query';
2708
2731
  _empty?: Maybe<Scalars['String']['output']>;
2732
+ /** What the caller may give a new project API key in this scope: the permissions (machine-only ones flagged) and the standard roles whose permissions the caller covers. Project scopes only. */
2733
+ apiKeyAttachableGrants: ApiKeyAttachableGrants;
2709
2734
  apiKeys: ApiKeyPage;
2710
2735
  /** Public signup policy. No authentication required. */
2711
2736
  authSignupPolicy: AuthSignupPolicy;
@@ -2779,6 +2804,10 @@ export type Query = {
2779
2804
  webhookSubscriptions: Array<WebhookSubscription>;
2780
2805
  };
2781
2806
 
2807
+ export type QueryApiKeyAttachableGrantsArgs = {
2808
+ scope: Scope;
2809
+ };
2810
+
2782
2811
  export type QueryApiKeysArgs = {
2783
2812
  ids?: InputMaybe<Array<Scalars['ID']['input']>>;
2784
2813
  limit?: InputMaybe<Scalars['Int']['input']>;
@@ -3704,6 +3733,13 @@ export type SessionExportData = {
3704
3733
  userAgent?: Maybe<Scalars['String']['output']>;
3705
3734
  };
3706
3735
 
3736
+ export type SetApiKeyPermissionsInput = {
3737
+ id: Scalars['ID']['input'];
3738
+ /** The complete set of permissions the key carries afterwards; it may not be empty. Permissions you add must be covered by what you can do in this scope (machine-only permissions: see `apiKeyAttachableGrants`); permissions you leave out are removed. */
3739
+ permissionIds: Array<Scalars['ID']['input']>;
3740
+ scope: Scope;
3741
+ };
3742
+
3707
3743
  export type SetMyNotificationPreferenceInput = {
3708
3744
  /** Notification preference category (security, iam, membership, integrations). */
3709
3745
  category: Scalars['String']['input'];
@@ -0,0 +1,23 @@
1
+ query GetApiKeyAttachableGrants($scope: Scope!) {
2
+ apiKeyAttachableGrants(scope: $scope) {
3
+ permissions {
4
+ machineOnly
5
+ permission {
6
+ id
7
+ name
8
+ action
9
+ description
10
+ resource {
11
+ id
12
+ slug
13
+ name
14
+ }
15
+ }
16
+ }
17
+ roles {
18
+ id
19
+ name
20
+ description
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,28 @@
1
+ query GetApiKeyRolePermissions($scope: Scope!, $ids: [ID!]) {
2
+ roles(scope: $scope, ids: $ids, limit: 1, page: 1) {
3
+ roles {
4
+ id
5
+ name
6
+ # The permissions a role key resolves to come from the role's groups. `groups(scope)` does not
7
+ # list the standard groups, so they are read through the role.
8
+ groups {
9
+ id
10
+ permissions {
11
+ id
12
+ name
13
+ action
14
+ description
15
+ resource {
16
+ id
17
+ slug
18
+ name
19
+ }
20
+ }
21
+ }
22
+ rolePermissions {
23
+ id
24
+ permissionId
25
+ }
26
+ }
27
+ }
28
+ }
@@ -25,6 +25,17 @@ query GetApiKeys(
25
25
  id
26
26
  name
27
27
  }
28
+ permissions {
29
+ id
30
+ name
31
+ action
32
+ description
33
+ resource {
34
+ id
35
+ slug
36
+ name
37
+ }
38
+ }
28
39
  createdByUser {
29
40
  id
30
41
  name
@@ -0,0 +1,27 @@
1
+ mutation SetApiKeyPermissions($input: SetApiKeyPermissionsInput!) {
2
+ setApiKeyPermissions(input: $input) {
3
+ id
4
+ clientId
5
+ name
6
+ description
7
+ expiresAt
8
+ lastUsedAt
9
+ isRevoked
10
+ updatedAt
11
+ role {
12
+ id
13
+ name
14
+ }
15
+ permissions {
16
+ id
17
+ name
18
+ action
19
+ description
20
+ resource {
21
+ id
22
+ slug
23
+ name
24
+ }
25
+ }
26
+ }
27
+ }
@@ -4,7 +4,11 @@ input CreateApiKeyInput {
4
4
  expiresAt: Date
5
5
  scope: Scope!
6
6
  """
7
- Required when scope.tenant is accountProject or organizationProject: the parent-tenant role the key impersonates.
7
+ For a project key (scope.tenant is accountProject or organizationProject): the standard role the key carries. Give exactly one of `roleId` or `permissionIds`. An account project key with neither carries the creator's account role. The role's permissions must be covered by what you can do in this scope.
8
8
  """
9
9
  roleId: ID
10
+ """
11
+ For a project key: the permissions the key carries instead of a role. Give exactly one of `roleId` or `permissionIds`. Each must be covered by what you can do in this scope, except machine-only permissions, which a holder of their gate permission may attach. Not allowed for other scopes.
12
+ """
13
+ permissionIds: [ID!]
10
14
  }
@@ -0,0 +1,8 @@
1
+ input SetApiKeyPermissionsInput {
2
+ scope: Scope!
3
+ id: ID!
4
+ """
5
+ The complete set of permissions the key carries afterwards; it may not be empty. Permissions you add must be covered by what you can do in this scope (machine-only permissions: see `apiKeyAttachableGrants`); permissions you leave out are removed.
6
+ """
7
+ permissionIds: [ID!]!
8
+ }
@@ -0,0 +1,6 @@
1
+ extend type Mutation {
2
+ """
3
+ Replace the permissions a project API key carries. Applied to a role key it converts the key to permissions; the conversion is one way. The key keeps its id, clientId and secret, and the change applies to its next request. Refuses a revoked key.
4
+ """
5
+ setApiKeyPermissions(input: SetApiKeyPermissionsInput!): ApiKey!
6
+ }
@@ -0,0 +1,6 @@
1
+ extend type Query {
2
+ """
3
+ What the caller may give a new project API key in this scope: the permissions (machine-only ones flagged) and the standard roles whose permissions the caller covers. Project scopes only.
4
+ """
5
+ apiKeyAttachableGrants(scope: Scope!): ApiKeyAttachableGrants!
6
+ }
@@ -0,0 +1,4 @@
1
+ type ApiKeyAttachableGrants {
2
+ permissions: [ApiKeyAttachablePermission!]!
3
+ roles: [Role!]!
4
+ }
@@ -0,0 +1,7 @@
1
+ type ApiKeyAttachablePermission {
2
+ permission: Permission!
3
+ """
4
+ True for a permission meant for credentials only (such as ProjectUser:ExportCredentials): it can be given to a key by a holder of its gate permission, without holding it.
5
+ """
6
+ machineOnly: Boolean!
7
+ }
@@ -15,7 +15,11 @@ type ApiKey implements Auditable {
15
15
  createdByUser: User
16
16
  revokedByUser: User
17
17
  """
18
- Role bound to this API key (project-level keys only). Null for user-scoped keys.
18
+ Standard role bound to this API key (project-level keys only). Null for user-scoped keys and for keys that carry permissions.
19
19
  """
20
20
  role: Role
21
+ """
22
+ Permissions this API key carries instead of a role (project-level keys only). Null for user-scoped keys and for role keys.
23
+ """
24
+ permissions: [Permission!]
21
25
  }
@@ -54,10 +54,12 @@ describe('operation documents', () => {
54
54
  // 138 after operator-set-email: SetProjectUserEmailDocument.
55
55
  // 140 after operator-set-password: SetProjectUserPasswordDocument, ExportProjectUserCredentialsDocument.
56
56
  // 141 after exportable-api-key-secret opt-in: SetProjectExportableApiKeySecretsDocument.
57
+ // 144 after api-key scopes web: GetApiKeyAttachableGrantsDocument, SetApiKeyPermissionsDocument,
58
+ // GetApiKeyRolePermissionsDocument.
57
59
  // Updating this number is the point of the pin, not a nuisance: every change
58
60
  // to it should be a line in a commit message.
59
61
  it('finds every document under src/operations', () => {
60
- expect(files).toHaveLength(141);
62
+ expect(files).toHaveLength(144);
61
63
  });
62
64
 
63
65
  it.each(files.map((f) => [relative(process.cwd(), f), f]))('%s validates', (_label, file) => {