From 52a877fcfacd23a17b46dc2f80c19b145364eb81 Mon Sep 17 00:00:00 2001 From: Nimish Date: Fri, 22 Aug 2025 23:22:55 +0530 Subject: [PATCH 01/80] feat: enhance ServiceAccountToken model with creator validation and relationship - Added `created_by_service_account` field to associate tokens with service accounts. - Implemented validation in `clean` method to ensure only one creator field is set. - Introduced `get_creator_account` method to retrieve the creator, whether an organisation member or a service account. --- backend/api/models.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/backend/api/models.py b/backend/api/models.py index e71b18ac8..8c5ec8c8c 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -556,11 +556,35 @@ class ServiceAccountToken(models.Model): created_by = models.ForeignKey( OrganisationMember, on_delete=models.CASCADE, blank=True, null=True ) + created_by_service_account = models.ForeignKey( + ServiceAccount, + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="created_tokens", + ) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(blank=True, null=True) expires_at = models.DateTimeField(null=True) + def clean(self): + # Ensure only one of created_by or created_by_service_account is set + # Service accounts can create tokens for themselves and others + if not (self.created_by or self.created_by_service_account): + from django.core.exceptions import ValidationError + raise ValidationError( + "Must set either created_by (organisation member) or created_by_service_account" + ) + if self.created_by and self.created_by_service_account: + from django.core.exceptions import ValidationError + raise ValidationError( + "Only one of created_by or created_by_service_account may be set" + ) + + def get_creator_account(self): + return self.created_by or self.created_by_service_account + def delete(self, *args, **kwargs): """ Soft delete the object by setting the 'deleted_at' field. From e9d91ba6927d49c785c02f2eeb04731a7fe9c32d Mon Sep 17 00:00:00 2001 From: Nimish Date: Fri, 22 Aug 2025 23:23:15 +0530 Subject: [PATCH 02/80] fix: update ServiceAccountTokenSerializer to use ServiceAccountToken model --- backend/api/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 7c8fd5d90..a8cc4b3e5 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -15,6 +15,7 @@ Organisation, Secret, ServiceToken, + ServiceAccountToken, UserToken, PersonalSecret, ) @@ -248,7 +249,7 @@ class ServiceAccountTokenSerializer(serializers.ModelSerializer): ) class Meta: - model = UserToken + model = ServiceAccountToken fields = [ "wrapped_key_share", "account_id", From d060d092b4b1caa2a2ad7a455e82f5f6081574e0 Mon Sep 17 00:00:00 2001 From: Nimish Date: Fri, 22 Aug 2025 23:23:31 +0530 Subject: [PATCH 03/80] feat: add created_by_service_account field to ServiceAccountToken model - Introduced a new ForeignKey field to associate service account tokens with their creator service accounts. - This migration enhances the model's relationship capabilities. --- ...accounttoken_created_by_service_account.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 backend/api/migrations/0106_serviceaccounttoken_created_by_service_account.py diff --git a/backend/api/migrations/0106_serviceaccounttoken_created_by_service_account.py b/backend/api/migrations/0106_serviceaccounttoken_created_by_service_account.py new file mode 100644 index 000000000..d48f24941 --- /dev/null +++ b/backend/api/migrations/0106_serviceaccounttoken_created_by_service_account.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.22 on 2025-08-22 08:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0105_environmentkey_unique_envkey_user_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='serviceaccounttoken', + name='created_by_service_account', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_tokens', to='api.serviceaccount'), + ), + ] From 9c2ae453624f93c13460fb94e1b3d512d33e295b Mon Sep 17 00:00:00 2001 From: Nimish Date: Fri, 22 Aug 2025 23:23:39 +0530 Subject: [PATCH 04/80] feat: implement ServiceAccountUser class for service account handling - Added a new ServiceAccountUser class to represent service account users in the authentication process. - Updated PhaseTokenAuthentication to utilize ServiceAccountUser when the creator is not an organization member, enhancing the handling of service account tokens. --- backend/api/auth.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/backend/api/auth.py b/backend/api/auth.py index 7439c356e..40a6f8457 100644 --- a/backend/api/auth.py +++ b/backend/api/auth.py @@ -17,6 +17,18 @@ logger = logging.getLogger(__name__) +class ServiceAccountUser: + """Mock ServiceAccount user""" + + def __init__(self, service_account): + self.userId = service_account.id + self.id = service_account.id + self.is_authenticated = True + self.is_active = True + self.username = service_account.name + self.service_account = service_account + + class PhaseTokenAuthentication(authentication.BaseAuthentication): def authenticate(self, request): @@ -97,8 +109,14 @@ def authenticate(self, request): try: service_token = get_service_token(auth_token) - service_account = get_service_account_from_token(auth_token) - user = service_token.created_by.user + service_account = get_service_account_from_token(auth_token) + + creator = getattr(service_token, "created_by", None) + if creator: + user = creator.user + else: + user = ServiceAccountUser(service_account) + auth["service_account"] = service_account auth["service_account_token"] = service_token From 828cf77fed4a762a36753974fbe97ef29d12bfc7 Mon Sep 17 00:00:00 2001 From: Nimish Date: Fri, 22 Aug 2025 23:56:23 +0530 Subject: [PATCH 05/80] feat: add createdByServiceAccount field to ServiceAccountTokenType - Introduced a new field `createdByServiceAccount` to the ServiceAccountTokenType to enhance the model's relationship with service accounts. - This addition supports better tracking of token creation by service accounts. --- frontend/apollo/schema.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index e726ab1c0..70ce7e45c 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -305,6 +305,7 @@ type ServiceAccountTokenType { token: String! wrappedKeyShare: String! createdBy: OrganisationMemberType + createdByServiceAccount: ServiceAccountType createdAt: DateTime updatedAt: DateTime! deletedAt: DateTime From c42700fc84da5afd48f13fc5548cafe84a2410a9 Mon Sep 17 00:00:00 2001 From: Nimish Date: Fri, 22 Aug 2025 23:59:17 +0530 Subject: [PATCH 06/80] feat: add EnableServiceAccountSseMutation and related types - Introduced `EnableServiceAccountSseMutation` to facilitate enabling third-party authentication for service accounts. - Added `createdByServiceAccount` field to `ServiceAccountTokenType` for better tracking of token creation. - Updated `GetServiceAccountTokensQuery` to include `createdByServiceAccount` information, enhancing the query's detail on token origins. --- frontend/apollo/graphql.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 41d614bca..84193664d 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1975,6 +1975,7 @@ export type ServiceAccountTokenType = { __typename?: 'ServiceAccountTokenType'; createdAt?: Maybe; createdBy?: Maybe; + createdByServiceAccount?: Maybe; deletedAt?: Maybe; expiresAt?: Maybe; id: Scalars['String']['output']; @@ -2643,6 +2644,15 @@ export type DeleteServiceAccountTokenOpMutationVariables = Exact<{ export type DeleteServiceAccountTokenOpMutation = { __typename?: 'Mutation', deleteServiceAccountToken?: { __typename?: 'DeleteServiceAccountTokenMutation', ok?: boolean | null } | null }; +export type EnableServiceAccountSseMutationVariables = Exact<{ + serviceAccountId: Scalars['ID']['input']; + serverWrappedKeyring: Scalars['String']['input']; + serverWrappedRecovery: Scalars['String']['input']; +}>; + + +export type EnableServiceAccountSseMutation = { __typename?: 'Mutation', enableServiceAccountThirdPartyAuth?: { __typename?: 'EnableServiceAccountThirdPartyAuthMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, thirdPartyAuthEnabled?: boolean | null } | null } | null }; + export type UpdateServiceAccountHandlerKeysMutationVariables = Exact<{ orgId: Scalars['ID']['input']; handlers?: InputMaybe> | InputMaybe>; @@ -3145,7 +3155,7 @@ export type GetServiceAccountTokensQueryVariables = Exact<{ }>; -export type GetServiceAccountTokensQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, lastUsed?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null } | null> | null }; +export type GetServiceAccountTokensQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, lastUsed?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null, createdByServiceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null } | null } | null> | null } | null> | null }; export type GetServiceAccountsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3344,6 +3354,7 @@ export const CreateServiceAccountOpDocument = {"kind":"Document","definitions":[ export const CreateSaTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSAToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"expiry"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountTokenOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountTokenOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tokenId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; +export const EnableServiceAccountSseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableServiceAccountSSE"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountThirdPartyAuth"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountHandlerKeysDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountHandlerKeys"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewAwsSecretsSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewAWSSecretsSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createAwsSecretSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}}},{"kind":"Argument","name":{"kind":"Name","value":"kmsId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -3403,7 +3414,7 @@ export const GetSecretsDocument = {"kind":"Document","definitions":[{"kind":"Ope export const GetServiceTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"keys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountHandlersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountHandlers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}}]} as unknown as DocumentNode; -export const GetServiceAccountTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUsed"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetServiceAccountTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdByServiceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUsed"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccounts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationSyncsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationSyncs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"authentication"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"credentials"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"completedAt"}},{"kind":"Field","name":{"kind":"Name","value":"meta"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"savedCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"credentials"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"expectedCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"optionalCredentials"}}]}},{"kind":"Field","name":{"kind":"Name","value":"syncCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetAwsSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAwsSecrets"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"awsSecrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"arn"}}]}}]}}]} as unknown as DocumentNode; From 5a45642e0f492f5832048db8aedb90cf5115d9bc Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 00:02:53 +0530 Subject: [PATCH 07/80] feat: add EnableServiceAccountSSE mutation and update GetServiceAccountTokens query - Introduced `EnableServiceAccountSSE` mutation to enable third-party authentication for service accounts. - Updated `GetServiceAccountTokens` query to include `createdByServiceAccount` field, enhancing token origin tracking. --- frontend/apollo/gql.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 601d00997..7ebdf7d5e 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -66,6 +66,7 @@ const documents = { "mutation CreateSAToken($serviceAccountId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createServiceAccountToken(\n serviceAccountId: $serviceAccountId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n token {\n id\n }\n }\n}": types.CreateSaTokenDocument, "mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountOpDocument, "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}": types.DeleteServiceAccountTokenOpDocument, + "mutation EnableServiceAccountSSE($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountThirdPartyAuth(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}": types.EnableServiceAccountSseDocument, "mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}": types.UpdateServiceAccountHandlerKeysDocument, "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n }\n }\n}": types.UpdateServiceAccountOpDocument, "mutation CreateNewAWSSecretsSync($envId: ID!, $path: String!, $credentialId: ID!, $secretName: String!, $kmsId: String) {\n createAwsSecretSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n secretName: $secretName\n kmsId: $kmsId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewAwsSecretsSyncDocument, @@ -125,7 +126,7 @@ const documents = { "query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n expiresAt\n keys {\n id\n identityKey\n }\n }\n}": types.GetServiceTokensDocument, "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}": types.GetServiceAccountDetailDocument, "query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n email\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, - "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n }\n}": types.GetServiceAccountTokensDocument, + "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}": types.GetServiceAccountTokensDocument, "query GetServiceAccounts($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n }\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n createdAt\n }\n}": types.GetServiceAccountsDocument, "query GetOrganisationSyncs($orgId: ID!) {\n syncs(orgId: $orgId) {\n id\n environment {\n id\n name\n envType\n app {\n id\n name\n }\n }\n path\n serviceInfo {\n id\n name\n provider {\n id\n }\n }\n options\n isActive\n lastSync\n status\n authentication {\n id\n name\n credentials\n }\n createdAt\n history {\n id\n status\n createdAt\n completedAt\n meta\n }\n }\n savedCredentials(orgId: $orgId) {\n id\n name\n credentials\n createdAt\n provider {\n id\n name\n expectedCredentials\n optionalCredentials\n }\n syncCount\n }\n apps(organisationId: $orgId, appId: null) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n fullName\n avatarUrl\n email\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\n }\n }\n}": types.GetOrganisationSyncsDocument, "query GetAwsSecrets($credentialId: ID!) {\n awsSecrets(credentialId: $credentialId) {\n name\n arn\n }\n}": types.GetAwsSecretsDocument, @@ -375,6 +376,10 @@ export function graphql(source: "mutation DeleteServiceAccountOp($id: ID!) {\n * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation EnableServiceAccountSSE($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountThirdPartyAuth(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"): (typeof documents)["mutation EnableServiceAccountSSE($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountThirdPartyAuth(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -614,7 +619,7 @@ export function graphql(source: "query GetServiceAccountHandlers($orgId: ID!) {\ /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n }\n}"): (typeof documents)["query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n }\n}"]; +export function graphql(source: "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}"): (typeof documents)["query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ From c031aa9d4067be68eb2c090c2d11c3c903e8b7e9 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:24:57 +0530 Subject: [PATCH 08/80] feat: rename EnableServiceAccountThirdPartyAuthMutation and add client-side key management mutation - Renamed `EnableServiceAccountThirdPartyAuthMutation` to `EnableServiceAccountServerSideKeyManagementMutation` for clarity. - Introduced `EnableServiceAccountClientSideKeyManagementMutation` to manage client-side key management, allowing for the deletion of server-wrapped keys and enhancing service account security management. --- .../graphene/mutations/service_accounts.py | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index b8768e955..2b8bb9716 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -82,7 +82,7 @@ def mutate( return CreateServiceAccountMutation(service_account=service_account) -class EnableServiceAccountThirdPartyAuthMutation(graphene.Mutation): +class EnableServiceAccountServerSideKeyManagementMutation(graphene.Mutation): class Arguments: service_account_id = graphene.ID() server_wrapped_keyring = graphene.String() @@ -113,7 +113,35 @@ def mutate( service_account.server_wrapped_recovery = server_wrapped_recovery service_account.save() - return EnableServiceAccountThirdPartyAuthMutation( + return EnableServiceAccountServerSideKeyManagementMutation( + service_account=service_account + ) + + +class EnableServiceAccountClientSideKeyManagementMutation(graphene.Mutation): + class Arguments: + service_account_id = graphene.ID() + + service_account = graphene.Field(ServiceAccountType) + + @classmethod + def mutate(cls, root, info, service_account_id): + user = info.context.user + service_account = ServiceAccount.objects.get(id=service_account_id) + + if not user_has_permission( + user, "update", "ServiceAccounts", service_account.organisation + ): + raise GraphQLError( + "You don't have the permissions required to update Service Accounts in this organisation" + ) + + # Delete server-wrapped keys to disable server-side key management + service_account.server_wrapped_keyring = None + service_account.server_wrapped_recovery = None + service_account.save() + + return EnableServiceAccountClientSideKeyManagementMutation( service_account=service_account ) From 3d51fa9b3c28aafafd14a72228a3c92647dde6ff Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:25:10 +0530 Subject: [PATCH 09/80] feat: update service account mutations for key management - Renamed `EnableServiceAccountThirdPartyAuthMutation` to `EnableServiceAccountServerSideKeyManagementMutation` for improved clarity. - Added `EnableServiceAccountClientSideKeyManagementMutation` to support client-side key management, enhancing service account security features. --- backend/backend/schema.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 66a34f6f1..9d9a38635 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -10,7 +10,8 @@ CreateServiceAccountTokenMutation, DeleteServiceAccountMutation, DeleteServiceAccountTokenMutation, - EnableServiceAccountThirdPartyAuthMutation, + EnableServiceAccountClientSideKeyManagementMutation, + EnableServiceAccountServerSideKeyManagementMutation, UpdateServiceAccountHandlersMutation, UpdateServiceAccountMutation, ) @@ -931,8 +932,11 @@ class Mutation(graphene.ObjectType): # Service Accounts create_service_account = CreateServiceAccountMutation.Field() - enable_service_account_third_party_auth = ( - EnableServiceAccountThirdPartyAuthMutation.Field() + enable_service_account_server_side_key_management = ( + EnableServiceAccountServerSideKeyManagementMutation.Field() + ) + enable_service_account_client_side_key_management = ( + EnableServiceAccountClientSideKeyManagementMutation.Field() ) update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field() update_service_account = UpdateServiceAccountMutation.Field() From 5ce3ca8ab7cd48a238a2d0dc451cbb502e8ac84f Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:25:18 +0530 Subject: [PATCH 10/80] feat: enhance service account mutations for key management - Renamed `EnableServiceAccountThirdPartyAuthMutation` to `EnableServiceAccountServerSideKeyManagementMutation` for better clarity. - Added `EnableServiceAccountClientSideKeyManagementMutation` to facilitate client-side key management, improving service account security features. --- frontend/apollo/schema.graphql | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 70ce7e45c..a09623e64 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -802,7 +802,8 @@ type Mutation { deleteNetworkAccessPolicy(id: ID!): DeleteNetworkAccessPolicyMutation updateAccountNetworkAccessPolicies(accountInputs: [AccountPolicyInput], organisationId: ID!): UpdateAccountNetworkAccessPolicies createServiceAccount(handlers: [ServiceAccountHandlerInput], identityKey: String, name: String, organisationId: ID, roleId: ID, serverWrappedKeyring: String, serverWrappedRecovery: String): CreateServiceAccountMutation - enableServiceAccountThirdPartyAuth(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountThirdPartyAuthMutation + enableServiceAccountServerSideKeyManagement(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountServerSideKeyManagementMutation + enableServiceAccountClientSideKeyManagement(serviceAccountId: ID): EnableServiceAccountClientSideKeyManagementMutation updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], organisationId: ID): UpdateServiceAccountHandlersMutation updateServiceAccount(name: String, roleId: ID, serviceAccountId: ID): UpdateServiceAccountMutation deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation @@ -1027,7 +1028,11 @@ input ServiceAccountHandlerInput { wrappedRecovery: String! } -type EnableServiceAccountThirdPartyAuthMutation { +type EnableServiceAccountServerSideKeyManagementMutation { + serviceAccount: ServiceAccountType +} + +type EnableServiceAccountClientSideKeyManagementMutation { serviceAccount: ServiceAccountType } From 41001dd1dd1b43e8b1f48f14d39e41396eadb989 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:29:19 +0530 Subject: [PATCH 11/80] feat: update service account mutations and queries for key management - Replaced `EnableServiceAccountSSE` mutation with `EnableSAClientKeyManagement` and `EnableSAServerKeyManagement` mutations to enhance key management capabilities. - Updated `GetServiceAccountDetail` query to include `thirdPartyAuthEnabled` field, improving service account detail retrieval. --- frontend/apollo/gql.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 7ebdf7d5e..211aa3992 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -66,7 +66,8 @@ const documents = { "mutation CreateSAToken($serviceAccountId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createServiceAccountToken(\n serviceAccountId: $serviceAccountId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n token {\n id\n }\n }\n}": types.CreateSaTokenDocument, "mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountOpDocument, "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}": types.DeleteServiceAccountTokenOpDocument, - "mutation EnableServiceAccountSSE($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountThirdPartyAuth(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}": types.EnableServiceAccountSseDocument, + "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n }\n }\n}": types.EnableSaClientKeyManagementDocument, + "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}": types.EnableSaServerKeyManagementDocument, "mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}": types.UpdateServiceAccountHandlerKeysDocument, "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n }\n }\n}": types.UpdateServiceAccountOpDocument, "mutation CreateNewAWSSecretsSync($envId: ID!, $path: String!, $credentialId: ID!, $secretName: String!, $kmsId: String) {\n createAwsSecretSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n secretName: $secretName\n kmsId: $kmsId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewAwsSecretsSyncDocument, @@ -124,7 +125,7 @@ const documents = { "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, "query GetSecrets($appId: ID!, $envId: ID!, $path: String) {\n secrets(envId: $envId, path: $path) {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n comment\n createdAt\n updatedAt\n override {\n value\n isActive\n }\n environment {\n id\n app {\n id\n }\n }\n }\n folders(envId: $envId, path: $path) {\n id\n name\n path\n createdAt\n folderCount\n secretCount\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n app {\n name\n }\n }\n environmentKeys(appId: $appId, environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n envSyncs(envId: $envId) {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n options\n isActive\n status\n lastSync\n createdAt\n }\n}": types.GetSecretsDocument, "query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n expiresAt\n keys {\n id\n identityKey\n }\n }\n}": types.GetServiceTokensDocument, - "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}": types.GetServiceAccountDetailDocument, + "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}": types.GetServiceAccountDetailDocument, "query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n email\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}": types.GetServiceAccountTokensDocument, "query GetServiceAccounts($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n }\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n createdAt\n }\n}": types.GetServiceAccountsDocument, @@ -379,7 +380,11 @@ export function graphql(source: "mutation DeleteServiceAccountTokenOp($id: ID!) /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation EnableServiceAccountSSE($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountThirdPartyAuth(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"): (typeof documents)["mutation EnableServiceAccountSSE($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountThirdPartyAuth(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"]; +export function graphql(source: "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n }\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -611,7 +616,7 @@ export function graphql(source: "query GetServiceTokens($appId: ID!) {\n servic /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"): (typeof documents)["query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"]; +export function graphql(source: "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"): (typeof documents)["query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ From 9a5772fdd94b3ccdcb0c730271ad2d797d709bdf Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:29:35 +0530 Subject: [PATCH 12/80] feat: enhance service account mutations and queries for key management - Introduced `EnableServiceAccountClientSideKeyManagementMutation` and `EnableServiceAccountServerSideKeyManagementMutation` to improve key management capabilities. - Updated `GetServiceAccountDetail` query to include `thirdPartyAuthEnabled` field for better service account detail retrieval. - Renamed existing mutations for clarity and consistency in naming conventions. --- frontend/apollo/graphql.ts | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 84193664d..165d606c4 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -473,8 +473,13 @@ export type EditSecretMutation = { secret?: Maybe; }; -export type EnableServiceAccountThirdPartyAuthMutation = { - __typename?: 'EnableServiceAccountThirdPartyAuthMutation'; +export type EnableServiceAccountClientSideKeyManagementMutation = { + __typename?: 'EnableServiceAccountClientSideKeyManagementMutation'; + serviceAccount?: Maybe; +}; + +export type EnableServiceAccountServerSideKeyManagementMutation = { + __typename?: 'EnableServiceAccountServerSideKeyManagementMutation'; serviceAccount?: Maybe; }; @@ -735,7 +740,8 @@ export type Mutation = { deleteUserToken?: Maybe; editSecret?: Maybe; editSecrets?: Maybe; - enableServiceAccountThirdPartyAuth?: Maybe; + enableServiceAccountClientSideKeyManagement?: Maybe; + enableServiceAccountServerSideKeyManagement?: Maybe; initEnvSync?: Maybe; modifySubscription?: Maybe; readSecret?: Maybe; @@ -1149,7 +1155,12 @@ export type MutationEditSecretsArgs = { }; -export type MutationEnableServiceAccountThirdPartyAuthArgs = { +export type MutationEnableServiceAccountClientSideKeyManagementArgs = { + serviceAccountId?: InputMaybe; +}; + + +export type MutationEnableServiceAccountServerSideKeyManagementArgs = { serverWrappedKeyring?: InputMaybe; serverWrappedRecovery?: InputMaybe; serviceAccountId?: InputMaybe; @@ -2644,14 +2655,21 @@ export type DeleteServiceAccountTokenOpMutationVariables = Exact<{ export type DeleteServiceAccountTokenOpMutation = { __typename?: 'Mutation', deleteServiceAccountToken?: { __typename?: 'DeleteServiceAccountTokenMutation', ok?: boolean | null } | null }; -export type EnableServiceAccountSseMutationVariables = Exact<{ +export type EnableSaClientKeyManagementMutationVariables = Exact<{ + serviceAccountId: Scalars['ID']['input']; +}>; + + +export type EnableSaClientKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountClientSideKeyManagement?: { __typename?: 'EnableServiceAccountClientSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, thirdPartyAuthEnabled?: boolean | null } | null } | null }; + +export type EnableSaServerKeyManagementMutationVariables = Exact<{ serviceAccountId: Scalars['ID']['input']; serverWrappedKeyring: Scalars['String']['input']; serverWrappedRecovery: Scalars['String']['input']; }>; -export type EnableServiceAccountSseMutation = { __typename?: 'Mutation', enableServiceAccountThirdPartyAuth?: { __typename?: 'EnableServiceAccountThirdPartyAuthMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, thirdPartyAuthEnabled?: boolean | null } | null } | null }; +export type EnableSaServerKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountServerSideKeyManagement?: { __typename?: 'EnableServiceAccountServerSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, thirdPartyAuthEnabled?: boolean | null } | null } | null }; export type UpdateServiceAccountHandlerKeysMutationVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3140,7 +3158,7 @@ export type GetServiceAccountDetailQueryVariables = Exact<{ }>; -export type GetServiceAccountDetailQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, appMemberships?: Array<{ __typename?: 'AppMembershipType', id: string, name: string, sseEnabled: boolean, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null, networkPolicies?: Array<{ __typename?: 'NetworkAccessPolicyType', id: string, name: string, allowedIps: string, isGlobal: boolean }> | null } | null> | null }; +export type GetServiceAccountDetailQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, thirdPartyAuthEnabled?: boolean | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, appMemberships?: Array<{ __typename?: 'AppMembershipType', id: string, name: string, sseEnabled: boolean, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null, networkPolicies?: Array<{ __typename?: 'NetworkAccessPolicyType', id: string, name: string, allowedIps: string, isGlobal: boolean }> | null } | null> | null }; export type GetServiceAccountHandlersQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3354,7 +3372,8 @@ export const CreateServiceAccountOpDocument = {"kind":"Document","definitions":[ export const CreateSaTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSAToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"expiry"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountTokenOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountTokenOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tokenId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; -export const EnableServiceAccountSseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableServiceAccountSSE"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountThirdPartyAuth"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; +export const EnableSaClientKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAClientKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountClientSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; +export const EnableSaServerKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAServerKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountServerSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountHandlerKeysDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountHandlerKeys"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewAwsSecretsSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewAWSSecretsSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createAwsSecretSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}}},{"kind":"Argument","name":{"kind":"Name","value":"kmsId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -3412,7 +3431,7 @@ export const GetEnvSecretsKvDocument = {"kind":"Document","definitions":[{"kind" export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretTags"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secretTags"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]} as unknown as DocumentNode; export const GetSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecrets"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"override"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}}]}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"folders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"folderCount"}},{"kind":"Field","name":{"kind":"Name","value":"secretCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"appEnvironments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"envSyncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetServiceTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"keys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountHandlersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountHandlers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdByServiceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUsed"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccounts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; From 234b985702009c46f21f2ab207e1334a23d8bb7a Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:30:03 +0530 Subject: [PATCH 13/80] feat: add service account secret wrapping and unwrapping functions - Introduced `wrapServiceAccountSecretsForServer` to securely wrap service account keyring and recovery for server-side encryption. - Added `unwrapServiceAccountSecretsForUser` to decrypt and retrieve the original keyring and recovery for the current user. - Enhanced documentation with detailed JSDoc comments for both functions, improving code clarity and usability. --- frontend/utils/crypto/service-accounts.ts | 60 ++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/frontend/utils/crypto/service-accounts.ts b/frontend/utils/crypto/service-accounts.ts index 797479c53..81ed02bb5 100644 --- a/frontend/utils/crypto/service-accounts.ts +++ b/frontend/utils/crypto/service-accounts.ts @@ -124,4 +124,62 @@ export const updateServiceAccountHandlers = async (orgId: string, userKeyring: O }) -} \ No newline at end of file +} + +/** + * Wraps service account keyring and recovery for server-side encryption. + * + * @param {string} keyringString - The service account keyring as JSON string + * @param {string} recoveryString - The service account recovery/mnemonic string + * @param {string} serverPublicKey - The server's public key for encryption + * @returns {Promise<{ serverWrappedKeyring: string; serverWrappedRecovery: string }>} + */ +export const wrapServiceAccountSecretsForServer = async ( + keyringString: string, + recoveryString: string, + serverPublicKey: string +) => { + const serverWrappedKeyring = await encryptAsymmetric(keyringString, serverPublicKey) + const serverWrappedRecovery = await encryptAsymmetric(recoveryString, serverPublicKey) + + return { + serverWrappedKeyring, + serverWrappedRecovery, + } +} + +/** + * Unwraps service account keyring and recovery for the current user. + * + * @param {string} wrappedKeyring - The user-wrapped keyring + * @param {string} wrappedRecovery - The user-wrapped recovery + * @param {OrganisationKeyring} userKeyring - The current user's keyring + * @returns {Promise<{ keyringString: string; recoveryString: string }>} + */ +export const unwrapServiceAccountSecretsForUser = async ( + wrappedKeyring: string, + wrappedRecovery: string, + userKeyring: OrganisationKeyring +) => { + const userKxKeys = { + publicKey: await getUserKxPublicKey(userKeyring.publicKey), + privateKey: await getUserKxPrivateKey(userKeyring.privateKey), + } + + const keyringString = await decryptAsymmetric( + wrappedKeyring, + userKxKeys.privateKey, + userKxKeys.publicKey + ) + + const recoveryString = await decryptAsymmetric( + wrappedRecovery, + userKxKeys.privateKey, + userKxKeys.publicKey + ) + + return { + keyringString, + recoveryString, + } +} From d9528d547c15e8e72e34bdda15501036d285cfd9 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:31:06 +0530 Subject: [PATCH 14/80] feat: implement KeyManagementDialog for service account key management - Introduced `KeyManagementDialog` component to manage service account key settings, allowing users to switch between client-side and server-side key management. - Integrated Apollo Client for querying and mutating service account data, enhancing user experience with real-time updates. - Added user permission checks to ensure only authorized users can manage key settings, improving security and usability. - Included visual alerts and feedback for actions taken within the dialog, enhancing user interaction and clarity. --- .../service-accounts/KeyManagementDialog.tsx | 405 ++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 frontend/components/service-accounts/KeyManagementDialog.tsx diff --git a/frontend/components/service-accounts/KeyManagementDialog.tsx b/frontend/components/service-accounts/KeyManagementDialog.tsx new file mode 100644 index 000000000..4bba4271d --- /dev/null +++ b/frontend/components/service-accounts/KeyManagementDialog.tsx @@ -0,0 +1,405 @@ +import { ServiceAccountType } from '@/apollo/graphql' +import { useMutation, useQuery } from '@apollo/client' +import { Dialog, Transition } from '@headlessui/react' +import { useState, Fragment, useContext } from 'react' +import { FaTimes, FaCog, FaUsers } from 'react-icons/fa' +import { FaServer, FaArrowDownUpLock } from 'react-icons/fa6' +import { MdMenuBook } from 'react-icons/md' +import clsx from 'clsx' +import { toast } from 'react-toastify' +import { Alert } from '../common/Alert' +import { Button } from '../common/Button' +import { KeyringContext } from '@/contexts/keyringContext' +import GetServerKey from '@/graphql/queries/syncing/getServerKey.gql' +import { GetServiceAccountDetail } from '@/graphql/queries/service-accounts/getServiceAccountDetail.gql' +import { organisationContext } from '@/contexts/organisationContext' +import { + unwrapServiceAccountSecretsForUser, + wrapServiceAccountSecretsForServer, +} from '@/utils/crypto/service-accounts' +import { userHasPermission } from '@/utils/access/permissions' +import Link from 'next/link' +import EnableServerSide from '@/graphql/mutations/service-accounts/enableServiceAccountServerSideKeyManagement.gql' +import EnableClientSide from '@/graphql/mutations/service-accounts/enableServiceAccountClientSideKeyManagement.gql' + +interface KeyManagementDialogProps { + serviceAccount: ServiceAccountType + trigger?: React.ReactNode + mode?: 'enable' | 'manage' +} + +export const KeyManagementDialog = (props: KeyManagementDialogProps) => { + const { serviceAccount, trigger, mode = 'manage' } = props + + const { activeOrganisation: organisation } = useContext(organisationContext) + const { keyring } = useContext(KeyringContext) + + const { data: serverKeyData } = useQuery(GetServerKey) + const [enableSSE, { loading: enableLoading }] = useMutation(EnableServerSide) + const [disableSSE, { loading: disableLoading }] = useMutation(EnableClientSide) + + const userCanManageKeys = organisation + ? userHasPermission(organisation.role?.permissions, 'ServiceAccounts', 'update') + : false + + const [isOpen, setIsOpen] = useState(false) + const [selectedMode, setSelectedMode] = useState<'client' | 'server'>( + serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client' + ) + + const closeModal = () => { + setIsOpen(false) + // Reset to current state when closing + setSelectedMode(serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client') + } + + const openModal = () => { + // Always sync mode with latest account state on open + setSelectedMode(serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client') + setIsOpen(true) + } + + const handleSave = async (e: { preventDefault: () => void }) => { + e.preventDefault() + + if (selectedMode === 'server' && !serviceAccount.thirdPartyAuthEnabled) { + // Enable server-side encryption + await handleEnableSSE() + } else if (selectedMode === 'client' && serviceAccount.thirdPartyAuthEnabled) { + // Disable server-side encryption (switch to client-side) + await handleDisableSSE() + } else { + // No change needed + closeModal() + } + } + + const handleEnableSSE = async () => { + if (!keyring || !serverKeyData?.serverPublicKey) { + toast.error('Missing required keys') + return + } + + try { + // Find the current user's handler for this service account + const currentUserHandler = serviceAccount.handlers?.find( + (handler) => handler?.user.self === true + ) + + if (!currentUserHandler) { + toast.error('You do not have handler access to this service account') + return + } + + // Unwrap the service account secrets using the current user's keys + const { keyringString, recoveryString } = await unwrapServiceAccountSecretsForUser( + currentUserHandler.wrappedKeyring, + currentUserHandler.wrappedRecovery, + keyring + ) + + // Wrap the secrets for the server + const { serverWrappedKeyring, serverWrappedRecovery } = + await wrapServiceAccountSecretsForServer( + keyringString, + recoveryString, + serverKeyData.serverPublicKey + ) + + await toast.promise( + enableSSE({ + variables: { + serviceAccountId: serviceAccount.id, + serverWrappedKeyring, + serverWrappedRecovery, + }, + refetchQueries: [ + { + query: GetServiceAccountDetail, + variables: { orgId: organisation!.id, id: serviceAccount.id }, + }, + ], + }), + { + pending: 'Enabling server-side key management...', + success: 'Server-side key management enabled.', + error: 'Failed to enable server-side key management', + } + ) + // Ensure local state reflects new server-side mode + setSelectedMode('server') + closeModal() + } catch (error) { + console.error('Error enabling SSE:', error) + toast.error('Failed to enable server-side key management') + } + } + + const handleDisableSSE = async () => { + try { + await toast.promise( + disableSSE({ + variables: { + serviceAccountId: serviceAccount.id, + }, + refetchQueries: [ + { + query: GetServiceAccountDetail, + variables: { orgId: organisation!.id, id: serviceAccount.id }, + }, + ], + }), + { + pending: 'Switching to client-side key management...', + success: 'Client-side key management enabled.', + error: 'Failed to switch to client-side key management', + } + ) + // Ensure local state reflects new client-side mode + setSelectedMode('client') + closeModal() + } catch (error) { + console.error('Error disabling SSE:', error) + toast.error('Failed to switch to client-side key management') + } + } + + const currentMode = serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client' + const hasChanges = selectedMode !== currentMode + + return ( + <> + {trigger ? ( +
+ {trigger} +
+ ) : ( +
+ +
+ )} + + + + +
+ + +
+
+ + + +

+ Key Management Settings +

+ +
+ + + + + +
+
+ + {userCanManageKeys ? ( +
+

+ Choose where and how keys are managed for this Service Account: +

+ +
+ {/* Client-side option */} + + + {/* Server-side option */} + +
+ + {hasChanges && + selectedMode === 'server' && + !serviceAccount.thirdPartyAuthEnabled && ( + + Enabling server-side key management will allow the server to securely + access keys and generate tokens on behalf of the Service Account. + + )} + + {hasChanges && + selectedMode === 'client' && + serviceAccount.thirdPartyAuthEnabled && ( + + Switching to client-side key management will remove server access to + this account's keys. All previously generated access tokens will + continue to work until they expire. + + )} + +
+ + +
+
+ ) : ( +
+ + Only users with Service Account update permissions can manage key settings + for this Service Account. Please contact an Organization Owner or Admin. + + +
+ + + +
+
+ )} +
+
+
+
+
+
+ + ) +} From 7018c3be2dfdd9c6d24ebcb463bf6f1e52406de0 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:31:16 +0530 Subject: [PATCH 15/80] feat: enhance ServiceAccountTokens component to display creator information - Updated the `ServiceAccountTokens` component to conditionally render the creator's information, supporting both user and service account creators. - Added fallback text for unknown creators, improving user experience and clarity in token origin tracking. --- .../_components/ServiceAccountTokens.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx index 9ef68e79a..a187190a3 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx @@ -94,10 +94,23 @@ export const ServiceAccountTokens = ({ account }: { account: ServiceAccountType
by - - - {token?.createdBy?.fullName} - + {token?.createdBy ? ( + <> + + + {token.createdBy.fullName} + + + ) : token?.createdByServiceAccount ? ( + <> + + + {token.createdByServiceAccount.name} + + + ) : ( + Unknown + )}
From 498f4c2d4f2c7ac1f912febf7ff2974b70731e89 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:31:23 +0530 Subject: [PATCH 16/80] feat: add createdByServiceAccount field to GetServiceAccountTokens query - Enhanced the `GetServiceAccountTokens` query by including the `createdByServiceAccount` field, which provides details about the creator of the service account tokens, improving tracking and transparency of token origins. --- .../queries/service-accounts/getServiceAccountTokens.gql | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/graphql/queries/service-accounts/getServiceAccountTokens.gql b/frontend/graphql/queries/service-accounts/getServiceAccountTokens.gql index d8cba2ac2..86dfefe13 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccountTokens.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccountTokens.gql @@ -11,6 +11,11 @@ query GetServiceAccountTokens($orgId: ID!, $id: ID) { avatarUrl self } + createdByServiceAccount { + id + name + identityKey + } lastUsed } } From a0a57ab96882f204348b2f2006941a04942c8570 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:46:19 +0530 Subject: [PATCH 17/80] feat: rename third_party_auth_enabled to server_side_key_management_enabled in ServiceAccountType - Updated the `ServiceAccountType` to replace the `third_party_auth_enabled` field with `server_side_key_management_enabled`, reflecting a clearer purpose for server-side key management. - Adjusted the corresponding resolver method to match the new field name, ensuring consistency in the API. --- backend/backend/graphene/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index ebf3d3c82..6e32f3202 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -676,7 +676,7 @@ def resolve_environments(self, info): class ServiceAccountType(DjangoObjectType): - third_party_auth_enabled = graphene.Boolean() + server_side_key_management_enabled = graphene.Boolean() handlers = graphene.List(ServiceAccountHandlerType) tokens = graphene.List(ServiceAccountTokenType) app_memberships = graphene.List(graphene.NonNull(AppMembershipType)) @@ -694,7 +694,7 @@ class Meta: "deleted_at", ) - def resolve_third_party_auth_enabled(self, info): + def resolve_server_side_key_management_enabled(self, info): return ( self.server_wrapped_keyring is not None and self.server_wrapped_recovery is not None From 5e80b3f8e48263f951819f88e59a10469a373563 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:46:40 +0530 Subject: [PATCH 18/80] feat: update ServiceAccountType and related queries for key management - Replaced the `thirdPartyAuthEnabled` field with `serverSideKeyManagementEnabled` in the `ServiceAccountType` to better reflect the functionality of server-side key management. - Updated the `GetServiceAccountDetail` query and related mutations to utilize the new field, ensuring consistency across the API. - Adjusted the GraphQL documents and types to align with the new schema changes, enhancing clarity and usability in service account management. --- frontend/apollo/gql.ts | 12 ++++++------ frontend/apollo/graphql.ts | 14 +++++++------- frontend/apollo/schema.graphql | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 211aa3992..11620e7a3 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -66,8 +66,8 @@ const documents = { "mutation CreateSAToken($serviceAccountId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createServiceAccountToken(\n serviceAccountId: $serviceAccountId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n token {\n id\n }\n }\n}": types.CreateSaTokenDocument, "mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountOpDocument, "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}": types.DeleteServiceAccountTokenOpDocument, - "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n }\n }\n}": types.EnableSaClientKeyManagementDocument, - "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}": types.EnableSaServerKeyManagementDocument, + "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n }\n }\n}": types.EnableSaClientKeyManagementDocument, + "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n serverSideKeyManagementEnabled\n }\n }\n}": types.EnableSaServerKeyManagementDocument, "mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}": types.UpdateServiceAccountHandlerKeysDocument, "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n }\n }\n}": types.UpdateServiceAccountOpDocument, "mutation CreateNewAWSSecretsSync($envId: ID!, $path: String!, $credentialId: ID!, $secretName: String!, $kmsId: String) {\n createAwsSecretSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n secretName: $secretName\n kmsId: $kmsId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewAwsSecretsSyncDocument, @@ -125,7 +125,7 @@ const documents = { "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, "query GetSecrets($appId: ID!, $envId: ID!, $path: String) {\n secrets(envId: $envId, path: $path) {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n comment\n createdAt\n updatedAt\n override {\n value\n isActive\n }\n environment {\n id\n app {\n id\n }\n }\n }\n folders(envId: $envId, path: $path) {\n id\n name\n path\n createdAt\n folderCount\n secretCount\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n app {\n name\n }\n }\n environmentKeys(appId: $appId, environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n envSyncs(envId: $envId) {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n options\n isActive\n status\n lastSync\n createdAt\n }\n}": types.GetSecretsDocument, "query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n expiresAt\n keys {\n id\n identityKey\n }\n }\n}": types.GetServiceTokensDocument, - "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}": types.GetServiceAccountDetailDocument, + "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}": types.GetServiceAccountDetailDocument, "query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n email\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}": types.GetServiceAccountTokensDocument, "query GetServiceAccounts($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n }\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n createdAt\n }\n}": types.GetServiceAccountsDocument, @@ -380,11 +380,11 @@ export function graphql(source: "mutation DeleteServiceAccountTokenOp($id: ID!) /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n }\n }\n}"]; +export function graphql(source: "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n thirdPartyAuthEnabled\n }\n }\n}"]; +export function graphql(source: "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n serverSideKeyManagementEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n serverSideKeyManagementEnabled\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -616,7 +616,7 @@ export function graphql(source: "query GetServiceTokens($appId: ID!) {\n servic /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"): (typeof documents)["query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n thirdPartyAuthEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"]; +export function graphql(source: "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"): (typeof documents)["query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 165d606c4..b43cbb13a 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -2011,7 +2011,7 @@ export type ServiceAccountType = { name: Scalars['String']['output']; networkPolicies?: Maybe>; role?: Maybe; - thirdPartyAuthEnabled?: Maybe; + serverSideKeyManagementEnabled?: Maybe; tokens?: Maybe>>; updatedAt: Scalars['DateTime']['output']; }; @@ -2660,7 +2660,7 @@ export type EnableSaClientKeyManagementMutationVariables = Exact<{ }>; -export type EnableSaClientKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountClientSideKeyManagement?: { __typename?: 'EnableServiceAccountClientSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, thirdPartyAuthEnabled?: boolean | null } | null } | null }; +export type EnableSaClientKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountClientSideKeyManagement?: { __typename?: 'EnableServiceAccountClientSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, serverSideKeyManagementEnabled?: boolean | null } | null } | null }; export type EnableSaServerKeyManagementMutationVariables = Exact<{ serviceAccountId: Scalars['ID']['input']; @@ -2669,7 +2669,7 @@ export type EnableSaServerKeyManagementMutationVariables = Exact<{ }>; -export type EnableSaServerKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountServerSideKeyManagement?: { __typename?: 'EnableServiceAccountServerSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, thirdPartyAuthEnabled?: boolean | null } | null } | null }; +export type EnableSaServerKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountServerSideKeyManagement?: { __typename?: 'EnableServiceAccountServerSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, serverSideKeyManagementEnabled?: boolean | null } | null } | null }; export type UpdateServiceAccountHandlerKeysMutationVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3158,7 +3158,7 @@ export type GetServiceAccountDetailQueryVariables = Exact<{ }>; -export type GetServiceAccountDetailQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, thirdPartyAuthEnabled?: boolean | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, appMemberships?: Array<{ __typename?: 'AppMembershipType', id: string, name: string, sseEnabled: boolean, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null, networkPolicies?: Array<{ __typename?: 'NetworkAccessPolicyType', id: string, name: string, allowedIps: string, isGlobal: boolean }> | null } | null> | null }; +export type GetServiceAccountDetailQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, serverSideKeyManagementEnabled?: boolean | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, appMemberships?: Array<{ __typename?: 'AppMembershipType', id: string, name: string, sseEnabled: boolean, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null, networkPolicies?: Array<{ __typename?: 'NetworkAccessPolicyType', id: string, name: string, allowedIps: string, isGlobal: boolean }> | null } | null> | null }; export type GetServiceAccountHandlersQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3372,8 +3372,8 @@ export const CreateServiceAccountOpDocument = {"kind":"Document","definitions":[ export const CreateSaTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSAToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"expiry"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountTokenOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountTokenOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tokenId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; -export const EnableSaClientKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAClientKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountClientSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; -export const EnableSaServerKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAServerKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountServerSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; +export const EnableSaClientKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAClientKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountClientSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"serverSideKeyManagementEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; +export const EnableSaServerKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAServerKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountServerSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"serverSideKeyManagementEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountHandlerKeysDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountHandlerKeys"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewAwsSecretsSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewAWSSecretsSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createAwsSecretSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}}},{"kind":"Argument","name":{"kind":"Name","value":"kmsId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -3431,7 +3431,7 @@ export const GetEnvSecretsKvDocument = {"kind":"Document","definitions":[{"kind" export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretTags"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secretTags"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]} as unknown as DocumentNode; export const GetSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecrets"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"override"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}}]}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"folders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"folderCount"}},{"kind":"Field","name":{"kind":"Name","value":"secretCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"appEnvironments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"envSyncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetServiceTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"keys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"thirdPartyAuthEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"serverSideKeyManagementEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountHandlersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountHandlers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdByServiceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUsed"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccounts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index a09623e64..40a41f322 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -280,7 +280,7 @@ type ServiceAccountType { createdAt: DateTime updatedAt: DateTime! deletedAt: DateTime - thirdPartyAuthEnabled: Boolean + serverSideKeyManagementEnabled: Boolean handlers: [ServiceAccountHandlerType] tokens: [ServiceAccountTokenType] appMemberships: [AppMembershipType!] From b299c7718c382b77bf68f289857ca5b8c504127a Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:47:00 +0530 Subject: [PATCH 19/80] feat: add serverSideKeyManagementEnabled field to GetServiceAccountDetail query - Updated the `GetServiceAccountDetail` query to include the `serverSideKeyManagementEnabled` field, enhancing the detail provided for service accounts and aligning with recent schema changes for key management. --- .../graphql/queries/service-accounts/getServiceAccountDetail.gql | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/graphql/queries/service-accounts/getServiceAccountDetail.gql b/frontend/graphql/queries/service-accounts/getServiceAccountDetail.gql index 1ed76111c..791c432c9 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccountDetail.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccountDetail.gql @@ -3,6 +3,7 @@ query GetServiceAccountDetail($orgId: ID!, $id: ID) { id name identityKey + serverSideKeyManagementEnabled role { id name From 86f6fbac4e527063edff48809e79f88bdc1b286a Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:47:08 +0530 Subject: [PATCH 20/80] feat: add mutations for enabling client-side and server-side key management for service accounts - Introduced `EnableSAClientKeyManagement` mutation to enable client-side key management for service accounts. - Added `EnableSAServerKeyManagement` mutation to enable server-side key management, requiring additional parameters for secure key handling. - Enhanced service account management capabilities by providing distinct mutations for both client-side and server-side key management. --- ...eServiceAccountClientSideKeyManagement.gql | 12 ++++++++++++ ...eServiceAccountServerSideKeyManagement.gql | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 frontend/graphql/mutations/service-accounts/enableServiceAccountClientSideKeyManagement.gql create mode 100644 frontend/graphql/mutations/service-accounts/enableServiceAccountServerSideKeyManagement.gql diff --git a/frontend/graphql/mutations/service-accounts/enableServiceAccountClientSideKeyManagement.gql b/frontend/graphql/mutations/service-accounts/enableServiceAccountClientSideKeyManagement.gql new file mode 100644 index 000000000..91bfd41e8 --- /dev/null +++ b/frontend/graphql/mutations/service-accounts/enableServiceAccountClientSideKeyManagement.gql @@ -0,0 +1,12 @@ +mutation EnableSAClientKeyManagement($serviceAccountId: ID!) { + enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) { + serviceAccount { + id + name + identityKey + serverSideKeyManagementEnabled + } + } +} + + diff --git a/frontend/graphql/mutations/service-accounts/enableServiceAccountServerSideKeyManagement.gql b/frontend/graphql/mutations/service-accounts/enableServiceAccountServerSideKeyManagement.gql new file mode 100644 index 000000000..65a7d4950 --- /dev/null +++ b/frontend/graphql/mutations/service-accounts/enableServiceAccountServerSideKeyManagement.gql @@ -0,0 +1,19 @@ +mutation EnableSAServerKeyManagement( + $serviceAccountId: ID! + $serverWrappedKeyring: String! + $serverWrappedRecovery: String! +) { + enableServiceAccountServerSideKeyManagement( + serviceAccountId: $serviceAccountId + serverWrappedKeyring: $serverWrappedKeyring + serverWrappedRecovery: $serverWrappedRecovery + ) { + serviceAccount { + id + name + serverSideKeyManagementEnabled + } + } +} + + From 64bdc5514cfff51834c88deb11c59a03bf9b97e0 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:47:32 +0530 Subject: [PATCH 21/80] fix: update KeyManagementDialog to use serverSideKeyManagementEnabled - Replaced instances of thirdPartyAuthEnabled with serverSideKeyManagementEnabled in KeyManagementDialog component to ensure accurate state management for key management modes. - Adjusted modal open/close behavior and save logic to reflect the new field, enhancing the functionality and clarity of service account key management. --- .../service-accounts/KeyManagementDialog.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/components/service-accounts/KeyManagementDialog.tsx b/frontend/components/service-accounts/KeyManagementDialog.tsx index 4bba4271d..05e00c1fe 100644 --- a/frontend/components/service-accounts/KeyManagementDialog.tsx +++ b/frontend/components/service-accounts/KeyManagementDialog.tsx @@ -44,28 +44,28 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { const [isOpen, setIsOpen] = useState(false) const [selectedMode, setSelectedMode] = useState<'client' | 'server'>( - serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client' + serviceAccount.serverSideKeyManagementEnabled ? 'server' : 'client' ) const closeModal = () => { setIsOpen(false) // Reset to current state when closing - setSelectedMode(serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client') + setSelectedMode(serviceAccount.serverSideKeyManagementEnabled ? 'server' : 'client') } const openModal = () => { // Always sync mode with latest account state on open - setSelectedMode(serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client') + setSelectedMode(serviceAccount.serverSideKeyManagementEnabled ? 'server' : 'client') setIsOpen(true) } const handleSave = async (e: { preventDefault: () => void }) => { e.preventDefault() - if (selectedMode === 'server' && !serviceAccount.thirdPartyAuthEnabled) { + if (selectedMode === 'server' && !serviceAccount.serverSideKeyManagementEnabled) { // Enable server-side encryption await handleEnableSSE() - } else if (selectedMode === 'client' && serviceAccount.thirdPartyAuthEnabled) { + } else if (selectedMode === 'client' && serviceAccount.serverSideKeyManagementEnabled) { // Disable server-side encryption (switch to client-side) await handleDisableSSE() } else { @@ -164,7 +164,7 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { } } - const currentMode = serviceAccount.thirdPartyAuthEnabled ? 'server' : 'client' + const currentMode = serviceAccount.serverSideKeyManagementEnabled ? 'server' : 'client' const hasChanges = selectedMode !== currentMode return ( @@ -347,7 +347,7 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { {hasChanges && selectedMode === 'server' && - !serviceAccount.thirdPartyAuthEnabled && ( + !serviceAccount.serverSideKeyManagementEnabled && ( Enabling server-side key management will allow the server to securely access keys and generate tokens on behalf of the Service Account. @@ -356,7 +356,7 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { {hasChanges && selectedMode === 'client' && - serviceAccount.thirdPartyAuthEnabled && ( + serviceAccount.serverSideKeyManagementEnabled && ( Switching to client-side key management will remove server access to this account's keys. All previously generated access tokens will From c9dae07986554ba6ab3e60513b4b546e57a07178 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 17:47:50 +0530 Subject: [PATCH 22/80] feat: enhance ServiceAccount page with key management display - Added visual indicators for server-side and client-side key management modes in the ServiceAccount component. - Integrated KeyManagementDialog for managing key settings, allowing users to switch between management modes. - Improved user experience by providing clear labels and management options based on the service account's key management configuration. --- .../service-accounts/[account]/page.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index f54f2c4c6..19c2e44ff 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -9,6 +9,7 @@ import { useMutation, useQuery } from '@apollo/client' import Link from 'next/link' import { useContext, useEffect, useState } from 'react' import { FaBan, FaBoxOpen, FaChevronLeft, FaCog, FaEdit, FaNetworkWired } from 'react-icons/fa' +import { FaServer, FaArrowDownUpLock } from 'react-icons/fa6' import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog' import { AddAppButton } from './_components/AddAppsToServiceAccountsButton' import { ServiceAccountType } from '@/apollo/graphql' @@ -22,6 +23,7 @@ import { SseLabel } from '@/components/apps/EncryptionModeIndicator' import { IPChip } from '../../network/_components/IPChip' import { UpdateAccountNetworkPolicies } from '@/components/access/UpdateAccountNetworkPolicies' import { ServiceAccountTokens } from './_components/ServiceAccountTokens' +import { KeyManagementDialog } from '@/components/service-accounts/KeyManagementDialog' export default function ServiceAccount({ params }: { params: { team: string; account: string } }) { const { activeOrganisation: organisation } = useContext(organisationContext) @@ -166,6 +168,29 @@ export default function ServiceAccount({ params }: { params: { team: string; acc )} {account.id} +
+ {account.serverSideKeyManagementEnabled ? ( + + ) : ( + + )} + KMS Mode: + + {account.serverSideKeyManagementEnabled ? 'Server-side' : 'Client-side'} key + management + + {userCanUpdateSA && ( + + + Manage + + } + /> + )} +
From b5302520e124464f4560262a2e1886d207417ef3 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 18:09:07 +0530 Subject: [PATCH 23/80] refactor: streamline KeyManagementDialog save logic and remove unnecessary state updates - Updated the save handler in KeyManagementDialog to close the modal immediately after saving, improving user experience. - Removed redundant state updates and comments related to mode changes, simplifying the code and enhancing clarity. - Adjusted the rendering logic to eliminate unnecessary alerts, focusing on relevant user feedback for key management actions. --- .../components/service-accounts/KeyManagementDialog.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/frontend/components/service-accounts/KeyManagementDialog.tsx b/frontend/components/service-accounts/KeyManagementDialog.tsx index 05e00c1fe..6f021274c 100644 --- a/frontend/components/service-accounts/KeyManagementDialog.tsx +++ b/frontend/components/service-accounts/KeyManagementDialog.tsx @@ -61,6 +61,7 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { const handleSave = async (e: { preventDefault: () => void }) => { e.preventDefault() + setIsOpen(false) if (selectedMode === 'server' && !serviceAccount.serverSideKeyManagementEnabled) { // Enable server-side encryption @@ -69,8 +70,6 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { // Disable server-side encryption (switch to client-side) await handleDisableSSE() } else { - // No change needed - closeModal() } } @@ -126,9 +125,6 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { error: 'Failed to enable server-side key management', } ) - // Ensure local state reflects new server-side mode - setSelectedMode('server') - closeModal() } catch (error) { console.error('Error enabling SSE:', error) toast.error('Failed to enable server-side key management') @@ -155,9 +151,6 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { error: 'Failed to switch to client-side key management', } ) - // Ensure local state reflects new client-side mode - setSelectedMode('client') - closeModal() } catch (error) { console.error('Error disabling SSE:', error) toast.error('Failed to switch to client-side key management') From aeb4bac83746859ed46f9af89288d59d96a0bded Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Aug 2025 18:09:15 +0530 Subject: [PATCH 24/80] refactor: remove unnecessary alert from KeyManagementDialog - Eliminated the alert related to server-side key management in the KeyManagementDialog component, streamlining the user interface and focusing on relevant feedback for client-side management. - This change enhances clarity and reduces visual clutter for users managing service account keys. --- .../components/service-accounts/KeyManagementDialog.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/frontend/components/service-accounts/KeyManagementDialog.tsx b/frontend/components/service-accounts/KeyManagementDialog.tsx index 6f021274c..d6ac9b7d3 100644 --- a/frontend/components/service-accounts/KeyManagementDialog.tsx +++ b/frontend/components/service-accounts/KeyManagementDialog.tsx @@ -338,15 +338,6 @@ export const KeyManagementDialog = (props: KeyManagementDialogProps) => { - {hasChanges && - selectedMode === 'server' && - !serviceAccount.serverSideKeyManagementEnabled && ( - - Enabling server-side key management will allow the server to securely - access keys and generate tokens on behalf of the Service Account. - - )} - {hasChanges && selectedMode === 'client' && serviceAccount.serverSideKeyManagementEnabled && ( From b40b3779be7a7dd23941182c0199c968fc0af3eb Mon Sep 17 00:00:00 2001 From: Nimish Date: Sun, 24 Aug 2025 22:33:07 +0530 Subject: [PATCH 25/80] feat: add TTL utility functions for human-readable time formats - Introduced a new module with functions to parse, format, and validate TTL strings, enhancing the handling of time-related data. - Added examples for common TTL formats, improving usability and clarity for developers working with time-to-live values. --- frontend/utils/ttl.ts | 85 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 frontend/utils/ttl.ts diff --git a/frontend/utils/ttl.ts b/frontend/utils/ttl.ts new file mode 100644 index 000000000..379fead7d --- /dev/null +++ b/frontend/utils/ttl.ts @@ -0,0 +1,85 @@ +// TTL conversion utilities for human-readable time formats + +export interface ParsedTTL { + value: number + unit: 's' | 'm' | 'h' | 'd' + seconds: number +} + +/** + * Parse a human-readable TTL string into seconds + * Supports: 60s (seconds), 10m (minutes), 100h (hours), 365d (days) + */ +export function parseTTL(ttlString: string): number { + const trimmed = ttlString.trim() + if (!trimmed) return 0 + + // If it's just a number, assume seconds + if (/^\d+$/.test(trimmed)) { + return parseInt(trimmed, 10) + } + + const match = trimmed.match(/^(\d+)([smhd])$/i) + if (!match) { + // If invalid format, try to parse as number + const num = parseInt(trimmed, 10) + return isNaN(num) ? 0 : num + } + + const value = parseInt(match[1], 10) + const unit = match[2].toLowerCase() as 's' | 'm' | 'h' | 'd' + + switch (unit) { + case 's': + return value + case 'm': + return value * 60 + case 'h': + return value * 60 * 60 + case 'd': + return value * 24 * 60 * 60 + default: + return value + } +} + +/** + * Format seconds into a human-readable TTL string + * Automatically chooses the most appropriate unit + */ +export function formatTTL(seconds: number): string { + if (seconds === 0) return '0s' + + // Find the largest unit that divides evenly + if (seconds % (24 * 60 * 60) === 0) { + return `${seconds / (24 * 60 * 60)}d` + } + if (seconds % (60 * 60) === 0) { + return `${seconds / (60 * 60)}h` + } + if (seconds % 60 === 0) { + return `${seconds / 60}m` + } + return `${seconds}s` +} + +/** + * Validate a TTL string format + */ +export function isValidTTL(ttlString: string): boolean { + const trimmed = ttlString.trim() + if (!trimmed) return false + + // Just a number is valid (assumes seconds) + if (/^\d+$/.test(trimmed)) return true + + // Format: number + unit + return /^\d+[smhd]$/i.test(trimmed) +} + +/** + * Get TTL examples for placeholders + */ +export function getTTLExamples(): string[] { + return ['60s', '10m', '2h', '7d'] +} From 7023dca2926dd75f48351bd241fac97ea44d53f4 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:47:46 +0530 Subject: [PATCH 26/80] feat: add IdentityProviders class for managing authentication configurations - Introduced a new IdentityProviders class to encapsulate configurations for supported identity providers, enhancing the structure for identity authentication. - Implemented methods to retrieve all providers, supported providers, and specific provider configurations, improving the usability and maintainability of identity management. --- backend/api/identity_providers.py | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 backend/api/identity_providers.py diff --git a/backend/api/identity_providers.py b/backend/api/identity_providers.py new file mode 100644 index 000000000..5a62bdca3 --- /dev/null +++ b/backend/api/identity_providers.py @@ -0,0 +1,57 @@ +class IdentityProviders: + """ + Configuration for supported identity providers. + Similar to Providers in services.py but specifically for identity authentication. + """ + + AWS_IAM = { + "id": "aws_iam", + "name": "AWS IAM", + "description": "Use AWS STS GetCallerIdentity to authenticate.", + "icon_id": "aws", # Maps to ProviderIcon component + "supported": True, + } + + # Future identity providers can be added here: + # GitHub OIDC + # "id": "github_oidc", + # "name": "GitHub OIDC", + # "description": "Use GitHub OIDC for authentication.", + # "icon_id": "github", + # "supported": False, + # } + # + # Kubernetes OIDC + # "id": "kubernetes_oidc", + # "name": "Kubernetes OIDC", + # "description": "Use Kubernetes OIDC for authentication.", + # "icon_id": "kubernetes", + # "supported": False, + # } + + @classmethod + def get_all_providers(cls): + """Get all identity providers, including unsupported ones for future roadmap display.""" + return [ + provider + for provider in cls.__dict__.values() + if isinstance(provider, dict) + ] + + @classmethod + def get_supported_providers(cls): + """Get only currently supported identity providers.""" + return [ + provider + for provider in cls.__dict__.values() + if isinstance(provider, dict) and provider.get("supported", False) + ] + + @classmethod + def get_provider_config(cls, provider_id): + """Get configuration for a specific provider by ID.""" + for provider in cls.__dict__.values(): + if isinstance(provider, dict) and provider["id"] == provider_id: + return provider + raise ValueError(f"Identity provider '{provider_id}' not found") + From 75149441d62f7c2aa3621e96ac80ac95fad6b5bf Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:48:37 +0530 Subject: [PATCH 27/80] feat: add Identity model for third-party identity configurations - Introduced a new Identity model to manage third-party identity configurations at the organization level, allowing multiple service accounts to be associated with a single identity. - Added fields for provider-specific configurations, token settings, and methods to retrieve trusted principals, enhancing the flexibility and usability of identity management. --- backend/api/models.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/backend/api/models.py b/backend/api/models.py index 8c5ec8c8c..fde4f810e 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -279,6 +279,9 @@ class ServiceAccount(models.Model): network_policies = models.ManyToManyField( NetworkAccessPolicy, blank=True, related_name="service_accounts" ) + identities = models.ManyToManyField( + "Identity", blank=True, related_name="service_accounts" + ) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(null=True, blank=True) @@ -732,6 +735,46 @@ class SecretEvent(models.Model): user_agent = models.TextField(null=True, blank=True) +class Identity(models.Model): + """ + Third-party identity configuration. + + Scope: Organisation level; can be attached to multiple ServiceAccounts. + """ + id = models.TextField(default=uuid4, primary_key=True, editable=False) + organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) + provider = models.CharField(max_length=64) + name = models.CharField(max_length=100) + description = models.TextField(blank=True, null=True) + + # Provider-specific configuration + # Example for aws_iam: + # { + # "trustedPrincipals": "arn:..., arn:...", + # "signatureTtlSeconds": 60, + # "stsEndpoint": "https://sts.amazonaws.com" + # } + config = models.JSONField(default=dict) + + # Token configuration + token_name_pattern = models.CharField(max_length=128, blank=True, null=True) + default_ttl_seconds = models.IntegerField(default=3600) + max_ttl_seconds = models.IntegerField(default=86400) + + created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(blank=True, null=True) + + def get_trusted_list(self): + try: + principals = self.config.get("trustedPrincipals", []) + if isinstance(principals, list): + return [p.strip() for p in principals if isinstance(p, str) and p.strip()] + # Fallback for legacy comma-separated string format + return [p.strip() for p in str(principals).split(",") if p.strip()] + except Exception: + return [] + class PersonalSecret(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) secret = models.ForeignKey(Secret, on_delete=models.CASCADE) From 6b2de442cdc305b3a085cd1ddf6a5377038d67b4 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:48:52 +0530 Subject: [PATCH 28/80] feat: add cryptographic utility functions for secret sharing and key conversion - Introduced new functions for XORing byte strings, splitting secrets into shares, generating random hex strings, and converting Ed25519 keys to Curve25519 format. - Enhanced cryptographic capabilities by implementing a wrapping function for encrypting hex-encoded shares, improving the overall utility of the crypto module. --- backend/api/utils/crypto.py | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/backend/api/utils/crypto.py b/backend/api/utils/crypto.py index f59757375..3ecfd86d5 100644 --- a/backend/api/utils/crypto.py +++ b/backend/api/utils/crypto.py @@ -1,6 +1,7 @@ import re from nacl.hash import blake2b from nacl.utils import random +from nacl.utils import random as random_bytes from base64 import b64encode, b64decode from django.conf import settings from nacl.bindings import ( @@ -12,6 +13,8 @@ crypto_kx_server_session_keys, crypto_secretbox_NONCEBYTES, crypto_generichash, + crypto_sign_ed25519_pk_to_curve25519, + crypto_sign_ed25519_sk_to_curve25519, ) from nacl.encoding import RawEncoder from typing import Tuple @@ -230,3 +233,94 @@ def validate_encrypted_string(encrypted_string): return bool(match) return True + + +def xor_bytes(first: bytes, second: bytes) -> bytes: + """ + XOR two byte strings of equal length. + + Args: + first: First byte string. + second: Second byte string. Must be the same length as `first`. + + Returns: + A new byte string containing the XOR of the inputs. + + Raises: + ValueError: If the inputs are not the same length. + """ + if len(first) != len(second): + raise ValueError("xor_bytes inputs must be the same length") + return bytes(x ^ y for x, y in zip(first, second)) + + +def split_secret_hex(secret_hex: str) -> Tuple[str, str]: + """ + Split a secret (hex string) into two additive/XOR shares. + + This uses a one-time pad approach where a uniformly random share is + generated and the second share is computed as share2 = random ^ secret. + + Args: + secret_hex: Secret as a hex string. + + Returns: + Tuple of two hex strings (share_a, share_b). + """ + secret_bytes = bytes.fromhex(secret_hex) + random_share = random_bytes(len(secret_bytes)) + last_share = xor_bytes(random_share, secret_bytes) + return random_share.hex(), last_share.hex() + + +def random_hex(nbytes: int = 32) -> str: + """ + Generate a random hex string. + + Args: + nbytes: Number of random bytes to generate. Defaults to 32. + + Returns: + Hex string of length 2 * nbytes. + """ + + return random_bytes(nbytes).hex() + + +def ed25519_to_kx(pub_hex: str, priv_hex: str) -> Tuple[str, str]: + """ + Convert an Ed25519 keypair to Curve25519 (key exchange) form. + + This is useful when the stored signing keys are in Ed25519 form but we + require Curve25519 keys for symmetric key agreement operations. + + Args: + pub_hex: Ed25519 public key in hex. + priv_hex: Ed25519 private key in hex. + + Returns: + Tuple of hex strings (kx_public_hex, kx_private_hex). + """ + + pub_kx = crypto_sign_ed25519_pk_to_curve25519(bytes.fromhex(pub_hex)).hex() + priv_kx = crypto_sign_ed25519_sk_to_curve25519(bytes.fromhex(priv_hex)).hex() + return pub_kx, priv_kx + + +def wrap_share_hex(share_hex: str, wrap_key_hex: str) -> str: + """ + Wrap (encrypt) a hex-encoded share using a symmetric key, returning hex. + + This reuses the module's low-level XChaCha20-Poly1305 helper `encrypt_raw` + to avoid duplicate cryptographic code paths. + + Args: + share_hex: The plaintext share to encrypt, as a hex string. + wrap_key_hex: Symmetric key as a hex string (32-byte key recommended). + + Returns: + A hex string representing ciphertext||nonce. + """ + key_bytes = bytes.fromhex(wrap_key_hex) + ct_plus_nonce = encrypt_raw(share_hex, key_bytes) + return bytes(ct_plus_nonce).hex() From 8f18ed50d333b230a6ba4b9c6654dfdb4b2c31a3 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:49:20 +0530 Subject: [PATCH 29/80] feat: add function to list STS endpoints from botocore metadata - Introduced a new function `list_sts_endpoints` that retrieves and formats a list of STS endpoints based on botocore's endpoint metadata. - The function enhances AWS identity management by providing a structured output of available STS endpoints, including region codes and names. --- backend/api/utils/identity/aws.py | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 backend/api/utils/identity/aws.py diff --git a/backend/api/utils/identity/aws.py b/backend/api/utils/identity/aws.py new file mode 100644 index 000000000..80cabef45 --- /dev/null +++ b/backend/api/utils/identity/aws.py @@ -0,0 +1,43 @@ +import botocore.loaders + + +def list_sts_endpoints(): + """ + Return a list of STS endpoints from botocore's endpoint metadata. + + Output: List[dict(regionCode, regionName, endpoint)] + """ + loader = botocore.loaders.create_loader() + endpoints_data = loader.load_data("endpoints") + partitions = endpoints_data.get("partitions", []) + results = [] + for part in partitions: + services = part.get("services", {}) + if "sts" not in services: + continue + service = services["sts"] + endpoints = service.get("endpoints", {}) + for region_code, meta in endpoints.items(): + hostname = meta.get("hostname") + if not hostname: + hostname = f"sts.{region_code}.{part.get('dnsSuffix', 'amazonaws.com')}" + region_name = region_code + results.append( + { + "regionCode": region_code, + "regionName": region_name, + "endpoint": f"https://{hostname}", + } + ) + + # Add global legacy first + # results.insert( + # 0, + # { + # "regionCode": None, + # "regionName": "Global (Legacy)", + # "endpoint": "https://sts.amazonaws.com", + # }, + # ) + + return results From 3d810efc98d8b8f9522b6ed3aa568acb23719089 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:50:07 +0530 Subject: [PATCH 30/80] feat: implement service account token management utilities - Added functions for resolving service accounts and identities, minting service account tokens, and handling token expiration. - Enhanced the identity management system by providing a structured approach to create and manage service account tokens, including cryptographic operations for secure token generation. --- backend/api/utils/identity/common.py | 77 ++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 backend/api/utils/identity/common.py diff --git a/backend/api/utils/identity/common.py b/backend/api/utils/identity/common.py new file mode 100644 index 000000000..ab2cca1e8 --- /dev/null +++ b/backend/api/utils/identity/common.py @@ -0,0 +1,77 @@ +import json + +from django.utils import timezone + + +def resolve_service_account(account_id): + from api.models import ServiceAccount + + try: + service_account = ServiceAccount.objects.get(id=account_id) + except ServiceAccount.DoesNotExist: + return None + return service_account + + +def resolve_attached_identity(service_account, provider: str): + """Return the first non-deleted identity for the service account and provider.""" + return service_account.identities.filter(provider=provider, deleted_at=None).first() + + +def mint_service_account_token(service_account, identity, requested_ttl: int, token_name_fallback: str): + """ + Create a ServiceAccountToken for the given service account using the server + keyring, honoring the identity's TTL rules. Returns a dict suitable for + JSON response containing token strings and TTLs. + """ + from api.utils.crypto import ( + get_server_keypair, + decrypt_asymmetric, + split_secret_hex, + wrap_share_hex, + random_hex, + ed25519_to_kx, + ) + from api.models import ServiceAccountToken + + now = timezone.now() + + # Load and unwrap server-managed keyring + pk, sk = get_server_keypair() + keyring_json = decrypt_asymmetric( + service_account.server_wrapped_keyring, sk.hex(), pk.hex() + ) + keyring = json.loads(keyring_json) + kx_pub, kx_priv = ed25519_to_kx(keyring["publicKey"], keyring["privateKey"]) + + # Compute TTL consistent with limits + req_ttl = int(requested_ttl or identity.default_ttl_seconds) + ttl = min(req_ttl, identity.max_ttl_seconds) + expires_at = now + timezone.timedelta(seconds=ttl) + + # Create token material + wrap_key = random_hex(32) + token_value = random_hex(32) + share_a, share_b = split_secret_hex(kx_priv) + wrapped_share_b = wrap_share_hex(share_b, wrap_key) + + ServiceAccountToken.objects.create( + service_account=service_account, + name=(identity.token_name_pattern or token_name_fallback), + identity_key=kx_pub, + token=token_value, + wrapped_key_share=wrapped_share_b, + created_by_service_account=service_account, + expires_at=expires_at, + ) + + full_token = f"pss_service:v2:{token_value}:{kx_pub}:{share_a}:{wrap_key}" + bearer = f"ServiceAccount {token_value}" + + return { + "tokenType": "ServiceAccount", + "token": full_token, + "bearerToken": bearer, + "TTL": ttl, + "maxTTL": identity.max_ttl_seconds, + } From 120ec7fcfd05b6f01256e91855080763db8a0099 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:52:40 +0530 Subject: [PATCH 31/80] feat: implement AWS IAM authentication endpoint for service accounts - Added a new endpoint `aws_iam_auth` to handle SigV4-signed requests for service account authentication. - Implemented request validation, signature verification, and integration with AWS STS for identity validation. - Enhanced error handling for various failure scenarios, ensuring robust responses for invalid requests and configurations. --- backend/api/views/identities/aws/iam.py | 164 ++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 backend/api/views/identities/aws/iam.py diff --git a/backend/api/views/identities/aws/iam.py b/backend/api/views/identities/aws/iam.py new file mode 100644 index 000000000..16b2b5d71 --- /dev/null +++ b/backend/api/views/identities/aws/iam.py @@ -0,0 +1,164 @@ +import base64 +import json +import re +from urllib.parse import urlparse + +import requests +from django.http import JsonResponse +from django.utils import timezone +from rest_framework.decorators import api_view, permission_classes +from rest_framework.permissions import AllowAny + +from api.utils.identity.common import ( + resolve_service_account, + resolve_attached_identity, + mint_service_account_token, +) + + +@api_view(["POST"]) +@permission_classes([AllowAny]) +def aws_iam_auth(request): + """Accepts SigV4-signed STS GetCallerIdentity request and issues a ServiceAccount token if trusted.""" + try: + payload = json.loads(request.body) + except Exception: + return JsonResponse({"error": "Invalid JSON"}, status=400) + + account = (payload or {}).get("account", {}) + aws_iam = (payload or {}).get("awsIam", {}) + token_req = (payload or {}).get("tokenRequest", {}) + + account_type = (account.get("type") or "service").lower() + account_id = account.get("id") + if account_type != "service" or not account_id: + return JsonResponse({"error": "Only service account authentication supported"}, status=400) + + # Decode signed request + try: + method = aws_iam.get("httpRequestMethod") or "POST" + url = base64.b64decode(aws_iam["httpRequestUrl"]).decode("utf-8") + headers_json = base64.b64decode(aws_iam["httpRequestHeaders"]).decode("utf-8") + body = base64.b64decode(aws_iam.get("httpRequestBody") or b"").decode("utf-8") + headers = json.loads(headers_json) + except Exception: + return JsonResponse({"error": "Invalid awsIam request encoding"}, status=400) + + # Verify signature freshness using X-Amz-Date + amz_date = headers.get("X-Amz-Date") or headers.get("x-amz-date") + if not amz_date: + return JsonResponse({"error": "Missing X-Amz-Date header"}, status=400) + try: + from datetime import datetime, timezone as dt_timezone + + dt = ( + datetime.strptime(amz_date.replace("Z", ""), "%Y%m%dT%H%M%S") + .replace(tzinfo=dt_timezone.utc) + ) + except Exception: + return JsonResponse({"error": "Invalid X-Amz-Date"}, status=400) + + # Resolve account and identity + service_account = resolve_service_account(account_id) + if service_account is None: + return JsonResponse({"error": "Service account not found"}, status=404) + if not service_account.server_wrapped_keyring: + return JsonResponse({"error": "Server-side key management must be enabled"}, status=403) + + identity = resolve_attached_identity(service_account, "aws_iam") + if not identity: + return JsonResponse({"error": "No AWS IAM identity attached to this account"}, status=404) + + try: + max_skew = int(identity.config.get("signatureTtlSeconds", 60)) + except Exception: + max_skew = 60 + now = timezone.now() + if abs((now - dt).total_seconds()) > max_skew: + return JsonResponse({"error": "Signature expired"}, status=401) + + # Enforce that the signed request targets the identity's configured STS endpoint + try: + configured = identity.config.get("stsEndpoint") + if not configured.startswith("http"): + configured = f"https://{configured}" + request_url = url + if not request_url.startswith("http"): + request_url = f"https://{request_url}" + + cfg_host = urlparse(configured).netloc.lower() + req_host = urlparse(request_url).netloc.lower() + header_host = (headers.get("Host") or headers.get("host") or "").lower() + + if req_host != cfg_host or (header_host and header_host != cfg_host): + return JsonResponse( + { + "error": "STS endpoint mismatch. Please sign the request for the configured STS endpoint.", + "expectedEndpoint": configured, + "receivedEndpoint": request_url, + }, + status=400, + ) + except Exception: + return JsonResponse({"error": "Invalid STS endpoint configuration"}, status=400) + + # Forward the signed request to AWS STS + try: + resp = requests.request(method, url, headers=headers, data=body) + except Exception: + return JsonResponse({"error": "Failed to contact AWS STS"}, status=502) + + if resp.status_code != 200: + return JsonResponse({"error": "AWS STS validation failed", "status": resp.status_code}, status=401) + + # Parse minimal identity info from XML + arn = None + try: + m = re.search(r"([^<]+)", resp.text) + if m: + arn = m.group(1) + except Exception: + pass + if arn is None: + return JsonResponse({"error": "Unable to parse AWS identity"}, status=500) + + # Trust check + import fnmatch + + trusted = identity.get_trusted_list() + if any(p == "*" for p in trusted): + return JsonResponse({"error": "Invalid trusted principal pattern '*' is not allowed"}, status=400) + + def arn_matches_patterns(value: str, patterns: list[str]) -> bool: + for pattern in patterns: + if not pattern or pattern == "*": + continue + if fnmatch.fnmatch(value, pattern): + return True + return False + + if not arn_matches_patterns(arn, trusted): + return JsonResponse({"error": "Untrusted principal"}, status=403) + + # Validate requested TTL + requested_ttl = int(token_req.get("ttl") or identity.default_ttl_seconds) + max_ttl = identity.max_ttl_seconds + + if requested_ttl > max_ttl: + return JsonResponse({ + "error": f"Requested TTL ({requested_ttl}s) exceeds maximum allowed TTL ({max_ttl}s)" + }, status=400) + + try: + auth = mint_service_account_token( + service_account, + identity, + requested_ttl, + token_name_fallback="aws-iam", + ) + except Exception: + return JsonResponse({"error": "Failed to mint token"}, status=500) + + return JsonResponse({"authentication": auth}) + + From f06add9bce773953ae04b19d7eca95af229742d2 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:53:00 +0530 Subject: [PATCH 32/80] feat: add identity management mutations and queries - Introduced new mutations for creating, updating, and deleting identities, enhancing the identity management capabilities. - Added queries to retrieve identities, AWS STS endpoints, and identity providers, improving the overall functionality of the identity system. - Updated the GraphQL schema to include new types and resolvers for better integration with identity-related operations. --- backend/backend/schema.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 9d9a38635..e9efcb9da 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -25,6 +25,9 @@ CreateNetworkAccessPolicyMutation, DeleteCustomRoleMutation, DeleteNetworkAccessPolicyMutation, + CreateIdentityMutation, + UpdateIdentityMutation, + DeleteIdentityMutation, UpdateAccountNetworkAccessPolicies, UpdateCustomRoleMutation, UpdateNetworkAccessPolicyMutation, @@ -68,11 +71,13 @@ resolve_validate_aws_assume_role_auth, resolve_validate_aws_assume_role_credentials, ) +from .graphene.queries.identity import resolve_aws_sts_endpoints, resolve_identity_providers from .graphene.queries.access import ( resolve_roles, resolve_organisation_global_access_users, resolve_network_access_policies, resolve_client_ip, + resolve_identities, ) from .graphene.queries.service_accounts import ( resolve_service_accounts, @@ -166,6 +171,7 @@ PhaseLicenseType, ProviderCredentialsType, ProviderType, + IdentityProviderType, RoleType, SecretEventType, SecretFolderType, @@ -179,6 +185,7 @@ TimeRange, UserTokenType, AWSValidationResultType, + IdentityType, ) import graphene from graphql import GraphQLError @@ -218,6 +225,7 @@ class Query(graphene.ObjectType): network_access_policies = graphene.List( NetworkAccessPolicyType, organisation_id=graphene.ID() ) + identities = graphene.List(IdentityType, organisation_id=graphene.ID()) organisation_name_available = graphene.Boolean(name=graphene.String()) @@ -325,6 +333,8 @@ class Query(graphene.ObjectType): providers = graphene.List(ProviderType) services = graphene.List(ServiceType) + aws_sts_endpoints = graphene.List(graphene.JSONString) + identity_providers = graphene.List(IdentityProviderType) saved_credentials = graphene.List(ProviderCredentialsType, org_id=graphene.ID()) @@ -448,6 +458,12 @@ def resolve_organisations(root, info): resolve_roles = resolve_roles resolve_network_access_policies = resolve_network_access_policies + # Identities + resolve_identities = resolve_identities + resolve_aws_sts_endpoints = resolve_aws_sts_endpoints + resolve_identity_providers = resolve_identity_providers + + resolve_organisation_plan = resolve_organisation_plan def resolve_organisation_name_available(root, info, name): @@ -930,6 +946,11 @@ class Mutation(graphene.ObjectType): delete_network_access_policy = DeleteNetworkAccessPolicyMutation.Field() update_account_network_access_policies = UpdateAccountNetworkAccessPolicies.Field() + # Identities + create_identity = CreateIdentityMutation.Field() + update_identity = UpdateIdentityMutation.Field() + delete_identity = DeleteIdentityMutation.Field() + # Service Accounts create_service_account = CreateServiceAccountMutation.Field() enable_service_account_server_side_key_management = ( From b9c9a20dff3942eb0dd1e97075c7a267fd2f8505 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:53:25 +0530 Subject: [PATCH 33/80] feat: add AWS IAM authentication route to URL configuration - Added a new URL path for the `aws_iam_auth` endpoint, enabling access to AWS IAM authentication for identity management. - This integration enhances the existing identity system by providing a dedicated route for service account authentication. --- backend/backend/urls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/backend/urls.py b/backend/backend/urls.py index eaac0f175..7143946ea 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -12,6 +12,7 @@ secrets_tokens, root_endpoint, ) +from api.views.identities.aws.iam import aws_iam_auth from api.views.kms import kms @@ -30,6 +31,7 @@ path("secrets/", E2EESecretsView.as_view()), path("public/v1/secrets/", PublicSecretsView.as_view()), path("secrets/tokens/", secrets_tokens), + path("identity/v1/aws/iam/auth", aws_iam_auth), path("oauth/github/callback", github_integration_callback), path("lockbox/", LockboxView.as_view()), ] From 5152e90902e2414d58d188e38d43d9b421135060 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:53:51 +0530 Subject: [PATCH 34/80] feat: add identity management mutations for create, update, and delete operations - Introduced new mutations for creating, updating, and deleting identities, enhancing the identity management capabilities. - Implemented permission checks to ensure users have the necessary rights for identity operations. - Updated the GraphQL schema to include the new IdentityType and associated mutations, improving integration with identity-related functionalities. --- backend/backend/graphene/mutations/access.py | 169 ++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/backend/backend/graphene/mutations/access.py b/backend/backend/graphene/mutations/access.py index ada6f4ee5..57433b20f 100644 --- a/backend/backend/graphene/mutations/access.py +++ b/backend/backend/graphene/mutations/access.py @@ -6,7 +6,9 @@ ServiceAccount, ) from api.utils.access.permissions import user_has_permission -from backend.graphene.types import NetworkAccessPolicyType, RoleType +from backend.graphene.types import NetworkAccessPolicyType, RoleType, IdentityType +from api.models import Identity +from django.utils import timezone import graphene from graphql import GraphQLError @@ -221,6 +223,171 @@ def mutate(cls, root, info, id): return DeleteNetworkAccessPolicyMutation(ok=True) +class CreateIdentityMutation(graphene.Mutation): + class Arguments: + organisation_id = graphene.ID(required=True) + provider = graphene.String(required=True) + name = graphene.String(required=True) + description = graphene.String(required=False) + trusted_principals = graphene.String(required=True) + signature_ttl_seconds = graphene.Int(required=False) + sts_endpoint = graphene.String(required=False) + token_name_pattern = graphene.String(required=False) + default_ttl_seconds = graphene.Int(required=True) + max_ttl_seconds = graphene.Int(required=True) + + identity = graphene.Field(IdentityType) + + @classmethod + def mutate( + cls, + root, + info, + organisation_id, + provider, + name, + trusted_principals, + default_ttl_seconds, + max_ttl_seconds, + description=None, + signature_ttl_seconds=60, + sts_endpoint="https://sts.amazonaws.com", + token_name_pattern=None, + ): + user = info.context.user + org = Organisation.objects.get(id=organisation_id) + + if not user_has_permission(user, "create", "Identities", org): + raise GraphQLError( + "You don't have the permissions required to create identities in this organisation" + ) + + if default_ttl_seconds is not None and max_ttl_seconds is not None: + if int(default_ttl_seconds) > int(max_ttl_seconds): + raise GraphQLError( + "Default token expiry must be less than or equal to Maximum token expiry" + ) + + # Store provider-specific configuration in a generic config field + # Convert comma-separated trusted_principals to list for consistency + trusted_list = [p.strip() for p in trusted_principals.split(",") if p.strip()] + config = { + "trustedPrincipals": trusted_list, + "signatureTtlSeconds": signature_ttl_seconds, + "stsEndpoint": sts_endpoint, + } + + identity = Identity.objects.create( + organisation=org, + provider=provider, + name=name, + description=description, + config=config, + token_name_pattern=token_name_pattern, + default_ttl_seconds=default_ttl_seconds, + max_ttl_seconds=max_ttl_seconds, + ) + + return CreateIdentityMutation(identity=identity) + + +class UpdateIdentityMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + name = graphene.String(required=False) + description = graphene.String(required=False) + trusted_principals = graphene.String(required=False) + signature_ttl_seconds = graphene.Int(required=False) + sts_endpoint = graphene.String(required=False) + token_name_pattern = graphene.String(required=False) + default_ttl_seconds = graphene.Int(required=False) + max_ttl_seconds = graphene.Int(required=False) + + identity = graphene.Field(IdentityType) + + @classmethod + def mutate( + cls, + root, + info, + id, + name=None, + description=None, + trusted_principals=None, + signature_ttl_seconds=None, + sts_endpoint=None, + token_name_pattern=None, + default_ttl_seconds=None, + max_ttl_seconds=None, + ): + user = info.context.user + identity = Identity.objects.get(id=id, deleted_at=None) + + org = identity.organisation + if not user_has_permission(user, "update", "Identities", org): + raise GraphQLError( + "You don't have the permissions required to update identities in this organisation" + ) + + # Update basic fields using dictionary unpacking + basic_updates = { + k: v for k, v in { + 'name': name, + 'description': description, + 'token_name_pattern': token_name_pattern, + 'default_ttl_seconds': default_ttl_seconds, + 'max_ttl_seconds': max_ttl_seconds, + }.items() if v is not None + } + for field, value in basic_updates.items(): + setattr(identity, field, value) + + # Update provider-specific config atomically + config_updates = {} + if trusted_principals is not None: + config_updates["trustedPrincipals"] = [p.strip() for p in trusted_principals.split(",") if p.strip()] + if signature_ttl_seconds is not None: + config_updates["signatureTtlSeconds"] = signature_ttl_seconds + if sts_endpoint is not None: + config_updates["stsEndpoint"] = sts_endpoint + + if config_updates: + identity.config = {**(identity.config or {}), **config_updates} + + if identity.default_ttl_seconds is not None and identity.max_ttl_seconds is not None: + if int(identity.default_ttl_seconds) > int(identity.max_ttl_seconds): + raise GraphQLError( + "Default token expiry must be less than or equal to Maximum token expiry" + ) + + identity.save() + + return UpdateIdentityMutation(identity=identity) + + +class DeleteIdentityMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + + ok = graphene.Boolean() + + @classmethod + def mutate(cls, root, info, id): + user = info.context.user + identity = Identity.objects.get(id=id, deleted_at=None) + + org = identity.organisation + if not user_has_permission(user, "delete", "Identities", org): + raise GraphQLError( + "You don't have the permissions required to delete identities in this organisation" + ) + + identity.deleted_at = timezone.now() + identity.save() + + return DeleteIdentityMutation(ok=True) + + class AccountTypeEnum(graphene.Enum): USER = "user" SERVICE = "service" From 6027a11d4b969e05fc6db9b601c47f5aff300289 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:55:14 +0530 Subject: [PATCH 35/80] feat: enhance GraphQL schema with Identity and IdentityProvider types - Added IdentityProviderType to represent identity providers with relevant fields. - Introduced IdentityType to manage identity configurations, including a resolver for provider-specific settings. - Updated ServiceAccountType to include a list of identities, improving the integration of identity management within service accounts. --- backend/backend/graphene/types.py | 52 +++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 6e32f3202..ec531fd20 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -11,7 +11,6 @@ from graphene_django import DjangoObjectType from api.models import ( ActivatedPhaseLicense, - CustomUser, Environment, EnvironmentKey, EnvironmentSync, @@ -30,12 +29,12 @@ SecretEvent, SecretFolder, SecretTag, - ServerEnvironmentKey, ServiceAccount, ServiceAccountHandler, ServiceAccountToken, ServiceToken, UserToken, + Identity, ) from logs.dynamodb_models import KMSLog from django.utils import timezone @@ -327,6 +326,14 @@ class ProviderType(graphene.ObjectType): auth_scheme = graphene.String() +class IdentityProviderType(graphene.ObjectType): + id = graphene.String(required=True) + name = graphene.String(required=True) + description = graphene.String(required=True) + icon_id = graphene.String(required=True) + supported = graphene.Boolean(required=True) + + class ServiceType(ObjectType): id = graphene.String() name = graphene.String() @@ -681,6 +688,7 @@ class ServiceAccountType(DjangoObjectType): tokens = graphene.List(ServiceAccountTokenType) app_memberships = graphene.List(graphene.NonNull(AppMembershipType)) network_policies = graphene.List(graphene.NonNull(lambda: NetworkAccessPolicyType)) + identities = graphene.List(graphene.NonNull(lambda: IdentityType)) class Meta: model = ServiceAccount @@ -740,6 +748,9 @@ def resolve_network_policies(self, info): return list(chain(account_policies, global_policies)) + def resolve_identities(self, info): + return self.identities.filter(deleted_at=None) + class EnvironmentKeyType(DjangoObjectType): class Meta: @@ -994,3 +1005,40 @@ class AWSValidationResultType(graphene.ObjectType): method = graphene.String() error = graphene.String() assumed_role_arn = graphene.String() + + +class AwsIamConfigType(graphene.ObjectType): + trusted_principals = graphene.List(graphene.String) + signature_ttl_seconds = graphene.Int() + sts_endpoint = graphene.String() + + +class IdentityConfigUnion(graphene.Union): + class Meta: + types = (AwsIamConfigType,) + + +class IdentityType(DjangoObjectType): + config = graphene.Field(IdentityConfigUnion) + + class Meta: + model = Identity + fields = "__all__" + + def resolve_config(self, info): + """Map provider-specific config into typed objects""" + provider = (self.provider or '').lower() + cfg = self.config or {} + + if provider == 'aws_iam': + try: + ttl = int(cfg.get('signatureTtlSeconds', 60)) + except Exception: + ttl = 60 + return AwsIamConfigType( + trusted_principals=self.get_trusted_list(), + signature_ttl_seconds=ttl, + sts_endpoint=cfg.get('stsEndpoint'), + ) + + return None From 45761adafbedb2535f9cc717e47cfd4272541a02 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:55:35 +0530 Subject: [PATCH 36/80] feat: update UpdateServiceAccountMutation to include identity management - Enhanced the UpdateServiceAccountMutation to accept an optional list of identity IDs. - Implemented logic to associate specified identities with the service account during the update process, improving identity management capabilities within service accounts. --- .../backend/graphene/mutations/service_accounts.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 2b8bb9716..2b99c476b 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -7,6 +7,7 @@ ServiceAccount, ServiceAccountHandler, ServiceAccountToken, + Identity, ) from api.utils.access.permissions import user_has_permission, user_is_org_member from backend.graphene.types import ServiceAccountTokenType, ServiceAccountType @@ -151,11 +152,12 @@ class Arguments: service_account_id = graphene.ID() name = graphene.String() role_id = graphene.ID() + identity_ids = graphene.List(graphene.NonNull(graphene.ID), required=False) service_account = graphene.Field(ServiceAccountType) @classmethod - def mutate(cls, root, info, service_account_id, name, role_id): + def mutate(cls, root, info, service_account_id, name, role_id, identity_ids=None): user = info.context.user service_account = ServiceAccount.objects.get(id=service_account_id) @@ -169,6 +171,13 @@ def mutate(cls, root, info, service_account_id, name, role_id): role = Role.objects.get(id=role_id) service_account.name = name service_account.role = role + if identity_ids is not None: + identities = Identity.objects.filter( + id__in=identity_ids, + organisation=service_account.organisation, + deleted_at=None, + ) + service_account.identities.set(identities) service_account.save() return UpdateServiceAccountMutation(service_account=service_account) From 9725fbbea7b6ebb139b314b9c1f30deab17881e9 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:55:55 +0530 Subject: [PATCH 37/80] feat: add Identities role to default roles configuration - Introduced the "Identities" role with permissions for create, read, update, and delete operations in the default roles configuration. - Updated multiple sections of the roles.py file to ensure consistent access control for identity management functionalities. --- backend/api/utils/access/roles.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/api/utils/access/roles.py b/backend/api/utils/access/roles.py index b3723fd16..96a213ac4 100644 --- a/backend/api/utils/access/roles.py +++ b/backend/api/utils/access/roles.py @@ -12,6 +12,7 @@ "MemberPersonalAccessTokens": ["create", "read", "update", "delete"], "ServiceAccounts": ["create", "read", "update", "delete"], "ServiceAccountTokens": ["create", "read", "update", "delete"], + "Identities": ["create", "read", "update", "delete"], "Roles": ["create", "read", "update", "delete"], "IntegrationCredentials": ["create", "read", "update", "delete"], "NetworkAccessPolicies": ["create", "read", "update", "delete"], @@ -42,6 +43,7 @@ "MemberPersonalAccessTokens": ["create", "read", "update", "delete"], "ServiceAccounts": ["create", "read", "update", "delete"], "ServiceAccountTokens": ["create", "read", "update", "delete"], + "Identities": ["create", "read", "update", "delete"], "Roles": ["create", "read", "update", "delete"], "IntegrationCredentials": ["create", "read", "update", "delete"], "NetworkAccessPolicies": ["create", "read", "update", "delete"], @@ -71,6 +73,7 @@ "Members": ["create", "read", "update", "delete"], "ServiceAccounts": ["create", "read", "update", "delete"], "ServiceAccountTokens": ["create", "read", "update", "delete"], + "Identities": ["create", "read", "update", "delete"], "Roles": ["create", "read", "update", "delete"], "IntegrationCredentials": ["create", "read", "update", "delete"], "NetworkAccessPolicies": ["create", "read", "update", "delete"], @@ -100,6 +103,7 @@ "Members": ["read"], "ServiceAccounts": [], "ServiceAccountTokens": [], + "Identities": [], "Roles": ["read"], "IntegrationCredentials": [ "create", @@ -133,6 +137,7 @@ "Members": ["read"], "ServiceAccounts": ["read"], "ServiceAccountTokens": ["read"], + "Identities": ["read"], "Roles": ["read"], "IntegrationCredentials": ["read"], "NetworkAccessPolicies": ["read"], From 77d9316a77f5b462be7cd4e4d1318ec14cb5dc61 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:56:39 +0530 Subject: [PATCH 38/80] feat: add resolver for identities in organisation - Implemented a new resolver function to retrieve identities associated with a specific organisation. - Added permission checks to ensure users have access to read identities, enhancing security and access control within the identity management system. --- backend/backend/graphene/queries/access.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/backend/backend/graphene/queries/access.py b/backend/backend/graphene/queries/access.py index d7ba3740d..040a0fb91 100644 --- a/backend/backend/graphene/queries/access.py +++ b/backend/backend/graphene/queries/access.py @@ -1,5 +1,5 @@ from api.utils.access.permissions import user_has_permission, user_is_org_member -from api.models import NetworkAccessPolicy, Organisation, OrganisationMember, Role +from api.models import NetworkAccessPolicy, Organisation, OrganisationMember, Role, Identity from graphql import GraphQLError from django.db import transaction from api.utils.access.roles import default_roles @@ -97,3 +97,20 @@ def resolve_client_ip(root, info): else: ip = request.META.get("REMOTE_ADDR") return ip + + +def resolve_identities(root, info, organisation_id): + if not user_is_org_member(info.context.user.userId, organisation_id): + raise GraphQLError("You don't have access to this organisation") + + if user_has_permission( + info.context.user.userId, + "read", + "Identities", + Organisation.objects.get(id=organisation_id), + ): + return Identity.objects.filter(organisation_id=organisation_id, deleted_at=None) + else: + raise GraphQLError( + "You don't have permission to read identities in this Organisation" + ) From 4f7cd2bd39447ae8bd91129659dc33f54c829c3d Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:56:51 +0530 Subject: [PATCH 39/80] feat: add resolvers for AWS STS endpoints and identity providers - Implemented a resolver to dynamically return AWS STS endpoints using botocore's endpoint resolver data. - Added a resolver to retrieve all supported identity providers, enhancing the identity management capabilities within the GraphQL API. --- backend/backend/graphene/queries/identity.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 backend/backend/graphene/queries/identity.py diff --git a/backend/backend/graphene/queries/identity.py b/backend/backend/graphene/queries/identity.py new file mode 100644 index 000000000..20490f36b --- /dev/null +++ b/backend/backend/graphene/queries/identity.py @@ -0,0 +1,20 @@ +from graphql import GraphQLError +from api.identity_providers import IdentityProviders +from backend.graphene.types import IdentityProviderType + + +def resolve_aws_sts_endpoints(root, info): + """Return STS endpoints dynamically using botocore's endpoint resolver data.""" + try: + from api.utils.identity.aws import list_sts_endpoints + return list_sts_endpoints() + except Exception as ex: + raise GraphQLError(f"Failed to load AWS STS endpoints: {ex}") + + +def resolve_identity_providers(root, info): + """Get all supported identity providers.""" + return [ + IdentityProviderType(**provider) + for provider in IdentityProviders.get_supported_providers() + ] From 8e937a8ce1d0280fc0b34d0f06e44422dd8e24dc Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:57:26 +0530 Subject: [PATCH 40/80] feat: implement identity management page for organisation - Created a new IdentityPage component to manage identities within an organisation. - Integrated permission checks for reading, creating, updating, and deleting identities. - Added functionality to display, edit, and delete identities, along with a provider selection dialog for creating new identities. - Enhanced user experience with empty state handling and toast notifications for actions. --- .../app/[team]/access/identities/page.tsx | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 frontend/app/[team]/access/identities/page.tsx diff --git a/frontend/app/[team]/access/identities/page.tsx b/frontend/app/[team]/access/identities/page.tsx new file mode 100644 index 000000000..d3be0376b --- /dev/null +++ b/frontend/app/[team]/access/identities/page.tsx @@ -0,0 +1,279 @@ +'use client' + +import { useContext, useState } from 'react' +import { organisationContext } from '@/contexts/organisationContext' +import { userHasPermission } from '@/utils/access/permissions' +import { useMutation, useQuery } from '@apollo/client' +import GetOrganisationIdentities from '@/graphql/queries/identities/getOrganisationIdentities.gql' +import DeleteIdentity from '@/graphql/mutations/identities/deleteIdentity.gql' +import { Button } from '@/components/common/Button' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' +import { FaPlus, FaTrashAlt, FaEdit, FaBan } from 'react-icons/fa' +import { EmptyState } from '@/components/common/EmptyState' +import { toast } from 'react-toastify' +import { IdentityProviderSelector } from '@/components/identities/IdentityProviderSelector' +import { AwsIamIdentityDialog } from '@/components/identities/providers/aws/iam' +import { ProviderCards } from '@/components/identities/ProviderCards' +import GetIdentityProviders from '@/graphql/queries/identities/getIdentityProviders.gql' + +type AwsIamConfig = { + trustedPrincipals: string[] + signatureTtlSeconds: number + stsEndpoint: string +} + +type Identity = { + id: string + provider: string + name: string + description?: string | null + config: AwsIamConfig + tokenNamePattern?: string | null + defaultTtlSeconds: number + maxTtlSeconds: number +} + +export default function IdentityPage() { + const { activeOrganisation: organisation } = useContext(organisationContext) + + // Permission checks + const canReadIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'Identities', 'read') + : false + const canCreateIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'Identities', 'create') + : false + const canUpdateIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'Identities', 'update') + : false + const canDeleteIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'Identities', 'delete') + : false + + const { data, refetch } = useQuery(GetOrganisationIdentities, { + variables: { organisationId: organisation?.id }, + skip: !organisation || !canReadIdentities, + fetchPolicy: 'cache-and-network', + }) + + const { data: providersData } = useQuery(GetIdentityProviders) + + const [deleteIdentity, { loading: deleting }] = useMutation(DeleteIdentity) + + const [providerSelectorOpen, setProviderSelectorOpen] = useState(false) + const [awsEditDialogOpen, setAwsEditDialogOpen] = useState(false) + const [editingIdentity, setEditingIdentity] = useState(null) + + const identities: Identity[] = data?.identities ?? [] + const identityProviders = providersData?.identityProviders ?? [] + + // Helper function to get provider info by ID + const getProviderInfo = (providerId: string) => { + const provider = identityProviders.find((p: any) => p.id === providerId) + return provider || { name: providerId, iconId: 'unknown' } + } + + const handleCreateIdentity = () => { + if (!canCreateIdentities) return + // If no identities exist, direct provider selection isn't needed as cards are visible + // If identities exist, show provider selector modal + if (identities.length > 0) { + setProviderSelectorOpen(true) + } + } + + const handleProviderSelect = (providerId: string) => { + setEditingIdentity(null) // Ensure we're creating, not editing + if (providerId === 'aws_iam') { + setAwsEditDialogOpen(true) + } + // Future providers can be handled here + // Close provider selector if it was open + setProviderSelectorOpen(false) + } + + const handleEditIdentity = (identity: Identity) => { + if (!canUpdateIdentities) return + setEditingIdentity(identity) + if (identity.provider === 'aws_iam') { + setAwsEditDialogOpen(true) + } + } + + const handleProviderSelectorSuccess = () => { + setProviderSelectorOpen(false) + refetch() + } + + const handleAwsEditSuccess = () => { + setAwsEditDialogOpen(false) + setEditingIdentity(null) + refetch() + } + + const handleDelete = async (id: string) => { + if (!canDeleteIdentities) return + const ok = window.confirm('Delete this identity? This cannot be undone.') + if (!ok) return + try { + await deleteIdentity({ + variables: { id }, + refetchQueries: [ + { query: GetOrganisationIdentities, variables: { organisationId: organisation?.id } }, + ], + }) + toast.success('Identity deleted') + } catch (e) { + toast.error('Failed to delete identity') + } + } + + // Early return if no read permission + if (!canReadIdentities) { + return ( + + + + } + > + <> + + ) + } + + return ( +
+ {identities.length === 0 ? ( +
+
+

+ Set up a new identity +

+
+ Third party identity platforms can be used to authenticate clients and provision + access tokens. Select a provider below to get started. +
+
+ {canCreateIdentities ? ( + + ) : ( + + +
+ } + > + <> + + )} + + ) : ( +
+
+
Third-party identities
+
Manage identities used for external auth
+
+
+ {canCreateIdentities && ( +
+ +
+ )} +
+ + + + + + + + + + {identities.map((idn: Identity) => { + const providerInfo = getProviderInfo(idn.provider) + return ( + + + + + + + ) + })} + +
+ Provider + + Name + + Description +
+
+ +
+ {providerInfo.name} +
+ + + {idn.description} + +
+ {canUpdateIdentities && ( + + )} + {canDeleteIdentities && ( + + )} +
+
+
+
+
+ )} + + {/* Identity Provider Selector */} + setProviderSelectorOpen(false)} + organisationId={organisation?.id || ''} + onSuccess={handleProviderSelectorSuccess} + /> + + {/* AWS IAM Edit Dialog */} + { + setAwsEditDialogOpen(false) + setEditingIdentity(null) + }} + organisationId={organisation?.id || ''} + identity={editingIdentity} + onSuccess={handleAwsEditSuccess} + /> +
+ ) +} From 4984b74080de63818577d5cd4744cfb1b4d5fa3b Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:57:51 +0530 Subject: [PATCH 41/80] feat: enhance service account page with identity management features - Added ServiceAccountIdentities component to display associated identities for the service account. - Integrated a CopyButton for the service account ID, improving user experience by allowing easy copying of the ID to the clipboard. --- .../[team]/access/service-accounts/[account]/page.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 19c2e44ff..05d0b9473 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -24,6 +24,7 @@ import { IPChip } from '../../network/_components/IPChip' import { UpdateAccountNetworkPolicies } from '@/components/access/UpdateAccountNetworkPolicies' import { ServiceAccountTokens } from './_components/ServiceAccountTokens' import { KeyManagementDialog } from '@/components/service-accounts/KeyManagementDialog' +import { ServiceAccountIdentities } from './_components/ServiceAccountIdentities' export default function ServiceAccount({ params }: { params: { team: string; account: string } }) { const { activeOrganisation: organisation } = useContext(organisationContext) @@ -167,7 +168,13 @@ export default function ServiceAccount({ params }: { params: { team: string; acc )} - {account.id} + + {account.id} +
{account.serverSideKeyManagementEnabled ? ( @@ -320,6 +327,8 @@ export default function ServiceAccount({ params }: { params: { team: string; acc )}
+ + {userCanViewNetworkAccess && (
From 61d3b767825fbbb1f774d8bb64d9edb5f1617f32 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:58:13 +0530 Subject: [PATCH 42/80] feat: implement ServiceAccountIdentities component for managing associated identities - Added ServiceAccountIdentities component to manage third-party identities linked to a service account. - Integrated search functionality and toggle options for selecting identities. - Enhanced user experience with empty state handling and modal dialogs for identity management. --- .../_components/ServiceAccountIdentities.tsx | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx new file mode 100644 index 000000000..9b85fb10b --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx @@ -0,0 +1,254 @@ +'use client' + +import { ServiceAccountType } from '@/apollo/graphql' +import { organisationContext } from '@/contexts/organisationContext' +import { useContext, useMemo, useState } from 'react' +import { Button } from '@/components/common/Button' +import { EmptyState } from '@/components/common/EmptyState' +import { Dialog, Transition } from '@headlessui/react' +import { Fragment } from 'react' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' +import { ToggleSwitch } from '@/components/common/ToggleSwitch' +import { useMutation, useQuery } from '@apollo/client' +import GetOrganisationIdentities from '@/graphql/queries/identities/getOrganisationIdentities.gql' +import { GetServiceAccountDetail } from '@/graphql/queries/service-accounts/getServiceAccountDetail.gql' +import { toast } from 'react-toastify' +import { KeyManagementDialog } from '@/components/service-accounts/KeyManagementDialog' +import UpdateServiceAccount from '@/graphql/mutations/service-accounts/updateServiceAccount.gql' +import { TbLockShare } from 'react-icons/tb' +import { FaPlus, FaSearch, FaTimesCircle, FaServer } from 'react-icons/fa' +import clsx from 'clsx' + +export const ServiceAccountIdentities = ({ account }: { account: ServiceAccountType }) => { + const { activeOrganisation: organisation } = useContext(organisationContext) + const [isOpen, setIsOpen] = useState(false) + const { data } = useQuery(GetOrganisationIdentities, { + variables: { organisationId: organisation?.id }, + skip: !organisation, + }) + + const [updateAccount, { loading }] = useMutation(UpdateServiceAccount) + + const orgIdentities: any[] = data?.identities ?? [] + const initialSelected = new Set( + (account as any).identities?.map((i: any) => i.id as string) ?? [] + ) + const [selected, setSelected] = useState>(initialSelected) + const [searchQuery, setSearchQuery] = useState('') + + const toggle = (id: string) => { + const s = new Set(selected) + s.has(id) ? s.delete(id) : s.add(id) + setSelected(s) + } + + const open = () => { + if (!account.serverSideKeyManagementEnabled) { + const dlg = document.getElementById('sa-kms-dialog') + } + setIsOpen(true) + } + const close = () => setIsOpen(false) + + // Filter identities based on search query + const filteredIdentities = orgIdentities.filter( + (identity: any) => + identity.name.toLowerCase().includes(searchQuery.toLowerCase()) || + identity.description?.toLowerCase().includes(searchQuery.toLowerCase()) + ) + + const handleSave = async () => { + await updateAccount({ + variables: { + serviceAccountId: account.id, + name: account.name, + roleId: account.role!.id, + identityIds: Array.from(selected), + }, + refetchQueries: [ + { query: GetServiceAccountDetail, variables: { orgId: organisation?.id, id: account.id } }, + ], + }) + close() + toast.success('Updated identities for this account') + } + + if (!account.serverSideKeyManagementEnabled) { + return ( +
+ + +
+ } + > + + +
+ ) + } + + return ( +
+
+
+
Identities
+
+
+ Manage which third-party identities are trusted for this account +
+ {(account as any).identities && (account as any).identities.length > 0 && ( + + )} +
+
+
+ {(account as any).identities && (account as any).identities.length > 0 ? ( +
+ {(account as any).identities.map((identity: any) => ( +
+
+
+ +
+
+ {identity.name} +
+
+
+ {identity.description} +
+
+ ))} +
+ ) : ( + + +
+ } + > + + + )} +
+
+ + + + +
+ +
+
+ + + + Manage identities + +
+
+
+
+ +
+ setSearchQuery(e.target.value)} + /> + setSearchQuery('')} + /> +
+
+ + + + + + + + + + + {filteredIdentities.map((idn: any) => ( + + + + + + + ))} + +
+ Provider + + Name + + Description + + Enabled +
+
+ +
+
{idn.name}{idn.description} + toggle(idn.id)} + /> +
+
+ + +
+
+
+
+
+
+
+
+
+ ) +} From bc0a69bdb4fdaad8b8cf9a745aea0a208b9860ed Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:59:09 +0530 Subject: [PATCH 43/80] feat: add IdentityProviderSelector component for selecting identity providers - Introduced the IdentityProviderSelector component to facilitate the selection of identity providers for creating new identities. - Integrated AWS IAM provider selection with a dialog for additional configuration. - Enhanced user experience with a modal interface and improved accessibility for managing identity providers. --- .../identities/IdentityProviderSelector.tsx | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 frontend/components/identities/IdentityProviderSelector.tsx diff --git a/frontend/components/identities/IdentityProviderSelector.tsx b/frontend/components/identities/IdentityProviderSelector.tsx new file mode 100644 index 000000000..836abf774 --- /dev/null +++ b/frontend/components/identities/IdentityProviderSelector.tsx @@ -0,0 +1,108 @@ +import { useState } from 'react' +import { Dialog, Transition } from '@headlessui/react' +import { Fragment } from 'react' +import { Button } from '../common/Button' +import { FaTimes } from 'react-icons/fa' +import { AwsIamIdentityDialog } from './providers/aws/iam' +import { ProviderCards } from './ProviderCards' + +interface IdentityProviderSelectorProps { + isOpen: boolean + onClose: () => void + organisationId: string + onSuccess: () => void +} + +export const IdentityProviderSelector = ({ + isOpen, + onClose, + organisationId, + onSuccess, +}: IdentityProviderSelectorProps) => { + const [selectedProvider, setSelectedProvider] = useState(null) + const [awsIamDialogOpen, setAwsIamDialogOpen] = useState(false) + + const handleProviderSelect = (providerId: string) => { + setSelectedProvider(providerId) + if (providerId === 'aws_iam') { + setAwsIamDialogOpen(true) + onClose() + } + // Future providers can be handled here + } + + const handleAwsIamSuccess = () => { + setAwsIamDialogOpen(false) + setSelectedProvider(null) + onSuccess() + } + + const handleAwsIamClose = () => { + setAwsIamDialogOpen(false) + setSelectedProvider(null) + } + + return ( + <> + + + +
+ +
+
+ + +
+
+ + Add Identity Provider + +
+ Select a provider below to create a new identity. +
+
+ +
+ +
+ +
+
+
+
+
+
+
+ + {/* AWS IAM Identity Dialog */} + + + ) +} From 5553582135cad78e56312b1a8009df6fb3537ded Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 22:59:29 +0530 Subject: [PATCH 44/80] feat: add ProviderCards component for displaying identity providers - Introduced the ProviderCards component to render a grid of identity providers. - Integrated Apollo Client to fetch identity provider data via GraphQL. - Enhanced user interaction with clickable cards for selecting providers, improving the identity selection process. --- .../components/identities/ProviderCards.tsx | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 frontend/components/identities/ProviderCards.tsx diff --git a/frontend/components/identities/ProviderCards.tsx b/frontend/components/identities/ProviderCards.tsx new file mode 100644 index 000000000..ee4358b97 --- /dev/null +++ b/frontend/components/identities/ProviderCards.tsx @@ -0,0 +1,63 @@ +import { Card } from '../common/Card' +import { ProviderIcon } from '../syncing/ProviderIcon' +import { FaArrowRight } from 'react-icons/fa' +import { useQuery } from '@apollo/client' +import GetIdentityProviders from '@/graphql/queries/identities/getIdentityProviders.gql' + +interface IdentityProvider { + id: string + name: string + description: string + iconId: string + supported: boolean +} + +const ProviderCard = ({ + provider, + onClick, +}: { + provider: IdentityProvider + onClick: () => void +}) => { + return ( + + + + ) +} + +interface ProviderCardsProps { + onProviderSelect: (providerId: string) => void +} + +export const ProviderCards = ({ onProviderSelect }: ProviderCardsProps) => { + const { data } = useQuery(GetIdentityProviders) + const providers: IdentityProvider[] = data?.identityProviders ?? [] + + return ( +
+ {providers.map((provider) => ( +
+ onProviderSelect(provider.id)} /> +
+ ))} +
+ ) +} From aecfce08a0956fc9d20c08c592942014d0b358e2 Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 25 Aug 2025 23:00:26 +0530 Subject: [PATCH 45/80] feat: add AwsIamIdentityDialog component for AWS IAM identity management - Introduced the AwsIamIdentityDialog component to facilitate the creation and updating of AWS IAM identities. - Integrated Apollo Client for GraphQL mutations and queries to manage identity configurations. - Enhanced user experience with a modal interface for configuring trusted entities, token settings, and STS endpoints. - Implemented form validation and state management for identity attributes, improving usability and error handling. --- .../identities/providers/aws/iam.tsx | 580 ++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 frontend/components/identities/providers/aws/iam.tsx diff --git a/frontend/components/identities/providers/aws/iam.tsx b/frontend/components/identities/providers/aws/iam.tsx new file mode 100644 index 000000000..5a82b8b6c --- /dev/null +++ b/frontend/components/identities/providers/aws/iam.tsx @@ -0,0 +1,580 @@ +'use client' + +import { useState, Fragment, useEffect, useMemo } from 'react' +import { useMutation, useQuery } from '@apollo/client' +import GetAwsStsEndpoints from '@/graphql/queries/identities/getAwsStsEndpoints.gql' +import CreateIdentity from '@/graphql/mutations/identities/createIdentity.gql' +import UpdateIdentity from '@/graphql/mutations/identities/updateIdentity.gql' +import { Dialog, Transition, Combobox } from '@headlessui/react' +import { Input } from '@/components/common/Input' +import { Button } from '@/components/common/Button' +import { FaTimes, FaChevronDown, FaCheckCircle } from 'react-icons/fa' +import { LiaAws } from 'react-icons/lia' +import { toast } from 'react-toastify' +import clsx from 'clsx' +import { parseTTL, formatTTL, isValidTTL, getTTLExamples } from '@/utils/ttl' + +type AwsIamConfig = { + trustedPrincipals: string[] + signatureTtlSeconds: number + stsEndpoint: string +} + +type Identity = { + id: string + provider: string + name: string + description?: string | null + config: AwsIamConfig + tokenNamePattern?: string | null + defaultTtlSeconds: number + maxTtlSeconds: number +} + +type AwsStsEndpoint = { + regionCode: string | null + regionName: string + endpoint: string +} + +interface AwsIamIdentityDialogProps { + isOpen: boolean + onClose: () => void + organisationId: string + identity?: Identity | null + onSuccess: () => void +} + +export const AwsIamIdentityDialog = ({ + isOpen, + onClose, + organisationId, + identity, + onSuccess, +}: AwsIamIdentityDialogProps) => { + const { data: stsData } = useQuery(GetAwsStsEndpoints) + const [createIdentity, { loading: creating }] = useMutation(CreateIdentity) + const [updateIdentity, { loading: updating }] = useMutation(UpdateIdentity) + + const [initialState, setInitialState] = useState<{ + name: string + description: string + trustedPrincipals: string + signatureTtlInput: string + signatureTtlSeconds: number + stsEndpoint: string + tokenNamePattern: string + defaultTtlInput: string + maxTtlInput: string + defaultTtlSeconds: number + maxTtlSeconds: number + } | null>(null) + + // Form state + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [trustedPrincipals, setTrustedPrincipals] = useState('') + const [signatureTtlInput, setSignatureTtlInput] = useState('60s') + const [signatureTtlSeconds, setSignatureTtlSeconds] = useState(60) + const [useCustomEndpoint, setUseCustomEndpoint] = useState(false) + const [stsEndpoint, setStsEndpoint] = useState('https://sts.amazonaws.com') + const [selectedStsEndpoint, setSelectedStsEndpoint] = useState(null) + const [stsQuery, setStsQuery] = useState('') + const [tokenNamePattern, setTokenNamePattern] = useState('') + const [defaultTtlInput, setDefaultTtlInput] = useState('1h') + const [maxTtlInput, setMaxTtlInput] = useState('24h') + const [defaultTtlSeconds, setDefaultTtlSeconds] = useState(3600) + const [maxTtlSeconds, setMaxTtlSeconds] = useState(86400) + + const awsStsEndpoints: AwsStsEndpoint[] = useMemo(() => { + try { + return (stsData?.awsStsEndpoints || []).map((raw: string) => JSON.parse(raw)) + } catch { + return [] + } + }, [stsData]) + + const filteredStsEndpoints = awsStsEndpoints.filter((endpoint) => { + if (stsQuery === '') { + return true + } + const queryLower = stsQuery.toLowerCase() + const regionNameMatches = endpoint.regionName?.toLowerCase().includes(queryLower) || false + const regionCodeMatches = endpoint.regionCode?.toLowerCase().includes(queryLower) || false + const endpointMatches = endpoint.endpoint?.toLowerCase().includes(queryLower) || false + + return regionNameMatches || regionCodeMatches || endpointMatches + }) + + const handleDefaultTtlChange = (value: string) => { + setDefaultTtlInput(value) + if (isValidTTL(value)) { + setDefaultTtlSeconds(parseTTL(value)) + } + } + + const handleMaxTtlChange = (value: string) => { + setMaxTtlInput(value) + if (isValidTTL(value)) { + setMaxTtlSeconds(parseTTL(value)) + } + } + + const handleSignatureTtlChange = (value: string) => { + setSignatureTtlInput(value) + if (isValidTTL(value)) { + setSignatureTtlSeconds(parseTTL(value)) + } + } + + const resetForm = () => { + setName('') + setDescription('') + setTrustedPrincipals('') + setSignatureTtlInput('60s') + setSignatureTtlSeconds(60) + setUseCustomEndpoint(false) + setStsEndpoint('https://sts.amazonaws.com') + setSelectedStsEndpoint(null) + setStsQuery('') + setTokenNamePattern('') + setDefaultTtlInput('1h') + setMaxTtlInput('24h') + setDefaultTtlSeconds(3600) + setMaxTtlSeconds(86400) + setInitialState({ + name: '', + description: '', + trustedPrincipals: '', + signatureTtlInput: '60s', + signatureTtlSeconds: 60, + stsEndpoint: 'https://sts.amazonaws.com', + tokenNamePattern: '', + defaultTtlInput: '1h', + maxTtlInput: '24h', + defaultTtlSeconds: 3600, + maxTtlSeconds: 86400, + }) + } + + useEffect(() => { + if (isOpen) { + if (identity) { + // Populate form with identity data + setName(identity.name) + setDescription(identity.description || '') + setTrustedPrincipals(identity.config.trustedPrincipals.join(', ')) + setSignatureTtlInput(formatTTL(identity.config.signatureTtlSeconds)) + setSignatureTtlSeconds(identity.config.signatureTtlSeconds) + setStsEndpoint(identity.config.stsEndpoint) + + const existingEndpoint = awsStsEndpoints.find( + (ep) => ep.endpoint === identity.config.stsEndpoint + ) + setUseCustomEndpoint(!existingEndpoint) + setSelectedStsEndpoint(existingEndpoint || null) + setStsQuery('') + + setTokenNamePattern(identity.tokenNamePattern || '') + setDefaultTtlInput(formatTTL(identity.defaultTtlSeconds)) + setMaxTtlInput(formatTTL(identity.maxTtlSeconds)) + setDefaultTtlSeconds(identity.defaultTtlSeconds) + setMaxTtlSeconds(identity.maxTtlSeconds) + setInitialState({ + name: identity.name, + description: identity.description || '', + trustedPrincipals: identity.config.trustedPrincipals.join(', '), + signatureTtlInput: formatTTL(identity.config.signatureTtlSeconds), + signatureTtlSeconds: identity.config.signatureTtlSeconds, + stsEndpoint: identity.config.stsEndpoint, + tokenNamePattern: identity.tokenNamePattern || '', + defaultTtlInput: formatTTL(identity.defaultTtlSeconds), + maxTtlInput: formatTTL(identity.maxTtlSeconds), + defaultTtlSeconds: identity.defaultTtlSeconds, + maxTtlSeconds: identity.maxTtlSeconds, + }) + } else { + resetForm() + // Set default to Global (Legacy) endpoint if available + if (awsStsEndpoints.length > 0) { + const globalEndpoint = awsStsEndpoints.find( + (ep) => ep.endpoint === 'https://sts.amazonaws.com' + ) + if (globalEndpoint) { + setSelectedStsEndpoint(globalEndpoint) + setStsEndpoint(globalEndpoint.endpoint) + setUseCustomEndpoint(false) + } + } + } + } + }, [isOpen, identity, awsStsEndpoints]) + + const handleSave = async () => { + // Client-side guard to avoid firing mutation and duplicating toasts + if (defaultTtlSeconds > maxTtlSeconds) { + toast.error('Default token expiry must be less than or equal to Maximum token expiry') + return + } + if (trustedPrincipals.trim() === '*') { + toast.error("'*' is not allowed as a trusted principal pattern") + return + } + if (!stsEndpoint || stsEndpoint.trim() === '') { + toast.error('Please select an STS endpoint') + return + } + + try { + if (identity) { + await updateIdentity({ + variables: { + id: identity.id, + name, + description: description || null, + trustedPrincipals, + signatureTtlSeconds, + stsEndpoint, + tokenNamePattern: tokenNamePattern || null, + defaultTtlSeconds, + maxTtlSeconds, + }, + }) + toast.success('Identity updated') + } else { + await createIdentity({ + variables: { + organisationId, + provider: 'aws_iam', + name, + description: description || null, + trustedPrincipals, + signatureTtlSeconds, + stsEndpoint, + tokenNamePattern: tokenNamePattern || null, + defaultTtlSeconds, + maxTtlSeconds, + }, + }) + toast.success('Identity created') + } + onSuccess() + onClose() + } catch (e: any) { + const hasGraphQLErrors = Array.isArray(e?.graphQLErrors) && e.graphQLErrors.length > 0 + if (!hasGraphQLErrors) { + toast.error('Failed to save identity') + } else { + toast.error(e.message) + } + } + } + + const isSaving = creating || updating + + const hasChanges = useMemo(() => { + if (!initialState) return true + return ( + name !== initialState.name || + description !== initialState.description || + trustedPrincipals !== initialState.trustedPrincipals || + signatureTtlInput !== initialState.signatureTtlInput || + stsEndpoint !== initialState.stsEndpoint || + tokenNamePattern !== initialState.tokenNamePattern || + defaultTtlInput !== initialState.defaultTtlInput || + maxTtlInput !== initialState.maxTtlInput + ) + }, [ + name, + description, + trustedPrincipals, + signatureTtlInput, + stsEndpoint, + tokenNamePattern, + defaultTtlInput, + maxTtlInput, + initialState, + ]) + + return ( + + + +
+ +
+
+ + +
+
+ +
+ + AWS IAM Identity + +
+ Configure trusted entities and token settings +
+
+
+ +
+ +
+
+ + +
+ +
+
Trusted entities
+
+ +