From 0a063d99e19e0863aba9d65b7376ec9d817bdbf0 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 20 Oct 2024 14:44:06 +0530 Subject: [PATCH 01/92] feat: add service account models, types, resolvers and schema --- ...ceaccount_environmentkey_paths_and_more.py | 58 ++++++++ backend/api/models.py | 43 ++++++ .../graphene/mutations/service_accounts.py | 136 ++++++++++++++++++ .../graphene/queries/service_accounts.py | 8 ++ backend/backend/graphene/types.py | 39 +++++ backend/backend/schema.py | 18 +++ 6 files changed, 302 insertions(+) create mode 100644 backend/api/migrations/0085_serviceaccount_environmentkey_paths_and_more.py create mode 100644 backend/backend/graphene/mutations/service_accounts.py create mode 100644 backend/backend/graphene/queries/service_accounts.py diff --git a/backend/api/migrations/0085_serviceaccount_environmentkey_paths_and_more.py b/backend/api/migrations/0085_serviceaccount_environmentkey_paths_and_more.py new file mode 100644 index 00000000..a2c4973f --- /dev/null +++ b/backend/api/migrations/0085_serviceaccount_environmentkey_paths_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 4.2.15 on 2024-10-20 09:12 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0084_auto_20241008_0708'), + ] + + operations = [ + migrations.CreateModel( + name='ServiceAccount', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('identity_key', models.CharField(blank=True, max_length=256, null=True)), + ('server_wrapped_keyring', models.TextField(null=True)), + ('server_wrapped_recovery', models.TextField(null=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ('apps', models.ManyToManyField(related_name='apps', to='api.app')), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisation')), + ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.role')), + ], + ), + migrations.AddField( + model_name='environmentkey', + name='paths', + field=models.TextField(blank=True, null=True), + ), + migrations.CreateModel( + name='ServiceAccountHandler', + fields=[ + ('id', models.TextField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('wrapped_keyring', models.TextField()), + ('wrapped_recovery', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('service_account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember')), + ], + ), + migrations.AddField( + model_name='environmentkey', + name='service_account', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount'), + ), + migrations.AddField( + model_name='servicetoken', + name='service_account', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount'), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 1918af68..c02e35b0 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -215,6 +215,42 @@ def delete(self, *args, **kwargs): self.save() +class ServiceAccount(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + name = models.CharField(max_length=255) + organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) + role = models.ForeignKey( + Role, + on_delete=models.SET_NULL, + null=True, + blank=True, + ) + apps = models.ManyToManyField(App, related_name="apps") + identity_key = models.CharField(max_length=256, null=True, blank=True) + server_wrapped_keyring = models.TextField(null=True) + server_wrapped_recovery = models.TextField(null=True) + 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) + + def delete(self, *args, **kwargs): + """ + Soft delete the object by setting the 'deleted_at' field. + """ + self.deleted_at = timezone.now() + self.save() + + +class ServiceAccountHandler(models.Model): + id = models.TextField(default=uuid4, primary_key=True) + service_account = models.ForeignKey(ServiceAccount, on_delete=models.CASCADE) + user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE) + wrapped_keyring = models.TextField() + wrapped_recovery = models.TextField() + created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + + class OrganisationMemberInviteManager(models.Manager): def create(self, *args, **kwargs): organisation = kwargs.get("organisation") @@ -299,6 +335,10 @@ class EnvironmentKey(models.Model): user = models.ForeignKey( OrganisationMember, on_delete=models.CASCADE, blank=True, null=True ) + service_account = models.ForeignKey( + ServiceAccount, on_delete=models.CASCADE, blank=True, null=True + ) + paths = models.TextField(blank=True, null=True) identity_key = models.CharField(max_length=256) wrapped_seed = models.CharField(max_length=256) wrapped_salt = models.CharField(max_length=256) @@ -415,6 +455,9 @@ class ServiceToken(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) app = models.ForeignKey(App, on_delete=models.CASCADE) keys = models.ManyToManyField(EnvironmentKey) + service_account = models.ForeignKey( + ServiceAccount, on_delete=models.CASCADE, null=True + ) identity_key = models.CharField(max_length=256) token = models.CharField(max_length=64) wrapped_key_share = models.CharField(max_length=406) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py new file mode 100644 index 00000000..2ccf2cac --- /dev/null +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -0,0 +1,136 @@ +from backend.graphene.mutations.environment import EnvironmentKeyInput +import graphene +from graphql import GraphQLError +from api.models import Organisation, Role, ServiceAccount, ServiceAccountHandler +from api.utils.access.permissions import user_has_permission +from backend.graphene.types import ServiceAccountType + + +class ServiceAccountHandlerInput(graphene.InputObjectType): + member_id = graphene.ID(required=False) + wrapped_keyring = graphene.String(required=True) + wrapped_recovery = graphene.String(required=True) + + +class CreateServiceAccountMutation(graphene.Mutation): + class Arguments: + name = graphene.String() + organisation_id = graphene.ID() + role_id = graphene.ID() + handlers = graphene.List(ServiceAccountHandlerInput) + identity_key = graphene.String() + server_wrapped_keyring = graphene.String(required=False) + server_wrapped_recovery = graphene.String(required=False) + + service_account = graphene.Field(ServiceAccountType) + + @classmethod + def mutate( + cls, + root, + info, + name, + organisation_id, + role_id, + handlers, + identity_key, + server_wrapped_keyring=None, + server_wrapped_recovery=None, + ): + user = info.context.user + org = Organisation.objects.get(id=organisation_id) + + if not user_has_permission(user, "create", "ServiceAccounts", org): + raise GraphQLError( + "You don't have the permissions required to create Service Accounts in this organisation" + ) + + service_account = ServiceAccount.objects.create( + name=name, + organisation=org, + role=Role.objects.get(id=role_id), + identity_key=identity_key, + server_wrapped_keyring=server_wrapped_keyring, + server_wrapped_recovery=server_wrapped_recovery, + ) + + for handler in handlers: + ServiceAccountHandler.objects.create( + service_account=service_account, + user_id=handler.member_id, + wrapped_keyring=handler.wrapped_keyring, + wrapped_recovery=handler.wrapped_recovery, + ) + + return CreateServiceAccountMutation(service_account=service_account) + + +class EnableServiceAccountThirdPartyAuthMutation(graphene.Mutation): + class Arguments: + service_account_id = graphene.ID() + server_wrapped_keyring = graphene.String() + server_wrapped_recovery = graphene.String() + + service_account = graphene.Field(ServiceAccountType) + + @classmethod + def mutate( + cls, + root, + info, + service_account_id, + server_wrapped_keyring, + server_wrapped_recovery, + ): + 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" + ) + + service_account.server_wrapped_keyring = server_wrapped_keyring + service_account.server_wrapped_recovery = server_wrapped_recovery + service_account.save() + + return EnableServiceAccountThirdPartyAuthMutation( + service_account=service_account + ) + + +class UpdateServiceAccountHandlersMutation(graphene.Mutation): + class Arguments: + service_account_id = graphene.ID() + handlers = graphene.List(ServiceAccountHandlerInput) + + service_account = graphene.Field(ServiceAccountType) + + @classmethod + def mutate(cls, root, info, service_account_id, handlers): + 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" + ) + + for handler in handlers: + if not ServiceAccountHandler.objects.filter( + service_account=service_account, user_id=handler.member_id + ).exists(): + ServiceAccountHandler.objects.create( + service_account=service_account, + user_id=handler.member_id, + wrapped_keyring=handler.wrapped_keyring, + wrapped_recovery=handler.wrapped_recovery, + ) + + return EnableServiceAccountThirdPartyAuthMutation( + service_account=service_account + ) diff --git a/backend/backend/graphene/queries/service_accounts.py b/backend/backend/graphene/queries/service_accounts.py new file mode 100644 index 00000000..2ce55e90 --- /dev/null +++ b/backend/backend/graphene/queries/service_accounts.py @@ -0,0 +1,8 @@ +from api.utils.access.permissions import user_has_permission +from api.models import Organisation, ServiceAccount + + +def resolve_service_accounts(root, info, org_id): + org = Organisation.objects.get(id=org_id) + if user_has_permission(info.context.user.userId, "read", "ServiceAccounts", org): + return ServiceAccount.objects.filter(organisation=org) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 9c75c050..f9b0c486 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -27,6 +27,8 @@ SecretFolder, SecretTag, ServerEnvironmentKey, + ServiceAccount, + ServiceAccountHandler, ServiceToken, UserToken, ) @@ -457,6 +459,43 @@ class Meta: ) +class ServiceAccountHandlerType(DjangoObjectType): + class Meta: + model = ServiceAccountHandler + fields = "__all__" + + +class ServiceAccountType(DjangoObjectType): + + third_party_auth_enabled = graphene.Boolean() + handlers = graphene.List(ServiceAccountHandlerType) + tokens = graphene.List(ServiceTokenType) + + class Meta: + model = ServiceAccount + fields = ( + "id", + "name", + "role", + "apps", + "identity_key", + "created_at", + "updated_at", + ) + + def resolve_third_party_auth_enabled(self, info): + return ( + self.server_wrapped_keyring is not None + and self.server_wrapped_recovery is not None + ) + + def resolve_handlers(self, info): + return ServiceAccountHandler.objects.filter(service_account=self) + + def resolve_tokens(self, info): + return ServiceToken.objects.filter(service_account=self) + + class SecretFolderType(DjangoObjectType): folder_count = graphene.Int() secret_count = graphene.Int() diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 4035d530..c8885584 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -3,6 +3,11 @@ from api.utils.syncing.github.actions import GitHubRepoType from api.utils.syncing.gitlab.main import GitLabGroupType, GitLabProjectType from api.utils.syncing.railway.main import RailwayProjectType +from backend.graphene.mutations.service_accounts import ( + CreateServiceAccountMutation, + EnableServiceAccountThirdPartyAuthMutation, + UpdateServiceAccountHandlersMutation, +) from .graphene.mutations.access import ( CreateCustomRoleMutation, DeleteCustomRoleMutation, @@ -32,6 +37,7 @@ resolve_railway_projects, ) from .graphene.queries.access import resolve_roles +from .graphene.queries.service_accounts import resolve_service_accounts from .graphene.queries.quotas import resolve_organisation_plan from .graphene.queries.license import resolve_license, resolve_organisation_license from .graphene.mutations.environment import ( @@ -119,6 +125,7 @@ SecretFolderType, SecretTagType, SecretType, + ServiceAccountType, ServiceTokenType, ServiceType, TimeRange, @@ -232,6 +239,8 @@ class Query(graphene.ObjectType): user_tokens = graphene.List(UserTokenType, organisation_id=graphene.ID()) service_tokens = graphene.List(ServiceTokenType, app_id=graphene.ID()) + service_accounts = graphene.List(ServiceAccountType, org_id=graphene.ID()) + server_public_key = graphene.String() sse_enabled = graphene.Boolean(app_id=graphene.ID()) @@ -562,6 +571,8 @@ def resolve_service_tokens(root, info, app_id): return ServiceToken.objects.filter(app=app, deleted_at=None) + resolve_service_accounts = resolve_service_accounts + def resolve_logs(root, info, app_id, start=0, end=0): if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") @@ -766,6 +777,13 @@ class Mutation(graphene.ObjectType): update_custom_role = UpdateCustomRoleMutation.Field() delete_custom_role = DeleteCustomRoleMutation.Field() + # Service Accounts + create_service_account = CreateServiceAccountMutation.Field() + enable_service_account_third_party_auth = ( + EnableServiceAccountThirdPartyAuthMutation.Field() + ) + update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field() + init_env_sync = InitEnvSync.Field() delete_env_sync = DeleteSync.Field() trigger_sync = TriggerSync.Field() From d1a52ee99da3ecf4b5aa915da0857b2078225a7d Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 20 Oct 2024 14:44:19 +0530 Subject: [PATCH 02/92] chore: regenerate graphql schema --- frontend/apollo/schema.graphql | 47 +++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 47a4f6b2..1980a4b3 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -24,6 +24,7 @@ type Query { environmentTokens(environmentId: ID): [EnvironmentTokenType] userTokens(organisationId: ID): [UserTokenType] serviceTokens(appId: ID): [ServiceTokenType] + serviceAccounts(orgId: ID): [ServiceAccountType] serverPublicKey: String sseEnabled(appId: ID): Boolean providers: [ProviderType] @@ -191,7 +192,7 @@ type AppType { appSeed: String! wrappedKeyShare: String! createdAt: DateTime - sseEnabled: Boolean + sseEnabled: Boolean! environments: [EnvironmentType]! members: [OrganisationMemberType]! } @@ -488,6 +489,29 @@ type UserTokenType { expiresAt: DateTime } +type ServiceAccountType { + id: String! + name: String! + role: RoleType + apps: [AppType!]! + identityKey: String + createdAt: DateTime + updatedAt: DateTime! + thirdPartyAuthEnabled: Boolean + handlers: [ServiceAccountHandlerType] + tokens: [ServiceTokenType] +} + +type ServiceAccountHandlerType { + id: String! + serviceAccount: ServiceAccountType! + user: OrganisationMemberType! + wrappedKeyring: String! + wrappedRecovery: String! + createdAt: DateTime + updatedAt: DateTime! +} + type CloudFlarePagesType { name: String deploymentId: String @@ -610,6 +634,9 @@ type Mutation { createCustomRole(color: String, description: String, name: String, organisationId: ID!, permissions: JSONString): CreateCustomRoleMutation updateCustomRole(color: String, description: String, id: ID!, name: String, permissions: JSONString): UpdateCustomRoleMutation deleteCustomRole(id: ID!): DeleteCustomRoleMutation + createServiceAccount(handlers: [ServiceAccountHandlerInput], identityKey: String, name: String, organisationId: ID, roleId: ID, serverWrappedKeyring: String, serverWrappedRecovery: String): CreateServiceAccountMutation + enableServiceAccountThirdPartyAuth(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountThirdPartyAuthMutation + updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], serviceAccountId: ID): UpdateServiceAccountHandlersMutation initEnvSync(appId: ID, envKeys: [EnvironmentKeyInput]): InitEnvSync deleteEnvSync(syncId: ID): DeleteSync triggerSync(syncId: ID): TriggerSync @@ -750,6 +777,24 @@ type DeleteCustomRoleMutation { ok: Boolean } +type CreateServiceAccountMutation { + serviceAccount: ServiceAccountType +} + +input ServiceAccountHandlerInput { + memberId: ID + wrappedKeyring: String! + wrappedRecovery: String! +} + +type EnableServiceAccountThirdPartyAuthMutation { + serviceAccount: ServiceAccountType +} + +type UpdateServiceAccountHandlersMutation { + serviceAccount: ServiceAccountType +} + type InitEnvSync { app: AppType } From 457b94311f99c791ba2b26d46595f8db0fd71cf4 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 21 Oct 2024 18:13:38 +0530 Subject: [PATCH 03/92] feat: add various query and mutation resolvers --- .../graphene/mutations/service_accounts.py | 23 ++++++++++ backend/backend/graphene/queries/access.py | 31 ++++++++++++- .../graphene/queries/service_accounts.py | 31 ++++++++++++- backend/backend/schema.py | 46 +++++++------------ 4 files changed, 99 insertions(+), 32 deletions(-) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 2ccf2cac..ebba081a 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -134,3 +134,26 @@ def mutate(cls, root, info, service_account_id, handlers): return EnableServiceAccountThirdPartyAuthMutation( service_account=service_account ) + + +class DeleteServiceAccountMutation(graphene.Mutation): + class Arguments: + service_account_id = graphene.ID() + + ok = graphene.Boolean() + + @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, "delete", "ServiceAccounts", service_account.organisation + ): + raise GraphQLError( + "You don't have the permissions required to delete Service Accounts in this organisation" + ) + + service_account.delete() + + return DeleteServiceAccountMutation(ok=True) diff --git a/backend/backend/graphene/queries/access.py b/backend/backend/graphene/queries/access.py index af2e6844..f48d2206 100644 --- a/backend/backend/graphene/queries/access.py +++ b/backend/backend/graphene/queries/access.py @@ -1,8 +1,10 @@ from api.utils.access.permissions import user_has_permission, user_is_org_member -from api.models import Organisation, Role +from api.models import Organisation, OrganisationMember, Role from graphql import GraphQLError from django.db import transaction from api.utils.access.roles import default_roles +from itertools import chain +from django.db.models import Q @transaction.atomic @@ -30,3 +32,30 @@ def resolve_roles(root, info, org_id): return Role.objects.filter(organisation=org) else: raise GraphQLError("You don't have permission to perform this action") + + +def resolve_organisation_global_access_users(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") + + global_access_roles = Role.objects.filter( + Q(organisation_id=organisation_id) + & (Q(name__iexact="owner") | Q(name__iexact="admin")) + | Q(permissions__global_access=True) + ) + + members = OrganisationMember.objects.filter( + organisation_id=organisation_id, + role__in=global_access_roles, + deleted_at=None, + ) + + if not info.context.user.userId in [member.user_id for member in members]: + self_member = OrganisationMember.objects.filter( + organisation_id=organisation_id, + user_id=info.context.user.userId, + deleted_at=None, + ) + members = list(chain(members, self_member)) + + return members diff --git a/backend/backend/graphene/queries/service_accounts.py b/backend/backend/graphene/queries/service_accounts.py index 2ce55e90..b28cf8ef 100644 --- a/backend/backend/graphene/queries/service_accounts.py +++ b/backend/backend/graphene/queries/service_accounts.py @@ -1,8 +1,35 @@ -from api.utils.access.permissions import user_has_permission -from api.models import Organisation, ServiceAccount +from api.utils.access.permissions import user_has_permission, user_is_org_member +from api.models import Organisation, OrganisationMember, Role, ServiceAccount +from .access import resolve_organisation_global_access_users +from django.db.models import Q +from graphql import GraphQLError def resolve_service_accounts(root, info, org_id): org = Organisation.objects.get(id=org_id) if user_has_permission(info.context.user.userId, "read", "ServiceAccounts", org): return ServiceAccount.objects.filter(organisation=org) + + +def resolve_service_account_handlers(root, info, org_id): + if not user_is_org_member(info.context.user.userId, org_id): + raise GraphQLError("You don't have access to this organisation") + + service_account_handler_roles = Role.objects.filter( + Q(organisation_id=org_id) + & ( + Q(name__iexact="owner") | Q(name__iexact="admin") + ) # Check for "owner" or "admin" roles + | Q(permissions__global_access=True), # Check for global access roles + Q( + permissions__permissions__ServiceAccounts__gt=0 + ), # Add condition for non-empty 'ServiceAccounts' list + ) + + members = OrganisationMember.objects.filter( + organisation_id=org_id, + role__in=service_account_handler_roles, + deleted_at=None, + ) + + return members diff --git a/backend/backend/schema.py b/backend/backend/schema.py index c8885584..d135b0b0 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -5,6 +5,7 @@ from api.utils.syncing.railway.main import RailwayProjectType from backend.graphene.mutations.service_accounts import ( CreateServiceAccountMutation, + DeleteServiceAccountMutation, EnableServiceAccountThirdPartyAuthMutation, UpdateServiceAccountHandlersMutation, ) @@ -36,8 +37,14 @@ resolve_test_nomad_creds, resolve_railway_projects, ) -from .graphene.queries.access import resolve_roles -from .graphene.queries.service_accounts import resolve_service_accounts +from .graphene.queries.access import ( + resolve_roles, + resolve_organisation_global_access_users, +) +from .graphene.queries.service_accounts import ( + resolve_service_accounts, + resolve_service_account_handlers, +) from .graphene.queries.quotas import resolve_organisation_plan from .graphene.queries.license import resolve_license, resolve_organisation_license from .graphene.mutations.environment import ( @@ -125,6 +132,7 @@ SecretFolderType, SecretTagType, SecretType, + ServiceAccountHandlerType, ServiceAccountType, ServiceTokenType, ServiceType, @@ -153,9 +161,7 @@ from datetime import datetime, timedelta from django.conf import settings from logs.models import KMSDBLog -from itertools import chain from django.utils import timezone -from django.db.models import Q CLOUD_HOSTED = settings.APP_HOST == "cloud" @@ -241,6 +247,10 @@ class Query(graphene.ObjectType): service_accounts = graphene.List(ServiceAccountType, org_id=graphene.ID()) + service_account_handlers = graphene.List( + OrganisationMemberType, org_id=graphene.ID() + ) + server_public_key = graphene.String() sse_enabled = graphene.Boolean(app_id=graphene.ID()) @@ -348,31 +358,7 @@ def resolve_organisation_members(root, info, organisation_id, role, user_id=None return OrganisationMember.objects.filter(**filter) - def resolve_organisation_global_access_users(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") - - global_access_roles = Role.objects.filter( - Q(organisation_id=organisation_id) - & (Q(name__iexact="owner") | Q(name__iexact="admin")) - | Q(permissions__global_access=True) - ) - - members = OrganisationMember.objects.filter( - organisation_id=organisation_id, - role__in=global_access_roles, - deleted_at=None, - ) - - if not info.context.user.userId in [member.user_id for member in members]: - self_member = OrganisationMember.objects.filter( - organisation_id=organisation_id, - user_id=info.context.user.userId, - deleted_at=None, - ) - members = list(chain(members, self_member)) - - return members + resolve_organisation_global_access_users = resolve_organisation_global_access_users def resolve_organisation_invites(root, info, org_id): if not user_is_org_member(info.context.user.userId, org_id): @@ -572,6 +558,7 @@ def resolve_service_tokens(root, info, app_id): return ServiceToken.objects.filter(app=app, deleted_at=None) resolve_service_accounts = resolve_service_accounts + resolve_service_account_handlers = resolve_service_account_handlers def resolve_logs(root, info, app_id, start=0, end=0): if not user_can_access_app(info.context.user.userId, app_id): @@ -783,6 +770,7 @@ class Mutation(graphene.ObjectType): EnableServiceAccountThirdPartyAuthMutation.Field() ) update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field() + delete_service_account = DeleteServiceAccountMutation.Field() init_env_sync = InitEnvSync.Field() delete_env_sync = DeleteSync.Field() From 29ea9ee5a5bb0d978852452cb55d98abf6f43367 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 21 Oct 2024 18:14:01 +0530 Subject: [PATCH 04/92] fix: hard delete service accounts --- backend/api/models.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/backend/api/models.py b/backend/api/models.py index c02e35b0..8122ec5f 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -233,13 +233,6 @@ class ServiceAccount(models.Model): updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(null=True, blank=True) - def delete(self, *args, **kwargs): - """ - Soft delete the object by setting the 'deleted_at' field. - """ - self.deleted_at = timezone.now() - self.save() - class ServiceAccountHandler(models.Model): id = models.TextField(default=uuid4, primary_key=True) From 5da23ce132690e7f1f1e4c993f49f59b04ea7616 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 21 Oct 2024 18:14:51 +0530 Subject: [PATCH 05/92] chore: regenerate schema and types --- frontend/apollo/gql.ts | 20 +++++ frontend/apollo/graphql.ts | 146 +++++++++++++++++++++++++++++++-- frontend/apollo/schema.graphql | 6 ++ 3 files changed, 166 insertions(+), 6 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index a59f992b..fc5faf25 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -50,6 +50,8 @@ const documents = { "mutation UpdateMemberRole($memberId: ID!, $roleId: ID!) {\n updateOrganisationMemberRole(memberId: $memberId, roleId: $roleId) {\n orgMember {\n id\n role {\n name\n }\n }\n }\n}": types.UpdateMemberRoleDocument, "mutation UpdateWrappedSecrets($orgId: ID!, $wrappedKeyring: String!, $wrappedRecovery: String!) {\n updateMemberWrappedSecrets(\n orgId: $orgId\n wrappedKeyring: $wrappedKeyring\n wrappedRecovery: $wrappedRecovery\n ) {\n orgMember {\n id\n }\n }\n}": types.UpdateWrappedSecretsDocument, "mutation RotateAppKey($id: ID!, $appToken: String!, $wrappedKeyShare: String!) {\n rotateAppKeys(id: $id, appToken: $appToken, wrappedKeyShare: $wrappedKeyShare) {\n app {\n id\n }\n }\n}": types.RotateAppKeyDocument, + "mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}": types.CreateServiceAccountDocument, + "mutation DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountDocument, "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, "mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument, "mutation DeleteProviderCreds($credentialId: ID!) {\n deleteProviderCredentials(credentialId: $credentialId) {\n ok\n }\n}": types.DeleteProviderCredsDocument, @@ -93,6 +95,8 @@ 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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n eventType\n }\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 GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, + "query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\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 }\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, "query GetCfPages($credentialId: ID!) {\n cloudflarePagesProjects(credentialId: $credentialId) {\n name\n deploymentId\n environments\n }\n}": types.GetCfPagesDocument, @@ -271,6 +275,14 @@ export function graphql(source: "mutation UpdateWrappedSecrets($orgId: ID!, $wra * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation RotateAppKey($id: ID!, $appToken: String!, $wrappedKeyShare: String!) {\n rotateAppKeys(id: $id, appToken: $appToken, wrappedKeyShare: $wrappedKeyShare) {\n app {\n id\n }\n }\n}"): (typeof documents)["mutation RotateAppKey($id: ID!, $appToken: String!, $wrappedKeyShare: String!) {\n rotateAppKeys(id: $id, appToken: $appToken, wrappedKeyShare: $wrappedKeyShare) {\n app {\n id\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 CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}"): (typeof documents)["mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\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 DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -443,6 +455,14 @@ export function graphql(source: "query GetSecrets($appId: ID!, $envId: ID!, $pat * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "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}"): (typeof 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}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}"): (typeof documents)["query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\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: "query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n }\n}"): (typeof documents)["query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\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 9ee4c97d..346a7a47 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -147,7 +147,7 @@ export type AppType = { identityKey: Scalars['String']['output']; members: Array>; name: Scalars['String']['output']; - sseEnabled?: Maybe; + sseEnabled: Scalars['Boolean']['output']; wrappedKeyShare: Scalars['String']['output']; }; @@ -280,6 +280,11 @@ export type CreateSecretTagMutation = { tag?: Maybe; }; +export type CreateServiceAccountMutation = { + __typename?: 'CreateServiceAccountMutation'; + serviceAccount?: Maybe; +}; + export type CreateServiceTokenMutation = { __typename?: 'CreateServiceTokenMutation'; serviceToken?: Maybe; @@ -341,6 +346,11 @@ export type DeleteSecretMutation = { secret?: Maybe; }; +export type DeleteServiceAccountMutation = { + __typename?: 'DeleteServiceAccountMutation'; + ok?: Maybe; +}; + export type DeleteServiceTokenMutation = { __typename?: 'DeleteServiceTokenMutation'; ok?: Maybe; @@ -361,6 +371,11 @@ export type EditSecretMutation = { secret?: Maybe; }; +export type EnableServiceAccountThirdPartyAuthMutation = { + __typename?: 'EnableServiceAccountThirdPartyAuthMutation'; + serviceAccount?: Maybe; +}; + export type EnvironmentInput = { appId: Scalars['ID']['input']; envType: Scalars['String']['input']; @@ -574,6 +589,7 @@ export type Mutation = { createSecretFolder?: Maybe; createSecretTag?: Maybe; createSecrets?: Maybe; + createServiceAccount?: Maybe; createServiceToken?: Maybe; createUserToken?: Maybe; createVaultSync?: Maybe; @@ -587,10 +603,12 @@ export type Mutation = { deleteSecret?: Maybe; deleteSecretFolder?: Maybe; deleteSecrets?: Maybe; + deleteServiceAccount?: Maybe; deleteServiceToken?: Maybe; deleteUserToken?: Maybe; editSecret?: Maybe; editSecrets?: Maybe; + enableServiceAccountThirdPartyAuth?: Maybe; initEnvSync?: Maybe; inviteOrganisationMember?: Maybe; readSecret?: Maybe; @@ -606,6 +624,7 @@ export type Mutation = { updateMemberWrappedSecrets?: Maybe; updateOrganisationMemberRole?: Maybe; updateProviderCredentials?: Maybe; + updateServiceAccountHandlers?: Maybe; updateSyncAuthentication?: Maybe; }; @@ -789,6 +808,17 @@ export type MutationCreateSecretsArgs = { }; +export type MutationCreateServiceAccountArgs = { + handlers?: InputMaybe>>; + identityKey?: InputMaybe; + name?: InputMaybe; + organisationId?: InputMaybe; + roleId?: InputMaybe; + serverWrappedKeyring?: InputMaybe; + serverWrappedRecovery?: InputMaybe; +}; + + export type MutationCreateServiceTokenArgs = { appId: Scalars['ID']['input']; environmentKeys?: InputMaybe>>; @@ -869,6 +899,11 @@ export type MutationDeleteSecretsArgs = { }; +export type MutationDeleteServiceAccountArgs = { + serviceAccountId?: InputMaybe; +}; + + export type MutationDeleteServiceTokenArgs = { tokenId: Scalars['ID']['input']; }; @@ -890,6 +925,13 @@ export type MutationEditSecretsArgs = { }; +export type MutationEnableServiceAccountThirdPartyAuthArgs = { + serverWrappedKeyring?: InputMaybe; + serverWrappedRecovery?: InputMaybe; + serviceAccountId?: InputMaybe; +}; + + export type MutationInitEnvSyncArgs = { appId?: InputMaybe; envKeys?: InputMaybe>>; @@ -984,6 +1026,12 @@ export type MutationUpdateProviderCredentialsArgs = { }; +export type MutationUpdateServiceAccountHandlersArgs = { + handlers?: InputMaybe>>; + serviceAccountId?: InputMaybe; +}; + + export type MutationUpdateSyncAuthenticationArgs = { credentialId?: InputMaybe; syncId?: InputMaybe; @@ -1149,6 +1197,8 @@ export type Query = { secrets?: Maybe>>; secretsLogsCount?: Maybe; serverPublicKey?: Maybe; + serviceAccountHandlers?: Maybe>>; + serviceAccounts?: Maybe>>; serviceTokens?: Maybe>>; services?: Maybe>>; sseEnabled?: Maybe; @@ -1314,6 +1364,16 @@ export type QuerySecretsLogsCountArgs = { }; +export type QueryServiceAccountHandlersArgs = { + orgId?: InputMaybe; +}; + + +export type QueryServiceAccountsArgs = { + orgId?: InputMaybe; +}; + + export type QueryServiceTokensArgs = { appId?: InputMaybe; }; @@ -1488,6 +1548,37 @@ export type ServerEnvironmentKeyType = { wrappedSeed: Scalars['String']['output']; }; +export type ServiceAccountHandlerInput = { + memberId?: InputMaybe; + wrappedKeyring: Scalars['String']['input']; + wrappedRecovery: Scalars['String']['input']; +}; + +export type ServiceAccountHandlerType = { + __typename?: 'ServiceAccountHandlerType'; + createdAt?: Maybe; + id: Scalars['String']['output']; + serviceAccount: ServiceAccountType; + updatedAt: Scalars['DateTime']['output']; + user: OrganisationMemberType; + wrappedKeyring: Scalars['String']['output']; + wrappedRecovery: Scalars['String']['output']; +}; + +export type ServiceAccountType = { + __typename?: 'ServiceAccountType'; + apps: Array; + createdAt?: Maybe; + handlers?: Maybe>>; + id: Scalars['String']['output']; + identityKey?: Maybe; + name: Scalars['String']['output']; + role?: Maybe; + thirdPartyAuthEnabled?: Maybe; + tokens?: Maybe>>; + updatedAt: Scalars['DateTime']['output']; +}; + export type ServiceTokenType = { __typename?: 'ServiceTokenType'; createdAt?: Maybe; @@ -1564,6 +1655,11 @@ export type UpdateProviderCredentials = { credential?: Maybe; }; +export type UpdateServiceAccountHandlersMutation = { + __typename?: 'UpdateServiceAccountHandlersMutation'; + serviceAccount?: Maybe; +}; + export type UpdateSyncAuthentication = { __typename?: 'UpdateSyncAuthentication'; sync?: Maybe; @@ -1912,6 +2008,26 @@ export type RotateAppKeyMutationVariables = Exact<{ export type RotateAppKeyMutation = { __typename?: 'Mutation', rotateAppKeys?: { __typename?: 'RotateAppKeysMutation', app?: { __typename?: 'AppType', id: string } | null } | null }; +export type CreateServiceAccountMutationVariables = Exact<{ + name: Scalars['String']['input']; + orgId: Scalars['ID']['input']; + roleId: Scalars['ID']['input']; + identityKey: Scalars['String']['input']; + handlers?: InputMaybe> | InputMaybe>; + serverWrappedKeyring?: InputMaybe; + serverWrappedRecovery?: InputMaybe; +}>; + + +export type CreateServiceAccountMutation = { __typename?: 'Mutation', createServiceAccount?: { __typename?: 'CreateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string } | null } | null }; + +export type DeleteServiceAccountMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type DeleteServiceAccountMutation = { __typename?: 'Mutation', deleteServiceAccount?: { __typename?: 'DeleteServiceAccountMutation', ok?: boolean | null } | null }; + export type CreateNewAwsSecretsSyncMutationVariables = Exact<{ envId: Scalars['ID']['input']; path: Scalars['String']['input']; @@ -1980,7 +2096,7 @@ export type InitAppSyncingMutationVariables = Exact<{ }>; -export type InitAppSyncingMutation = { __typename?: 'Mutation', initEnvSync?: { __typename?: 'InitEnvSync', app?: { __typename?: 'AppType', id: string, sseEnabled?: boolean | null } | null } | null }; +export type InitAppSyncingMutation = { __typename?: 'Mutation', initEnvSync?: { __typename?: 'InitEnvSync', app?: { __typename?: 'AppType', id: string, sseEnabled: boolean } | null } | null }; export type CreateNewNomadSyncMutationVariables = Exact<{ envId: Scalars['ID']['input']; @@ -2104,7 +2220,7 @@ export type GetAppDetailQueryVariables = Exact<{ }>; -export type GetAppDetailQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, appToken: string, appSeed: string, appVersion: number, sseEnabled?: boolean | null } | null> | null }; +export type GetAppDetailQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, appToken: string, appSeed: string, appVersion: number, sseEnabled: boolean } | null> | null }; export type GetAppKmsLogsQueryVariables = Exact<{ appId: Scalars['ID']['input']; @@ -2121,14 +2237,14 @@ export type GetAppsQueryVariables = Exact<{ }>; -export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled?: boolean | null, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; +export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; export type GetDashboardQueryVariables = Exact<{ organisationId: Scalars['ID']['input']; }>; -export type GetDashboardQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, sseEnabled?: boolean | null } | null> | null, userTokens?: Array<{ __typename?: 'UserTokenType', id: string } | null> | null, organisationInvites?: Array<{ __typename?: 'OrganisationMemberInviteType', id: string } | null> | null, organisationMembers?: Array<{ __typename?: 'OrganisationMemberType', id: string } | null> | null, savedCredentials?: Array<{ __typename?: 'ProviderCredentialsType', id: string } | null> | null, syncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string } | null> | null }; +export type GetDashboardQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, sseEnabled: boolean } | null> | null, userTokens?: Array<{ __typename?: 'UserTokenType', id: string } | null> | null, organisationInvites?: Array<{ __typename?: 'OrganisationMemberInviteType', id: string } | null> | null, organisationMembers?: Array<{ __typename?: 'OrganisationMemberType', id: string } | null> | null, savedCredentials?: Array<{ __typename?: 'ProviderCredentialsType', id: string } | null> | null, syncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string } | null> | null }; export type GetOrganisationsQueryVariables = Exact<{ [key: string]: never; }>; @@ -2267,12 +2383,26 @@ export type GetServiceTokensQueryVariables = Exact<{ export type GetServiceTokensQuery = { __typename?: 'Query', serviceTokens?: Array<{ __typename?: 'ServiceTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null, keys: Array<{ __typename?: 'ServerEnvironmentKeyType', id: string, identityKey: string }> } | null> | null }; +export type GetServiceAccountHandlersQueryVariables = Exact<{ + orgId: Scalars['ID']['input']; +}>; + + +export type GetServiceAccountHandlersQuery = { __typename?: 'Query', serviceAccountHandlers?: Array<{ __typename?: 'OrganisationMemberType', id: string, identityKey?: string | null, self?: boolean | null, role?: { __typename?: 'RoleType', name?: string | null, permissions?: any | null } | null } | null> | null }; + +export type GetServiceAccountsQueryVariables = Exact<{ + orgId: Scalars['ID']['input']; +}>; + + +export type GetServiceAccountsQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, permissions?: any | null } | null } | null> | null }; + export type GetOrganisationSyncsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; }>; -export type GetOrganisationSyncsQuery = { __typename?: 'Query', syncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string, path: string, options: any, isActive: boolean, lastSync?: any | null, status: ApiEnvironmentSyncStatusChoices, createdAt?: any | null, environment: { __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, app: { __typename?: 'AppType', id: string, name: string } }, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string } | null } | null, authentication?: { __typename?: 'ProviderCredentialsType', id: string, name: string, credentials: any } | null, history: Array<{ __typename?: 'EnvironmentSyncEventType', id: string, status: ApiEnvironmentSyncEventStatusChoices, createdAt?: any | null, completedAt?: any | null, meta?: any | null }> } | null> | null, savedCredentials?: Array<{ __typename?: 'ProviderCredentialsType', id: string, name: string, credentials: any, createdAt?: any | null, syncCount?: number | null, provider?: { __typename?: 'ProviderType', id: string, name: string, expectedCredentials: Array, optionalCredentials: Array } | null } | null> | null, apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled?: boolean | null, members: Array<{ __typename?: 'OrganisationMemberType', id: string } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; +export type GetOrganisationSyncsQuery = { __typename?: 'Query', syncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string, path: string, options: any, isActive: boolean, lastSync?: any | null, status: ApiEnvironmentSyncStatusChoices, createdAt?: any | null, environment: { __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, app: { __typename?: 'AppType', id: string, name: string } }, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string } | null } | null, authentication?: { __typename?: 'ProviderCredentialsType', id: string, name: string, credentials: any } | null, history: Array<{ __typename?: 'EnvironmentSyncEventType', id: string, status: ApiEnvironmentSyncEventStatusChoices, createdAt?: any | null, completedAt?: any | null, meta?: any | null }> } | null> | null, savedCredentials?: Array<{ __typename?: 'ProviderCredentialsType', id: string, name: string, credentials: any, createdAt?: any | null, syncCount?: number | null, provider?: { __typename?: 'ProviderType', id: string, name: string, expectedCredentials: Array, optionalCredentials: Array } | null } | null> | null, apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; export type GetAwsSecretsQueryVariables = Exact<{ credentialId: Scalars['ID']['input']; @@ -2397,6 +2527,8 @@ export const InviteMemberDocument = {"kind":"Document","definitions":[{"kind":"O export const UpdateMemberRoleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateMemberRole"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"updateOrganisationMemberRole"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"memberId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}}},{"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":"orgMember"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateWrappedSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWrappedSecrets"},"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":"wrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateMemberWrappedSecrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orgMember"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const RotateAppKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RotateAppKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"rotateAppKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"appToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateServiceAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateServiceAccount"},"variableDefinitions":[{"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":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}},{"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"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteServiceAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccount"},"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 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; export const CreateNewCfPagesSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfPagesSync"},"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":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflarePagesSync"},"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":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"deploymentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectEnv"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}}},{"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":"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":"id"}},{"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; export const DeleteProviderCredsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteProviderCreds"},"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":"deleteProviderCredentials"},"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":"ok"}}]}}]}}]} as unknown as DocumentNode; @@ -2440,6 +2572,8 @@ 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}}]}},{"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 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":"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 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"}}}}],"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"}}}],"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":"permissions"}}]}},{"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":"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; export const GetCfPagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCfPages"},"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":"cloudflarePagesProjects"},"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":"deploymentId"}},{"kind":"Field","name":{"kind":"Name","value":"environments"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 1980a4b3..024e8653 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -25,6 +25,7 @@ type Query { userTokens(organisationId: ID): [UserTokenType] serviceTokens(appId: ID): [ServiceTokenType] serviceAccounts(orgId: ID): [ServiceAccountType] + serviceAccountHandlers(orgId: ID): [OrganisationMemberType] serverPublicKey: String sseEnabled(appId: ID): Boolean providers: [ProviderType] @@ -637,6 +638,7 @@ type Mutation { createServiceAccount(handlers: [ServiceAccountHandlerInput], identityKey: String, name: String, organisationId: ID, roleId: ID, serverWrappedKeyring: String, serverWrappedRecovery: String): CreateServiceAccountMutation enableServiceAccountThirdPartyAuth(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountThirdPartyAuthMutation updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], serviceAccountId: ID): UpdateServiceAccountHandlersMutation + deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation initEnvSync(appId: ID, envKeys: [EnvironmentKeyInput]): InitEnvSync deleteEnvSync(syncId: ID): DeleteSync triggerSync(syncId: ID): TriggerSync @@ -795,6 +797,10 @@ type UpdateServiceAccountHandlersMutation { serviceAccount: ServiceAccountType } +type DeleteServiceAccountMutation { + ok: Boolean +} + type InitEnvSync { app: AppType } From 619a191a30951d94688b585ca25d21833adaddd2 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 21 Oct 2024 18:15:02 +0530 Subject: [PATCH 06/92] feat: add client queries --- .../service-accounts/createServiceAccount.gql | 23 +++++++++++++++++++ .../service-accounts/deleteServiceAccount.gql | 5 ++++ .../getServiceAccountHandlers.gql | 11 +++++++++ .../service-accounts/getServiceAccounts.gql | 12 ++++++++++ 4 files changed, 51 insertions(+) create mode 100644 frontend/graphql/mutations/service-accounts/createServiceAccount.gql create mode 100644 frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql create mode 100644 frontend/graphql/queries/service-accounts/getServiceAccountHandlers.gql create mode 100644 frontend/graphql/queries/service-accounts/getServiceAccounts.gql diff --git a/frontend/graphql/mutations/service-accounts/createServiceAccount.gql b/frontend/graphql/mutations/service-accounts/createServiceAccount.gql new file mode 100644 index 00000000..b80c932b --- /dev/null +++ b/frontend/graphql/mutations/service-accounts/createServiceAccount.gql @@ -0,0 +1,23 @@ +mutation CreateServiceAccount( + $name: String! + $orgId: ID! + $roleId: ID! + $identityKey: String! + $handlers: [ServiceAccountHandlerInput] + $serverWrappedKeyring: String + $serverWrappedRecovery: String +) { + createServiceAccount( + name: $name + organisationId: $orgId + roleId: $roleId + identityKey: $identityKey + handlers: $handlers + serverWrappedKeyring: $serverWrappedKeyring + serverWrappedRecovery: $serverWrappedRecovery + ) { + serviceAccount { + id + } + } +} diff --git a/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql b/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql new file mode 100644 index 00000000..ae545236 --- /dev/null +++ b/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql @@ -0,0 +1,5 @@ +mutation DeleteServiceAccount($id: ID!) { + deleteServiceAccount(serviceAccountId: $id) { + ok + } +} diff --git a/frontend/graphql/queries/service-accounts/getServiceAccountHandlers.gql b/frontend/graphql/queries/service-accounts/getServiceAccountHandlers.gql new file mode 100644 index 00000000..9c38e11a --- /dev/null +++ b/frontend/graphql/queries/service-accounts/getServiceAccountHandlers.gql @@ -0,0 +1,11 @@ +query GetServiceAccountHandlers($orgId: ID!) { + serviceAccountHandlers(orgId: $orgId) { + id + role { + name + permissions + } + identityKey + self + } +} diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql new file mode 100644 index 00000000..e3ac35e4 --- /dev/null +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -0,0 +1,12 @@ +query GetServiceAccounts($orgId: ID!) { + serviceAccounts(orgId: $orgId) { + id + name + role { + id + name + permissions + } + createdAt + } +} From 3e46b57abd687a27e07d809b166d059ab400276a Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 21 Oct 2024 18:15:23 +0530 Subject: [PATCH 07/92] feat: add service account page, create and delete dialogs --- frontend/app/[team]/access/layout.tsx | 4 + .../CreateServiceAccountDialog.tsx | 200 ++++++++++++++++++ .../DeleteServiceAccountDialog.tsx | 54 +++++ .../[team]/access/service-accounts/page.tsx | 109 ++++++++++ 4 files changed, 367 insertions(+) create mode 100644 frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx create mode 100644 frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx create mode 100644 frontend/app/[team]/access/service-accounts/page.tsx diff --git a/frontend/app/[team]/access/layout.tsx b/frontend/app/[team]/access/layout.tsx index c3e169c7..09d74d9d 100644 --- a/frontend/app/[team]/access/layout.tsx +++ b/frontend/app/[team]/access/layout.tsx @@ -29,6 +29,10 @@ export default function AccessLayout({ name: 'Members', link: 'members', }, + { + name: 'Service Accounts', + link: 'service-accounts', + }, { name: 'Roles', link: 'roles', diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx new file mode 100644 index 00000000..f6b30824 --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -0,0 +1,200 @@ +import { OrganisationMemberType, RoleType } from '@/apollo/graphql' +import GenericDialog from '@/components/common/GenericDialog' +import { Fragment, useContext, useRef, useState } from 'react' +import { FaChevronDown, FaPlus } from 'react-icons/fa' +import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' +import { GetServiceAccountHandlers } from '@/graphql/queries/service-accounts/getServiceAccountHandlers.gql' +import { GetRoles } from '@/graphql/queries/organisation/getRoles.gql' +import { GetServerKey } from '@/graphql/queries/syncing/getServerKey.gql' +import { CreateServiceAccount } from '@/graphql/mutations/service-accounts/createServiceAccount.gql' +import { organisationContext } from '@/contexts/organisationContext' +import { useMutation, useQuery } from '@apollo/client' +import { + organisationSeed, + organisationKeyring, + deviceVaultKey, + encryptAccountKeyring, + encryptAccountRecovery, + OrganisationKeyring, + getUserKxPublicKey, +} from '@/utils/crypto' +import { Input } from '@/components/common/Input' +import { RoleLabel } from '@/components/users/RoleLabel' +import { Listbox } from '@headlessui/react' +import clsx from 'clsx' +import { ToggleSwitch } from '@/components/common/ToggleSwitch' +import { Button } from '@/components/common/Button' +import { identity } from 'lodash' +import { toast } from 'react-toastify' + +const bip39 = require('bip39') + +export const CreateServiceAccountDialog = () => { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const { data: roleData, loading: roleDataPending } = useQuery(GetRoles, { + variables: { orgId: organisation?.id }, + skip: !organisation, + }) + + const { data: serviceAccountHandlerData } = useQuery(GetServiceAccountHandlers, { + variables: { orgId: organisation?.id }, + skip: !organisation, + }) + + const [createServiceAccount] = useMutation(CreateServiceAccount) + + const { data: serverKeyData } = useQuery(GetServerKey) + + const dialogRef = useRef<{ closeModal: () => void }>(null) + + const [name, setName] = useState('') + const [role, setRole] = useState(null) + const [thirdParty, setThirdParty] = useState(false) + const [createPending, setCreatePending] = useState(false) + + const roleOptions = roleData?.roles.filter((option: RoleType) => option.name !== 'Owner') || [] + + const handleCreateServiceAccount = (e: { preventDefault: () => void }) => { + return new Promise((resolve) => { + e.preventDefault() + setCreatePending(true) + setTimeout(async () => { + // Compute new keys for service account + const mnemonic = bip39.generateMnemonic(256) + const accountSeed = await organisationSeed(mnemonic, organisation!.id) + const keyring = await organisationKeyring(accountSeed) + + // Wrap keys for server if required + let serverKeys = undefined + if (thirdParty) { + const serverKey = serverKeyData.serverPublicKey + + const serverEncryptedKeyring = await encryptAccountKeyring(keyring, serverKey) + + const serverEncryptedMnemonic = await encryptAccountRecovery(mnemonic, serverKey) + + serverKeys = { + serverEncryptedKeyring, + serverEncryptedMnemonic, + } + } + + // Wrap keys for service account handlers + const handlers: OrganisationMemberType[] = serviceAccountHandlerData.serviceAccountHandlers + + const handlerWrappingPromises = handlers.map(async (handler) => { + const kxKey = await getUserKxPublicKey(handler.identityKey!) + const wrappedKeyring = await encryptAccountKeyring(keyring, kxKey) + const wrappedRecovery = await encryptAccountRecovery(mnemonic, kxKey) + return { + memberId: handler.id, + wrappedKeyring, + wrappedRecovery, + } + }) + + const handlerKeys = await Promise.all(handlerWrappingPromises) + + await createServiceAccount({ + variables: { + name, + orgId: organisation!.id, + roleId: role!.id, + identityKey: keyring.publicKey, + serverWrappedKeyring: serverKeys?.serverEncryptedKeyring || null, + serverWrappedRecovery: serverKeys?.serverEncryptedMnemonic || null, + handlers: handlerKeys, + }, + refetchQueries: [ + { + query: GetServiceAccounts, + variables: { orgId: organisation!.id }, + }, + ], + }) + + setCreatePending(false) + + if (dialogRef.current) dialogRef.current.closeModal() + + toast.success('Created new service account!') + + resolve(true) + }, 500) + }) + } + + return ( + + Create Service Account + + } + buttonVariant="primary" + size="lg" + ref={dialogRef} + > +
+
+ +
+ + + {({ open }) => ( + <> + +
+ {role ? : <>} + +
+
+ + {roleOptions.map((role: RoleType) => ( + + {({ active, selected }) => ( +
+ +
+ )} +
+ ))} +
+ + )} +
+
+
+ + setThirdParty(!thirdParty)} /> +
+
+
+ +
+
+
+ ) +} diff --git a/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx new file mode 100644 index 00000000..34362eac --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx @@ -0,0 +1,54 @@ +import { FaTrash } from 'react-icons/fa' +import { DeleteServiceAccount } from '@/graphql/mutations/service-accounts/deleteServiceAccount.gql' +import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' +import { useMutation } from '@apollo/client' +import { toast } from 'react-toastify' +import { useContext, useRef } from 'react' +import { organisationContext } from '@/contexts/organisationContext' +import { ServiceAccountType } from '@/apollo/graphql' +import { Button } from '@/components/common/Button' +import GenericDialog from '@/components/common/GenericDialog' + +export const DeleteServiceAccountDialog = ({ account }: { account: ServiceAccountType }) => { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const dialogRef = useRef<{ closeModal: () => void }>(null) + + const [deleteAccount] = useMutation(DeleteServiceAccount) + + const handleDelete = async () => { + const deleted = await deleteAccount({ + variables: { id: account.id }, + refetchQueries: [{ query: GetServiceAccounts, variables: { orgId: organisation!.id } }], + }) + if (deleted.data.deleteServiceAccount.ok) { + toast.success('Deleted service account!') + if (dialogRef.current) dialogRef.current.closeModal() + } + } + + return ( + + Delete + + } + buttonVariant="danger" + ref={dialogRef} + > +
+
+ Are you sure you want to delete this service account? This will delete all service tokens + associated with this account. +
+
+ +
+
+
+ ) +} diff --git a/frontend/app/[team]/access/service-accounts/page.tsx b/frontend/app/[team]/access/service-accounts/page.tsx new file mode 100644 index 00000000..cc75de44 --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/page.tsx @@ -0,0 +1,109 @@ +'use client' + +import { ServiceAccountType } from '@/apollo/graphql' +import { EmptyState } from '@/components/common/EmptyState' +import { RoleLabel } from '@/components/users/RoleLabel' +import { organisationContext } from '@/contexts/organisationContext' +import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' +import { userHasPermission } from '@/utils/access/permissions' +import { useQuery } from '@apollo/client' +import { useContext } from 'react' +import { FaBan } from 'react-icons/fa' +import { CreateServiceAccountDialog } from './_components/CreateServiceAccountDialog' +import { FaRobot } from 'react-icons/fa6' +import { relativeTimeFromDates } from '@/utils/time' +import { DeleteServiceAccountDialog } from './_components/DeleteServiceAccountDialog' + +export default function ServiceAccounts({ params }: { params: { team: string } }) { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const userCanReadSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'read') + : false + + const userCanCreateSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'create') + : false + + const userCanDeleteSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'delete') + : false + + const { data, loading } = useQuery(GetServiceAccounts, { + variables: { orgId: organisation?.id }, + skip: !organisation || !userCanReadSA, + }) + + return ( +
+
+
+

{params.team} Service Accounts

+

Manage service accounts.

+
+
+ {userCanCreateSA && ( +
+ +
+ )} + + {userCanReadSA ? ( + + + + + + + + + + + {data?.serviceAccounts.map((account: ServiceAccountType) => ( + + + + + + + + + + ))} + +
+ Account name + + Role + + Created +
+
+ +
+ {account.name} +
+ + + {relativeTimeFromDates(new Date(account.createdAt))} + + {userCanDeleteSA && } +
+ ) : ( + + +
+ } + > + <> + + )} +
+ +
+ ) +} From 75169e200237f161e058d6412ca8fdcc2dceacca Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:02:04 +0530 Subject: [PATCH 08/92] feat: add new service account token model --- ...e_servicetoken_service_account_and_more.py | 35 +++++++++++++++++++ backend/api/models.py | 19 ++++++++-- backend/backend/graphene/types.py | 11 ++++-- 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 backend/api/migrations/0086_remove_servicetoken_service_account_and_more.py diff --git a/backend/api/migrations/0086_remove_servicetoken_service_account_and_more.py b/backend/api/migrations/0086_remove_servicetoken_service_account_and_more.py new file mode 100644 index 00000000..32b22764 --- /dev/null +++ b/backend/api/migrations/0086_remove_servicetoken_service_account_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.15 on 2024-10-22 07:29 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0085_serviceaccount_environmentkey_paths_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='servicetoken', + name='service_account', + ), + migrations.CreateModel( + name='ServiceAccountToken', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('identity_key', models.CharField(max_length=256)), + ('token', models.CharField(max_length=64)), + ('wrapped_key_share', models.CharField(max_length=406)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ('expires_at', models.DateTimeField(null=True)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember')), + ('service_account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount')), + ], + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 8122ec5f..f8f53221 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -448,9 +448,6 @@ class ServiceToken(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) app = models.ForeignKey(App, on_delete=models.CASCADE) keys = models.ManyToManyField(EnvironmentKey) - service_account = models.ForeignKey( - ServiceAccount, on_delete=models.CASCADE, null=True - ) identity_key = models.CharField(max_length=256) token = models.CharField(max_length=64) wrapped_key_share = models.CharField(max_length=406) @@ -465,6 +462,22 @@ class ServiceToken(models.Model): objects = ServiceTokenManager() +class ServiceAccountToken(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + service_account = models.ForeignKey(ServiceAccount, on_delete=models.CASCADE) + name = models.CharField(max_length=64) + identity_key = models.CharField(max_length=256) + token = models.CharField(max_length=64) + wrapped_key_share = models.CharField(max_length=406) + created_by = models.ForeignKey( + OrganisationMember, on_delete=models.CASCADE, blank=True, null=True + ) + 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) + + class UserToken(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) user = models.ForeignKey( diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index f9b0c486..8e4143f5 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -29,6 +29,7 @@ ServerEnvironmentKey, ServiceAccount, ServiceAccountHandler, + ServiceAccountToken, ServiceToken, UserToken, ) @@ -465,11 +466,17 @@ class Meta: fields = "__all__" +class ServiceAccountTokenType(DjangoObjectType): + class Meta: + model = ServiceAccountToken + fields = "__all__" + + class ServiceAccountType(DjangoObjectType): third_party_auth_enabled = graphene.Boolean() handlers = graphene.List(ServiceAccountHandlerType) - tokens = graphene.List(ServiceTokenType) + tokens = graphene.List(ServiceAccountTokenType) class Meta: model = ServiceAccount @@ -493,7 +500,7 @@ def resolve_handlers(self, info): return ServiceAccountHandler.objects.filter(service_account=self) def resolve_tokens(self, info): - return ServiceToken.objects.filter(service_account=self) + return ServiceAccountToken.objects.filter(service_account=self) class SecretFolderType(DjangoObjectType): From adb26286e9f876369af91776474163e4483b678a Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:02:31 +0530 Subject: [PATCH 09/92] feat: allow fetching single service account by id --- backend/backend/graphene/queries/service_accounts.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/backend/graphene/queries/service_accounts.py b/backend/backend/graphene/queries/service_accounts.py index b28cf8ef..36a97c15 100644 --- a/backend/backend/graphene/queries/service_accounts.py +++ b/backend/backend/graphene/queries/service_accounts.py @@ -5,10 +5,16 @@ from graphql import GraphQLError -def resolve_service_accounts(root, info, org_id): +def resolve_service_accounts(root, info, org_id, service_account_id=None): org = Organisation.objects.get(id=org_id) if user_has_permission(info.context.user.userId, "read", "ServiceAccounts", org): - return ServiceAccount.objects.filter(organisation=org) + + filter = {"organisation": org} + + if service_account_id is not None: + filter["id"] = service_account_id + + return ServiceAccount.objects.filter(**filter) def resolve_service_account_handlers(root, info, org_id): From 56e4373913d75aa29f2cc3e878b4349e8f3bfb99 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:03:07 +0530 Subject: [PATCH 10/92] feat: add sa token create mutation --- .../graphene/mutations/service_accounts.py | 66 ++++++++++++++++++- backend/backend/schema.py | 8 ++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index ebba081a..97c34a9a 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -1,9 +1,17 @@ from backend.graphene.mutations.environment import EnvironmentKeyInput import graphene from graphql import GraphQLError -from api.models import Organisation, Role, ServiceAccount, ServiceAccountHandler +from api.models import ( + Organisation, + OrganisationMember, + Role, + ServiceAccount, + ServiceAccountHandler, + ServiceAccountToken, +) from api.utils.access.permissions import user_has_permission -from backend.graphene.types import ServiceAccountType +from backend.graphene.types import ServiceAccountTokenType, ServiceAccountType +from datetime import datetime class ServiceAccountHandlerInput(graphene.InputObjectType): @@ -157,3 +165,57 @@ def mutate(cls, root, info, service_account_id): service_account.delete() return DeleteServiceAccountMutation(ok=True) + + +class CreateServiceAccountTokenMutation(graphene.Mutation): + class Arguments: + service_account_id = graphene.ID() + name = graphene.String(required=True) + identity_key = graphene.String(required=True) + token = graphene.String(required=True) + wrapped_key_share = graphene.String(required=True) + expiry = graphene.BigInt(required=False) + + token = graphene.Field(ServiceAccountTokenType) + + @classmethod + def mutate( + cls, + root, + info, + service_account_id, + name, + identity_key, + token, + wrapped_key_share, + expiry, + ): + user = info.context.user + service_account = ServiceAccount.objects.get(id=service_account_id) + org_member = OrganisationMember.objects.get( + user=user, organisation=service_account.organisation, deleted_at=None + ) + + if not user_has_permission( + user, "create", "ServiceAccounts", service_account.organisation + ): + raise GraphQLError( + "You don't have the permissions required to create Service Tokens in this organisation" + ) + + if expiry is not None: + expires_at = datetime.fromtimestamp(expiry / 1000) + else: + expires_at = None + + token = ServiceAccountToken.objects.create( + service_account=service_account, + name=name, + identity_key=identity_key, + token=token, + wrapped_key_share=wrapped_key_share, + created_by=org_member, + expires_at=expires_at, + ) + + return CreateServiceAccountTokenMutation(token=token) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index d135b0b0..8e314eff 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -5,6 +5,7 @@ from api.utils.syncing.railway.main import RailwayProjectType from backend.graphene.mutations.service_accounts import ( CreateServiceAccountMutation, + CreateServiceAccountTokenMutation, DeleteServiceAccountMutation, EnableServiceAccountThirdPartyAuthMutation, UpdateServiceAccountHandlersMutation, @@ -245,7 +246,11 @@ class Query(graphene.ObjectType): user_tokens = graphene.List(UserTokenType, organisation_id=graphene.ID()) service_tokens = graphene.List(ServiceTokenType, app_id=graphene.ID()) - service_accounts = graphene.List(ServiceAccountType, org_id=graphene.ID()) + service_accounts = graphene.List( + ServiceAccountType, + org_id=graphene.ID(), + service_account_id=graphene.ID(required=False), + ) service_account_handlers = graphene.List( OrganisationMemberType, org_id=graphene.ID() @@ -771,6 +776,7 @@ class Mutation(graphene.ObjectType): ) update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field() delete_service_account = DeleteServiceAccountMutation.Field() + create_service_account_token = CreateServiceAccountTokenMutation.Field() init_env_sync = InitEnvSync.Field() delete_env_sync = DeleteSync.Field() From 290da6fd2007917e599ded69d900f98b6ed2f037 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:03:31 +0530 Subject: [PATCH 11/92] feat: add client side queries --- .../createServiceAccountToken.gql | 21 ++++++++++++++++++ .../service-accounts/getServiceAccounts.gql | 22 +++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 frontend/graphql/mutations/service-accounts/createServiceAccountToken.gql diff --git a/frontend/graphql/mutations/service-accounts/createServiceAccountToken.gql b/frontend/graphql/mutations/service-accounts/createServiceAccountToken.gql new file mode 100644 index 00000000..2dede669 --- /dev/null +++ b/frontend/graphql/mutations/service-accounts/createServiceAccountToken.gql @@ -0,0 +1,21 @@ +mutation CreateSAToken( + $serviceAccountId: ID! + $name: String! + $identityKey: String! + $token: String! + $wrappedKeyShare: String! + $expiry: BigInt +) { + createServiceAccountToken( + serviceAccountId: $serviceAccountId + name: $name + identityKey: $identityKey + token: $token + wrappedKeyShare: $wrappedKeyShare + expiry: $expiry + ) { + token { + id + } + } +} diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index e3ac35e4..790f6127 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -1,5 +1,5 @@ -query GetServiceAccounts($orgId: ID!) { - serviceAccounts(orgId: $orgId) { +query GetServiceAccounts($orgId: ID!, $id: ID) { + serviceAccounts(orgId: $orgId, serviceAccountId: $id) { id name role { @@ -8,5 +8,23 @@ query GetServiceAccounts($orgId: ID!) { permissions } createdAt + handlers { + id + wrappedKeyring + user { + self + } + } + tokens { + id + name + createdAt + expiresAt + createdBy { + fullName + avatarUrl + self + } + } } } From 31f2e7b27355d2c5d2db66ccec84640b91c086c2 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:03:44 +0530 Subject: [PATCH 12/92] chore: regenerate graphql schema and types --- frontend/apollo/gql.ts | 9 ++++-- frontend/apollo/graphql.ts | 51 ++++++++++++++++++++++++++++++++-- frontend/apollo/schema.graphql | 23 +++++++++++++-- 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index fc5faf25..a976bb15 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -51,6 +51,7 @@ const documents = { "mutation UpdateWrappedSecrets($orgId: ID!, $wrappedKeyring: String!, $wrappedRecovery: String!) {\n updateMemberWrappedSecrets(\n orgId: $orgId\n wrappedKeyring: $wrappedKeyring\n wrappedRecovery: $wrappedRecovery\n ) {\n orgMember {\n id\n }\n }\n}": types.UpdateWrappedSecretsDocument, "mutation RotateAppKey($id: ID!, $appToken: String!, $wrappedKeyShare: String!) {\n rotateAppKeys(id: $id, appToken: $appToken, wrappedKeyShare: $wrappedKeyShare) {\n app {\n id\n }\n }\n}": types.RotateAppKeyDocument, "mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}": types.CreateServiceAccountDocument, + "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 DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountDocument, "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, "mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument, @@ -96,7 +97,7 @@ const documents = { "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n eventType\n }\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 GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, - "query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n }\n}": types.GetServiceAccountsDocument, + "query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\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 }\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, "query GetCfPages($credentialId: ID!) {\n cloudflarePagesProjects(credentialId: $credentialId) {\n name\n deploymentId\n environments\n }\n}": types.GetCfPagesDocument, @@ -279,6 +280,10 @@ export function graphql(source: "mutation RotateAppKey($id: ID!, $appToken: Stri * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}"): (typeof documents)["mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\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 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}"): (typeof 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}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -462,7 +467,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 GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n }\n}"): (typeof documents)["query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n }\n}"]; +export function graphql(source: "query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}"): (typeof documents)["query GetServiceAccounts($orgId: ID!) {\n serviceAccounts(orgId: $orgId) {\n id\n name\n role {\n id\n name\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\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 346a7a47..8073c0e6 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -285,6 +285,11 @@ export type CreateServiceAccountMutation = { serviceAccount?: Maybe; }; +export type CreateServiceAccountTokenMutation = { + __typename?: 'CreateServiceAccountTokenMutation'; + token?: Maybe; +}; + export type CreateServiceTokenMutation = { __typename?: 'CreateServiceTokenMutation'; serviceToken?: Maybe; @@ -590,6 +595,7 @@ export type Mutation = { createSecretTag?: Maybe; createSecrets?: Maybe; createServiceAccount?: Maybe; + createServiceAccountToken?: Maybe; createServiceToken?: Maybe; createUserToken?: Maybe; createVaultSync?: Maybe; @@ -819,6 +825,16 @@ export type MutationCreateServiceAccountArgs = { }; +export type MutationCreateServiceAccountTokenArgs = { + expiry?: InputMaybe; + identityKey: Scalars['String']['input']; + name: Scalars['String']['input']; + serviceAccountId?: InputMaybe; + token: Scalars['String']['input']; + wrappedKeyShare: Scalars['String']['input']; +}; + + export type MutationCreateServiceTokenArgs = { appId: Scalars['ID']['input']; environmentKeys?: InputMaybe>>; @@ -1371,6 +1387,7 @@ export type QueryServiceAccountHandlersArgs = { export type QueryServiceAccountsArgs = { orgId?: InputMaybe; + serviceAccountId?: InputMaybe; }; @@ -1565,6 +1582,21 @@ export type ServiceAccountHandlerType = { wrappedRecovery: Scalars['String']['output']; }; +export type ServiceAccountTokenType = { + __typename?: 'ServiceAccountTokenType'; + createdAt?: Maybe; + createdBy?: Maybe; + deletedAt?: Maybe; + expiresAt?: Maybe; + id: Scalars['String']['output']; + identityKey: Scalars['String']['output']; + name: Scalars['String']['output']; + serviceAccount: ServiceAccountType; + token: Scalars['String']['output']; + updatedAt: Scalars['DateTime']['output']; + wrappedKeyShare: Scalars['String']['output']; +}; + export type ServiceAccountType = { __typename?: 'ServiceAccountType'; apps: Array; @@ -1575,7 +1607,7 @@ export type ServiceAccountType = { name: Scalars['String']['output']; role?: Maybe; thirdPartyAuthEnabled?: Maybe; - tokens?: Maybe>>; + tokens?: Maybe>>; updatedAt: Scalars['DateTime']['output']; }; @@ -2021,6 +2053,18 @@ export type CreateServiceAccountMutationVariables = Exact<{ export type CreateServiceAccountMutation = { __typename?: 'Mutation', createServiceAccount?: { __typename?: 'CreateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string } | null } | null }; +export type CreateSaTokenMutationVariables = Exact<{ + serviceAccountId: Scalars['ID']['input']; + name: Scalars['String']['input']; + identityKey: Scalars['String']['input']; + token: Scalars['String']['input']; + wrappedKeyShare: Scalars['String']['input']; + expiry?: InputMaybe; +}>; + + +export type CreateSaTokenMutation = { __typename?: 'Mutation', createServiceAccountToken?: { __typename?: 'CreateServiceAccountTokenMutation', token?: { __typename?: 'ServiceAccountTokenType', id: string } | null } | null }; + export type DeleteServiceAccountMutationVariables = Exact<{ id: Scalars['ID']['input']; }>; @@ -2395,7 +2439,7 @@ export type GetServiceAccountsQueryVariables = Exact<{ }>; -export type GetServiceAccountsQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, permissions?: any | null } | null } | null> | null }; +export type GetServiceAccountsQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null } | null> | null }; export type GetOrganisationSyncsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2528,6 +2572,7 @@ export const UpdateMemberRoleDocument = {"kind":"Document","definitions":[{"kind export const UpdateWrappedSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWrappedSecrets"},"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":"wrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateMemberWrappedSecrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orgMember"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const RotateAppKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RotateAppKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"rotateAppKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"appToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateServiceAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateServiceAccount"},"variableDefinitions":[{"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":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}},{"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"}}]}}]}}]}}]} as unknown as DocumentNode; +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 DeleteServiceAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccount"},"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 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; export const CreateNewCfPagesSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfPagesSync"},"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":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflarePagesSync"},"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":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"deploymentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectEnv"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}}},{"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":"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":"id"}},{"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; @@ -2573,7 +2618,7 @@ export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":" 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}}]}},{"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 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":"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 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"}}}}],"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"}}}],"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":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} 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"}}}}],"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"}}}],"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":"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":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"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"}}]}}]}}]}}]}}]} 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":"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; export const GetCfPagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCfPages"},"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":"cloudflarePagesProjects"},"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":"deploymentId"}},{"kind":"Field","name":{"kind":"Name","value":"environments"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 024e8653..bc40bdbe 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -24,7 +24,7 @@ type Query { environmentTokens(environmentId: ID): [EnvironmentTokenType] userTokens(organisationId: ID): [UserTokenType] serviceTokens(appId: ID): [ServiceTokenType] - serviceAccounts(orgId: ID): [ServiceAccountType] + serviceAccounts(orgId: ID, serviceAccountId: ID): [ServiceAccountType] serviceAccountHandlers(orgId: ID): [OrganisationMemberType] serverPublicKey: String sseEnabled(appId: ID): Boolean @@ -500,7 +500,7 @@ type ServiceAccountType { updatedAt: DateTime! thirdPartyAuthEnabled: Boolean handlers: [ServiceAccountHandlerType] - tokens: [ServiceTokenType] + tokens: [ServiceAccountTokenType] } type ServiceAccountHandlerType { @@ -513,6 +513,20 @@ type ServiceAccountHandlerType { updatedAt: DateTime! } +type ServiceAccountTokenType { + id: String! + serviceAccount: ServiceAccountType! + name: String! + identityKey: String! + token: String! + wrappedKeyShare: String! + createdBy: OrganisationMemberType + createdAt: DateTime + updatedAt: DateTime! + deletedAt: DateTime + expiresAt: DateTime +} + type CloudFlarePagesType { name: String deploymentId: String @@ -639,6 +653,7 @@ type Mutation { enableServiceAccountThirdPartyAuth(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountThirdPartyAuthMutation updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], serviceAccountId: ID): UpdateServiceAccountHandlersMutation deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation + createServiceAccountToken(expiry: BigInt, identityKey: String!, name: String!, serviceAccountId: ID, token: String!, wrappedKeyShare: String!): CreateServiceAccountTokenMutation initEnvSync(appId: ID, envKeys: [EnvironmentKeyInput]): InitEnvSync deleteEnvSync(syncId: ID): DeleteSync triggerSync(syncId: ID): TriggerSync @@ -801,6 +816,10 @@ type DeleteServiceAccountMutation { ok: Boolean } +type CreateServiceAccountTokenMutation { + token: ServiceAccountTokenType +} + type InitEnvSync { app: AppType } From 92698a37dbd19efc58e065f79d503994aebe2710 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:04:15 +0530 Subject: [PATCH 13/92] feat: add service account management page, misc changes and cleanup --- .../service-accounts/[account]/page.tsx | 165 +++++++++ .../CreateServiceAccountDialog.tsx | 15 +- .../CreateServiceAccountTokenDialog.tsx | 322 ++++++++++++++++++ .../DeleteServiceAccountDialog.tsx | 6 + .../[team]/access/service-accounts/page.tsx | 20 +- .../apps/tokens/CreateUserTokenDialog.tsx | 11 +- frontend/utils/crypto/service-accounts.ts | 38 +++ frontend/utils/tokens.ts | 5 + 8 files changed, 564 insertions(+), 18 deletions(-) create mode 100644 frontend/app/[team]/access/service-accounts/[account]/page.tsx create mode 100644 frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountTokenDialog.tsx create mode 100644 frontend/utils/crypto/service-accounts.ts diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx new file mode 100644 index 00000000..4ae7f355 --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -0,0 +1,165 @@ +'use client' + +import Spinner from '@/components/common/Spinner' +import { RoleLabel } from '@/components/users/RoleLabel' +import { organisationContext } from '@/contexts/organisationContext' +import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' +import { userHasPermission } from '@/utils/access/permissions' +import { relativeTimeFromDates } from '@/utils/time' +import { useQuery } from '@apollo/client' +import Link from 'next/link' +import { useContext } from 'react' +import { FaBan, FaChevronLeft, FaRobot } from 'react-icons/fa' +import { CreateServiceAccountTokenDialog } from '../_components/CreateServiceAccountTokenDialog' +import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog' +import { ServiceAccountTokenType } from '@/apollo/graphql' +import { Avatar } from '@/components/common/Avatar' +import { EmptyState } from '@/components/common/EmptyState' +import { humanReadableExpiry } from '@/utils/tokens' + +export default function ServiceAccount({ params }: { params: { team: string; account: string } }) { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const userCanReadSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'read') + : false + + const userCanCreateSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'create') + : false + + const userCanDeleteSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'delete') + : false + + const { data, loading } = useQuery(GetServiceAccounts, { + variables: { orgId: organisation?.id, id: params.account }, + skip: !organisation || !userCanReadSA, + }) + + const account = data?.serviceAccounts[0] + + if (!userCanReadSA) + return ( +
+ + + + } + > + <> + +
+ ) + + if (loading) + return ( +
+ +
+ ) + + if (!account) + return ( +
+ + + + } + > + <> + +
+ ) + + return ( +
+
+ + Back to service accouts + +
+
+
+
+ +
{' '} +
+

{account?.name}

+
+ +
+
+
+ + {/*
Created {relativeTimeFromDates(new Date(account.createdAt))}
*/} + +
+
+
Tokens
+
Manage tokens for this service account
+
+
+ +
+ +
+ {account.tokens?.map((token: ServiceAccountTokenType) => ( +
+
+ {token!.name} +
+ +
+ Created {relativeTimeFromDates(new Date(token?.createdAt))} by{' '} + + + {token?.createdBy?.fullName} + +
+ + {/*
{new Date(token.expiresAt).toDateString()}
*/} + +
+ Expires{' '} + {token.expiresAt ? relativeTimeFromDates(new Date(token?.expiresAt)) : 'never'} +
+
+ ))} +
+
+ +
+
+
Danger Zone
+
+ These actions are destructive and cannot be reversed +
+
+ {userCanDeleteSA && ( +
+
+
Delete account
+
+ Permanently delete this service account and all associated tokens +
+
+ +
+ )} +
+
+
+ ) +} diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index f6b30824..3ffab343 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -12,11 +12,8 @@ import { useMutation, useQuery } from '@apollo/client' import { organisationSeed, organisationKeyring, - deviceVaultKey, - encryptAccountKeyring, - encryptAccountRecovery, - OrganisationKeyring, getUserKxPublicKey, + encryptAsymmetric, } from '@/utils/crypto' import { Input } from '@/components/common/Input' import { RoleLabel } from '@/components/users/RoleLabel' @@ -70,9 +67,9 @@ export const CreateServiceAccountDialog = () => { if (thirdParty) { const serverKey = serverKeyData.serverPublicKey - const serverEncryptedKeyring = await encryptAccountKeyring(keyring, serverKey) + const serverEncryptedKeyring = await encryptAsymmetric(JSON.stringify(keyring), serverKey) - const serverEncryptedMnemonic = await encryptAccountRecovery(mnemonic, serverKey) + const serverEncryptedMnemonic = await encryptAsymmetric(mnemonic, serverKey) serverKeys = { serverEncryptedKeyring, @@ -85,8 +82,8 @@ export const CreateServiceAccountDialog = () => { const handlerWrappingPromises = handlers.map(async (handler) => { const kxKey = await getUserKxPublicKey(handler.identityKey!) - const wrappedKeyring = await encryptAccountKeyring(keyring, kxKey) - const wrappedRecovery = await encryptAccountRecovery(mnemonic, kxKey) + const wrappedKeyring = await encryptAsymmetric(JSON.stringify(keyring), kxKey) + const wrappedRecovery = await encryptAsymmetric(mnemonic, kxKey) return { memberId: handler.id, wrappedKeyring, @@ -153,7 +150,7 @@ export const CreateServiceAccountDialog = () => { 'py-2 flex items-center justify-between w-full rounded-md h-10' )} > - {role ? : <>} + {role ? : <>Select a role} { + const { activeOrganisation: organisation } = useContext(organisationContext) + const { keyring } = useContext(KeyringContext) + + const serviceAccountHandler = serviceAccount.handlers?.find( + (handler) => handler?.user.self === true + ) + + const dialogRef = useRef<{ closeModal: () => void }>(null) + + const [name, setName] = useState('') + const [expiry, setExpiry] = useState(tokenExpiryOptions[0]) + + const [cliSAToken, setCliSAToken] = useState('') + const [apiSAToken, setApiSAToken] = useState('') + const [createPending, setCreatePending] = useState(false) + + const [createToken] = useMutation(CreateSAToken) + + const reset = () => { + setName('') + setExpiry(tokenExpiryOptions[0]) + setCliSAToken('') + setApiSAToken('') + } + + const closeModal = () => { + if (dialogRef.current) dialogRef.current.closeModal() + } + + const handleCreateNewSAToken = async (event: { preventDefault: () => void }) => { + return new Promise(async (resolve, reject) => { + event.preventDefault() + + if (name.length === 0) { + toast.error('You must enter a name for the token') + reject() + } + + if (serviceAccountHandler && keyring) { + setCreatePending(true) + const wrappedKeyring = serviceAccountHandler.wrappedKeyring + + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring.publicKey), + privateKey: await getUserKxPrivateKey(keyring.privateKey), + } + + const serviceAccountKeyringString = await decryptAsymmetric( + wrappedKeyring, + userKxKeys.privateKey, + userKxKeys.publicKey + ) + + const serviceAccountKeys = JSON.parse(serviceAccountKeyringString) as OrganisationKeyring + + const saKxKeys = { + publicKey: await getUserKxPublicKey(serviceAccountKeys.publicKey), + privateKey: await getUserKxPrivateKey(serviceAccountKeys.privateKey), + } + + const { pssService, mutationPayload } = await generateSAToken( + serviceAccount.id, + saKxKeys, + name, + expiry.getExpiry() + ) + + await createToken({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetServiceAccounts, + variables: { + orgId: organisation!.id, + }, + }, + ], + }) + + setCliSAToken(pssService) + setApiSAToken(`ServiceAccount ${mutationPayload.token}`) + setCreatePending(false) + toast.success('Created new service account token!') + resolve(true) + } else { + console.log('keyring unavailable') + reject() + } + }) + } + + return ( + + {' '} + Create token + + } + buttonVariant="primary" + ref={dialogRef} + onClose={reset} + > +
+
Create a new token for this service account
+ + {cliSAToken ? ( +
+
+
+
{name}
+
{humanReadableExpiry(expiry)}
+
+ This token has access to all apps and environments accessible by you. +
+
+
+ + + Copy this token. You won't see it again! + + + + + + {({ selected }) => ( +
+ CLI / SDK / Kubernetes +
+ )} +
+ + {({ selected }) => ( +
+ API +
+ )} +
+
+ + +
+
+
+ + user token + +
+ {cliSAToken && ( +
+ +
+ )} +
+
+ + {cliSAToken} + +
+
+
+ +
+ +
+ You will need to enable server-side encryption (SSE) for any Apps that you + want to manage secrets with via the Public API. + +
+ Docs +
+ +
+
+ +
+
+ + API token + +
+ {apiSAToken && ( +
+ +
+ )} +
+
+ + {apiSAToken} + +
+ +
+
+
+ Example with curl +
+ + + +
+ +
+
+
+
+
+
+ ) : ( +
+
+ + setName(e.target.value)} /> +
+ +
+ + + + +
+ {tokenExpiryOptions.map((option) => ( + + {({ checked }) => ( +
+ +
+ )} +
+ ))} +
+
+ {humanReadableExpiry(expiry)} +
+ +
+ + +
+
+ )} +
+
+ ) +} diff --git a/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx index 34362eac..5e59b8d4 100644 --- a/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx @@ -8,6 +8,7 @@ import { organisationContext } from '@/contexts/organisationContext' import { ServiceAccountType } from '@/apollo/graphql' import { Button } from '@/components/common/Button' import GenericDialog from '@/components/common/GenericDialog' +import { useRouter } from 'next/navigation' export const DeleteServiceAccountDialog = ({ account }: { account: ServiceAccountType }) => { const { activeOrganisation: organisation } = useContext(organisationContext) @@ -27,6 +28,10 @@ export const DeleteServiceAccountDialog = ({ account }: { account: ServiceAccoun } } + const router = useRouter() + + const handleRedirect = () => router.push(`/${organisation?.name}/access/service-accounts`) + return (
diff --git a/frontend/app/[team]/access/service-accounts/page.tsx b/frontend/app/[team]/access/service-accounts/page.tsx index cc75de44..98578a24 100644 --- a/frontend/app/[team]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/access/service-accounts/page.tsx @@ -8,11 +8,14 @@ import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServic import { userHasPermission } from '@/utils/access/permissions' import { useQuery } from '@apollo/client' import { useContext } from 'react' -import { FaBan } from 'react-icons/fa' +import { FaBan, FaChevronRight } from 'react-icons/fa' import { CreateServiceAccountDialog } from './_components/CreateServiceAccountDialog' import { FaRobot } from 'react-icons/fa6' import { relativeTimeFromDates } from '@/utils/time' import { DeleteServiceAccountDialog } from './_components/DeleteServiceAccountDialog' +import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog' +import Link from 'next/link' +import { Button } from '@/components/common/Button' export default function ServiceAccounts({ params }: { params: { team: string } }) { const { activeOrganisation: organisation } = useContext(organisationContext) @@ -36,7 +39,7 @@ export default function ServiceAccounts({ params }: { params: { team: string } } return (
-
+

{params.team} Service Accounts

Manage service accounts.

@@ -82,8 +85,17 @@ export default function ServiceAccounts({ params }: { params: { team: string } } {relativeTimeFromDates(new Date(account.createdAt))} - - {userCanDeleteSA && } + {/* + + {userCanDeleteSA && } + */} + + + + + ))} diff --git a/frontend/components/apps/tokens/CreateUserTokenDialog.tsx b/frontend/components/apps/tokens/CreateUserTokenDialog.tsx index 664c7e8a..36f45b70 100644 --- a/frontend/components/apps/tokens/CreateUserTokenDialog.tsx +++ b/frontend/components/apps/tokens/CreateUserTokenDialog.tsx @@ -3,7 +3,12 @@ import { Button } from '@/components/common/Button' import { KeyringContext } from '@/contexts/keyringContext' -import { ExpiryOptionT, humanReadableExpiry, tokenExpiryOptions } from '@/utils/tokens' +import { + compareExpiryOptions, + ExpiryOptionT, + humanReadableExpiry, + tokenExpiryOptions, +} from '@/utils/tokens' import { useMutation } from '@apollo/client' import { Dialog, RadioGroup, Tab, Transition } from '@headlessui/react' import clsx from 'clsx' @@ -19,10 +24,6 @@ import Link from 'next/link' import { getApiHost } from '@/utils/appConfig' import { getUserKxPublicKey, getUserKxPrivateKey, generateUserToken } from '@/utils/crypto' -const compareExpiryOptions = (a: ExpiryOptionT, b: ExpiryOptionT) => { - return a.getExpiry() === b.getExpiry() -} - export const CreateUserTokenDialog = (props: { organisationId: string }) => { const { organisationId } = props diff --git a/frontend/utils/crypto/service-accounts.ts b/frontend/utils/crypto/service-accounts.ts new file mode 100644 index 00000000..997ef55f --- /dev/null +++ b/frontend/utils/crypto/service-accounts.ts @@ -0,0 +1,38 @@ +import { newEnvWrapKey, newEnvToken } from "./environments"; +import { getWrappedKeyShare } from "./general"; +import { splitSecret } from "./keyshares"; + +/** + * Generates a service account token. + * + * @param {string} serviceAccountId - The Service Account ID. + * @param {{ publicKey: string; privateKey: string }} saKeyring - The service account keyring. + * @returns {Promise<{ pssService: string; mutationPayload: object }>} - An object containing the user token and mutation payload. + */ +export const generateSAToken = async ( + serviceAccountId: string, + saKeyring: { publicKey: string; privateKey: string }, + name: string, + expiry: number | null +) => { + const wrapKey = await newEnvWrapKey() + const token = await newEnvToken() + + const keyShares = await splitSecret(saKeyring.privateKey) + const wrappedKeyShare = await getWrappedKeyShare(keyShares[1], wrapKey) + + const pssService = `pss_service:v2:${token}:${saKeyring.publicKey}:${keyShares[0]}:${wrapKey}` + const mutationPayload = { + serviceAccountId, + name, + identityKey: saKeyring.publicKey, + token, + wrappedKeyShare, + expiry, + } + + return { + pssService, + mutationPayload, + } +} diff --git a/frontend/utils/tokens.ts b/frontend/utils/tokens.ts index d77a5842..ddd49360 100644 --- a/frontend/utils/tokens.ts +++ b/frontend/utils/tokens.ts @@ -32,3 +32,8 @@ export const humanReadableExpiry = (expiryOption: ExpiryOptionT) => expiryOption.getExpiry() === null ? 'This token will never expire.' : `This token will expire on ${new Date(expiryOption.getExpiry()!).toLocaleDateString()}.` + + +export const compareExpiryOptions = (a: ExpiryOptionT, b: ExpiryOptionT) => { + return a.getExpiry() === b.getExpiry() +} \ No newline at end of file From e0b980216333cef1ac14286649fb65caf8a35ead Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 23 Oct 2024 14:17:19 +0530 Subject: [PATCH 14/92] feat: delete service account tokens --- .../graphene/mutations/service_accounts.py | 23 ++++++++ backend/backend/schema.py | 2 + frontend/apollo/schema.graphql | 5 ++ .../CreateServiceAccountTokenDialog.tsx | 0 .../DeleteServiceAccountTokenDialog.tsx | 52 +++++++++++++++++++ .../service-accounts/[account]/page.tsx | 23 ++++---- .../[team]/access/service-accounts/page.tsx | 7 +-- .../deleteServiceAccountToken.gql | 5 ++ 8 files changed, 101 insertions(+), 16 deletions(-) rename frontend/app/[team]/access/service-accounts/{ => [account]}/_components/CreateServiceAccountTokenDialog.tsx (100%) create mode 100644 frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx create mode 100644 frontend/graphql/mutations/service-accounts/deleteServiceAccountToken.gql diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 97c34a9a..dfa5a3dc 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -219,3 +219,26 @@ def mutate( ) return CreateServiceAccountTokenMutation(token=token) + + +class DeleteServiceAccountTokenMutation(graphene.Mutation): + class Arguments: + token_id = graphene.ID() + + ok = graphene.Boolean() + + @classmethod + def mutate(cls, root, info, token_id): + user = info.context.user + token = ServiceAccountToken.objects.get(id=token_id) + + if not user_has_permission( + user, "update", "ServiceAccounts", token.service_account.organisation + ): + raise GraphQLError( + "You don't have the permissions required to delete Service Tokens in this organisation" + ) + + token.delete() + + return DeleteServiceAccountTokenMutation(ok=True) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 8e314eff..33f7ca11 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -7,6 +7,7 @@ CreateServiceAccountMutation, CreateServiceAccountTokenMutation, DeleteServiceAccountMutation, + DeleteServiceAccountTokenMutation, EnableServiceAccountThirdPartyAuthMutation, UpdateServiceAccountHandlersMutation, ) @@ -777,6 +778,7 @@ class Mutation(graphene.ObjectType): update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field() delete_service_account = DeleteServiceAccountMutation.Field() create_service_account_token = CreateServiceAccountTokenMutation.Field() + delete_service_account_token = DeleteServiceAccountTokenMutation.Field() init_env_sync = InitEnvSync.Field() delete_env_sync = DeleteSync.Field() diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index bc40bdbe..e18b4b93 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -654,6 +654,7 @@ type Mutation { updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], serviceAccountId: ID): UpdateServiceAccountHandlersMutation deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation createServiceAccountToken(expiry: BigInt, identityKey: String!, name: String!, serviceAccountId: ID, token: String!, wrappedKeyShare: String!): CreateServiceAccountTokenMutation + deleteServiceAccountToken(tokenId: ID): DeleteServiceAccountTokenMutation initEnvSync(appId: ID, envKeys: [EnvironmentKeyInput]): InitEnvSync deleteEnvSync(syncId: ID): DeleteSync triggerSync(syncId: ID): TriggerSync @@ -820,6 +821,10 @@ type CreateServiceAccountTokenMutation { token: ServiceAccountTokenType } +type DeleteServiceAccountTokenMutation { + ok: Boolean +} + type InitEnvSync { app: AppType } diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountTokenDialog.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx similarity index 100% rename from frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountTokenDialog.tsx rename to frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx new file mode 100644 index 00000000..2f9f6906 --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx @@ -0,0 +1,52 @@ +import { FaTrash } from 'react-icons/fa' +import { DeleteServiceAccountToken } from '@/graphql/mutations/service-accounts/deleteServiceAccountToken.gql' +import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' +import { useMutation } from '@apollo/client' +import { toast } from 'react-toastify' +import { useContext, useRef } from 'react' +import { organisationContext } from '@/contexts/organisationContext' +import { ServiceAccountTokenType, ServiceAccountType } from '@/apollo/graphql' +import { Button } from '@/components/common/Button' +import GenericDialog from '@/components/common/GenericDialog' +import { useRouter } from 'next/navigation' + +export const DeleteServiceAccountTokenDialog = ({ token }: { token: ServiceAccountTokenType }) => { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const dialogRef = useRef<{ closeModal: () => void }>(null) + + const [deleteToken] = useMutation(DeleteServiceAccountToken) + + const handleDelete = async () => { + const deleted = await deleteToken({ + variables: { id: token.id }, + refetchQueries: [{ query: GetServiceAccounts, variables: { orgId: organisation!.id } }], + }) + if (deleted.data.deleteServiceAccountToken.ok) { + toast.success('Deleted token!') + if (dialogRef.current) dialogRef.current.closeModal() + } + } + + return ( + + Delete + + } + buttonVariant="danger" + ref={dialogRef} + > +
+
Are you sure you want to delete this token?
+
+ +
+
+
+ ) +} diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 4ae7f355..55f00b2c 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -9,13 +9,14 @@ import { relativeTimeFromDates } from '@/utils/time' import { useQuery } from '@apollo/client' import Link from 'next/link' import { useContext } from 'react' -import { FaBan, FaChevronLeft, FaRobot } from 'react-icons/fa' -import { CreateServiceAccountTokenDialog } from '../_components/CreateServiceAccountTokenDialog' +import { FaBan, FaChevronLeft, FaKey, FaRobot } from 'react-icons/fa' +import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog' import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog' import { ServiceAccountTokenType } from '@/apollo/graphql' import { Avatar } from '@/components/common/Avatar' import { EmptyState } from '@/components/common/EmptyState' import { humanReadableExpiry } from '@/utils/tokens' +import { DeleteServiceAccountTokenDialog } from './_components/DeleteServiceAccountTokenDialog' export default function ServiceAccount({ params }: { params: { team: string; account: string } }) { const { activeOrganisation: organisation } = useContext(organisationContext) @@ -114,14 +115,14 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
-
+
{account.tokens?.map((token: ServiceAccountTokenType) => ( -
-
- {token!.name} +
+
+ {token!.name}
-
+
Created {relativeTimeFromDates(new Date(token?.createdAt))} by{' '} @@ -129,12 +130,14 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
- {/*
{new Date(token.expiresAt).toDateString()}
*/} - -
+
Expires{' '} {token.expiresAt ? relativeTimeFromDates(new Date(token?.expiresAt)) : 'never'}
+ +
+ +
))}
diff --git a/frontend/app/[team]/access/service-accounts/page.tsx b/frontend/app/[team]/access/service-accounts/page.tsx index 98578a24..695191e6 100644 --- a/frontend/app/[team]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/access/service-accounts/page.tsx @@ -13,7 +13,7 @@ import { CreateServiceAccountDialog } from './_components/CreateServiceAccountDi import { FaRobot } from 'react-icons/fa6' import { relativeTimeFromDates } from '@/utils/time' import { DeleteServiceAccountDialog } from './_components/DeleteServiceAccountDialog' -import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog' +import { CreateServiceAccountTokenDialog } from './[account]/_components/CreateServiceAccountTokenDialog' import Link from 'next/link' import { Button } from '@/components/common/Button' @@ -85,11 +85,6 @@ export default function ServiceAccounts({ params }: { params: { team: string } } {relativeTimeFromDates(new Date(account.createdAt))} - {/* - - {userCanDeleteSA && } - */} -
- {/*
Created {relativeTimeFromDates(new Date(account.createdAt))}
*/} -
Tokens
diff --git a/frontend/app/[team]/apps/[app]/access/layout.tsx b/frontend/app/[team]/apps/[app]/access/layout.tsx new file mode 100644 index 00000000..d268e1b5 --- /dev/null +++ b/frontend/app/[team]/apps/[app]/access/layout.tsx @@ -0,0 +1,94 @@ +'use client' + +import { Fragment, useContext, useEffect, useMemo, useState } from 'react' +import { Tab } from '@headlessui/react' +import clsx from 'clsx' +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { organisationContext } from '@/contexts/organisationContext' + +export default function AccessLayout({ + params, + children, +}: { + params: { team: string; app: string } + children: React.ReactNode +}) { + const path = usePathname() + //const router = useRouter() + + const { activeOrganisation } = useContext(organisationContext) + + const [tabIndex, setTabIndex] = useState(0) + + const tabs = useMemo( + () => [ + { + name: 'Members', + link: 'members', + }, + { + name: 'Service Accounts', + link: 'service-accounts', + }, + { + name: 'Service Tokens', + link: 'tokens', + isLegacy: true, + }, + ], + [] + ) + + useEffect(() => { + const activeTabIndex = () => { + const currentUrl = path?.split('/')[5] || '' + const index = tabs.findIndex((tab) => tab.link === currentUrl) + return index >= 0 ? index : 0 + } + + setTabIndex(activeTabIndex()) + }, [path, tabs]) + + return ( +
+
+

Access

+
Manage user and service account access in this App
+
+ + setTabIndex(index)}> +
+ + {tabs.map((tab) => ( + + {({ selected }) => ( + + {tab.name}{' '} + {tab.isLegacy && ( + + Legacy + + )} + + )} + + ))} + +
{children}
+
+
+
+ ) +} diff --git a/frontend/app/[team]/apps/[app]/members/page.tsx b/frontend/app/[team]/apps/[app]/access/members/page.tsx similarity index 91% rename from frontend/app/[team]/apps/[app]/members/page.tsx rename to frontend/app/[team]/apps/[app]/access/members/page.tsx index c6c773ec..18c4bcdc 100644 --- a/frontend/app/[team]/apps/[app]/members/page.tsx +++ b/frontend/app/[team]/apps/[app]/access/members/page.tsx @@ -8,7 +8,7 @@ import GetAppMembers from '@/graphql/queries/apps/getAppMembers.gql' import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' import { GetEnvironmentKey } from '@/graphql/queries/secrets/getEnvironmentKey.gql' import { useLazyQuery, useMutation, useQuery } from '@apollo/client' -import { Fragment, useContext, useEffect, useState } from 'react' +import { Fragment, useContext, useEffect, useMemo, useState } from 'react' import { OrganisationMemberType, EnvironmentType } from '@/apollo/graphql' import { Button } from '@/components/common/Button' import { organisationContext } from '@/contexts/organisationContext' @@ -18,6 +18,7 @@ import { FaBan, FaCheckSquare, FaChevronDown, + FaCog, FaPlus, FaSquare, FaTimes, @@ -33,9 +34,10 @@ import { userHasGlobalAccess, userHasPermission, userIsAdmin } from '@/utils/acc import { RoleLabel } from '@/components/users/RoleLabel' import { Alert } from '@/components/common/Alert' import Link from 'next/link' -import { unwrapEnvSecretsForUser, wrapEnvSecretsForUser } from '@/utils/crypto' +import { unwrapEnvSecretsForUser, wrapEnvSecretsForAccount } from '@/utils/crypto' import { EmptyState } from '@/components/common/EmptyState' import Spinner from '@/components/common/Spinner' +import loading from '@/app/loading' export default function Members({ params }: { params: { team: string; app: string } }) { const { keyring } = useContext(KeyringContext) @@ -162,7 +164,7 @@ export default function Members({ params }: { params: { team: string; app: strin keyring! ) - const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForUser( + const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForAccount( { seed, salt }, selectedMember! ) @@ -271,7 +273,7 @@ export default function Members({ params }: { params: { team: string; app: strin onChange={(event) => setQuery(event.target.value)} required displayValue={(person: OrganisationMemberType) => - person?.fullName || person?.email! + person ? person?.fullName || person?.email! : 'Select a user' } />
@@ -460,9 +462,7 @@ export default function Members({ params }: { params: { team: string; app: strin <>
@@ -526,16 +526,33 @@ export default function Members({ params }: { params: { team: string; app: strin ) } - const ManageUserAccessDialog = (props: { member: OrganisationMemberType }) => { + const ManageUserAccessDialog = ({ member }: { member: OrganisationMemberType }) => { const [updateScope] = useMutation(UpdateEnvScope) - const [getUserEnvScope] = useLazyQuery(GetAppEnvironments) + // Get environments that the active user has access to const { data: appEnvsData } = useQuery(GetAppEnvironments, { variables: { appId: params.app, }, }) + // Get the environemnts that the member has access to + const { data: userEnvScopeData } = useQuery(GetAppEnvironments, { + variables: { + appId: params.app, + memberId: member.id, + }, + }) + + const envScope: Array> = useMemo(() => { + return ( + userEnvScopeData?.appEnvironments.map((env: EnvironmentType) => ({ + id: env.id, + name: env.name, + })) ?? [] + ) + }, [userEnvScopeData]) + const envOptions = appEnvsData?.appEnvironments.map((env: EnvironmentType) => { const { id, name } = env @@ -548,7 +565,7 @@ export default function Members({ params }: { params: { team: string; app: strin const [isOpen, setIsOpen] = useState(false) - const [envScope, setEnvScope] = useState>>([]) + const [scope, setScope] = useState>>([]) const [showEnvHint, setShowEnvHint] = useState(false) const memberHasGlobalAccess = (user: OrganisationMemberType) => @@ -563,36 +580,13 @@ export default function Members({ params }: { params: { team: string; app: strin } useEffect(() => { - if (isOpen) { - const handleGetCurrentSCope = async () => { - const { data: currentScope } = await getUserEnvScope({ - variables: { - appId: params.app, - memberId: props.member.id, - }, - fetchPolicy: 'no-cache', - }) - - setEnvScope( - currentScope?.appEnvironments.map((env: EnvironmentType) => { - const { id, name } = env - - return { - id, - name, - } - }) ?? [] - ) - } - - if (isOpen) handleGetCurrentSCope() - } - }, [getUserEnvScope, isOpen, props.member.id]) + setScope(envScope) + }, [envScope]) const handleUpdateScope = async (e: { preventDefault: () => void }) => { e.preventDefault() - if (envScope.length === 0) { + if (scope.length === 0) { setShowEnvHint(true) return false } @@ -600,7 +594,7 @@ export default function Members({ params }: { params: { team: string; app: strin const appEnvironments = appEnvsData.appEnvironments as EnvironmentType[] const envKeyPromises = appEnvironments - .filter((env) => envScope.map((selectedEnv) => selectedEnv.id).includes(env.id)) + .filter((env) => scope.map((selectedEnv) => selectedEnv.id).includes(env.id)) .map(async (env: EnvironmentType) => { const { data } = await getEnvKey({ variables: { @@ -621,14 +615,14 @@ export default function Members({ params }: { params: { team: string; app: strin keyring! ) - const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForUser( + const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForAccount( { seed, salt }, - props.member! + member! ) return { envId: env.id, - userId: props.member!.id, + userId: member!.id, identityKey, wrappedSeed, wrappedSalt, @@ -638,11 +632,14 @@ export default function Members({ params }: { params: { team: string; app: strin const envKeyInputs = await Promise.all(envKeyPromises) await updateScope({ - variables: { memberId: props.member!.id, appId: params.app, envKeys: envKeyInputs }, + variables: { memberId: member!.id, appId: params.app, envKeys: envKeyInputs }, refetchQueries: [ { - query: GetAppMembers, - variables: { appId: params.app }, + query: GetAppEnvironments, + variables: { + appId: params.app, + memberId: member.id, + }, }, ], }) @@ -650,12 +647,26 @@ export default function Members({ params }: { params: { team: string; app: strin toast.success('Updated user access', { autoClose: 2000 }) } + const allowUpdateScope = + member.email !== session?.user?.email && + member.role!.name!.toLowerCase() !== 'owner' && + userCanUpdateMemberAccess + return ( <> -
- +
+ {envScope.map((env) => ( + + {env.name} + + ))} + {allowUpdateScope && ( +
+ +
+ )}
@@ -686,7 +697,7 @@ export default function Members({ params }: { params: { team: string; app: strin

- Manage access for {props.member.fullName || props.member.email} + Manage access for {member.fullName || member.email}

@@ -827,7 +838,11 @@ export default function Members({ params }: { params: { team: string; app: strin ) return ( -
+
+
+

Members

+
Manage access for human users to this App
+
{userCanReadAppMembers ? (
{userCanAddAppMembers && ( @@ -844,14 +859,12 @@ export default function Members({ params }: { params: { team: string; app: strin - Joined + Environment Access - {(userCanRemoveAppMembers || userCanUpdateMemberAccess) && ( - - )} + {userCanRemoveAppMembers && } - + {data?.appUsers.map((member: OrganisationMemberType) => ( @@ -869,20 +882,16 @@ export default function Members({ params }: { params: { team: string; app: strin
- - {relativeTimeFromDates(new Date(member.createdAt))} + + - {(userCanRemoveAppMembers || userCanUpdateMemberAccess) && ( + + {userCanRemoveAppMembers && ( {member.email !== session?.user?.email && member.role!.name!.toLowerCase() !== 'owner' && (
- {userCanUpdateMemberAccess && ( - - )} - {userCanRemoveAppMembers && ( - - )} +
)} diff --git a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx new file mode 100644 index 00000000..86fc7481 --- /dev/null +++ b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx @@ -0,0 +1,934 @@ +'use client' + +import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' + +import AddMemberToApp from '@/graphql/mutations/apps/addAppMember.gql' +import RemoveMemberFromApp from '@/graphql/mutations/apps/removeAppMember.gql' +import UpdateEnvScope from '@/graphql/mutations/apps/updateEnvScope.gql' + +import { GetAppServiceAccounts } from '@/graphql/queries/apps/getAppServiceAccounts.gql' +import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' +import { GetEnvironmentKey } from '@/graphql/queries/secrets/getEnvironmentKey.gql' +import { useLazyQuery, useMutation, useQuery } from '@apollo/client' +import { Fragment, useContext, useEffect, useMemo, useState } from 'react' +import { + EnvironmentType, + ServiceAccountType, + MemberType, + OrganisationMemberType, +} from '@/apollo/graphql' +import { Button } from '@/components/common/Button' +import { organisationContext } from '@/contexts/organisationContext' +import { relativeTimeFromDates } from '@/utils/time' +import { Combobox, Dialog, Listbox, Transition } from '@headlessui/react' +import { + FaBan, + FaCheckSquare, + FaChevronDown, + FaCog, + FaPlus, + FaRobot, + FaSquare, + FaTimes, + FaTrash, + FaUserCog, + FaUserTimes, +} from 'react-icons/fa' +import clsx from 'clsx' +import { toast } from 'react-toastify' +import { useSession } from 'next-auth/react' +import { Avatar } from '@/components/common/Avatar' +import { KeyringContext } from '@/contexts/keyringContext' +import { userHasGlobalAccess, userHasPermission, userIsAdmin } from '@/utils/access/permissions' +import { RoleLabel } from '@/components/users/RoleLabel' +import { Alert } from '@/components/common/Alert' +import Link from 'next/link' +import { unwrapEnvSecretsForUser, wrapEnvSecretsForAccount } from '@/utils/crypto' +import { EmptyState } from '@/components/common/EmptyState' +import Spinner from '@/components/common/Spinner' +import loading from '@/app/loading' + +export default function ServiceAccounts({ params }: { params: { team: string; app: string } }) { + const { keyring } = useContext(KeyringContext) + const { activeOrganisation: organisation } = useContext(organisationContext) + + // Permissions + const userCanReadAppSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'read', true) + : false + const userCanReadEnvironments = organisation + ? userHasPermission(organisation?.role?.permissions, 'Environments', 'read', true) + : false + + // AppServiceAccounts:create + ServiceAccounts: read + const userCanAddAppSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'create', true) && + userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'read') + : false + const userCanRemoveAppSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'delete', true) + : false + // AppMembers:update + Environments:read + const userCanUpdateSAAccess = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'update', true) && + userHasPermission(organisation?.role?.permissions, 'Environments', 'read', true) + : false + + const { data, loading } = useQuery(GetAppServiceAccounts, { + variables: { appId: params.app }, + skip: !userCanReadAppSA, + }) + + const [getEnvKey] = useLazyQuery(GetEnvironmentKey) + + const { data: session } = useSession() + + const AddAccountDialog = () => { + const { data: serviceAccountsData } = useQuery(GetServiceAccounts, { + variables: { + orgId: organisation?.id, + }, + skip: !organisation || !userCanAddAppSA, + }) + + const accountOptions = + serviceAccountsData?.serviceAccounts.filter( + (account: ServiceAccountType) => + !data?.appServiceAccounts + .map((account: ServiceAccountType) => account.id) + .includes(account.id) + ) ?? [] + + const [addMember] = useMutation(AddMemberToApp) + + const { data: appEnvsData } = useQuery(GetAppEnvironments, { + variables: { + appId: params.app, + }, + skip: !userCanReadEnvironments, + }) + + const envOptions = + appEnvsData?.appEnvironments.map((env: EnvironmentType) => { + const { id, name } = env + + return { + id, + name, + } + }) ?? [] + + const [isOpen, setIsOpen] = useState(false) + const [selectedAccount, setSelectedAccount] = useState(null) + const [query, setQuery] = useState('') + const [envScope, setEnvScope] = useState>>([]) + const [showEnvHint, setShowEnvHint] = useState(false) + + const filteredAccounts = + query === '' + ? accountOptions + : accountOptions.filter((account: ServiceAccountType) => { + return account.name.toLowerCase().includes(query.toLowerCase()) + }) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const handleAddMember = async (e: { preventDefault: () => void }) => { + e.preventDefault() + + if (envScope.length === 0) { + setShowEnvHint(true) + return false + } + + const appEnvironments = appEnvsData.appEnvironments as EnvironmentType[] + + const envKeyPromises = appEnvironments + .filter((env) => envScope.map((selectedEnv) => selectedEnv.id).includes(env.id)) + .map(async (env: EnvironmentType) => { + const { data } = await getEnvKey({ + variables: { + envId: env.id, + appId: params.app, + }, + }) + + const { + wrappedSeed: userWrappedSeed, + wrappedSalt: userWrappedSalt, + identityKey, + } = data.environmentKeys[0] + + const { seed, salt } = await unwrapEnvSecretsForUser( + userWrappedSeed, + userWrappedSalt, + keyring! + ) + + console.log('unwrapped env secrets', seed, salt) + + const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForAccount( + { seed, salt }, + selectedAccount! + ) + + return { + envId: env.id, + userId: selectedAccount!.id, + identityKey, + wrappedSeed, + wrappedSalt, + } + }) + + const envKeyInputs = await Promise.all(envKeyPromises) + + await addMember({ + variables: { + memberId: selectedAccount!.id, + memberType: MemberType.Service, + appId: params.app, + envKeys: envKeyInputs, + }, + refetchQueries: [ + { + query: GetAppServiceAccounts, + variables: { appId: params.app }, + }, + ], + }) + + toast.success('Added account to App', { autoClose: 2000 }) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Add service account +

+ + +
+ + {accountOptions.length === 0 ? ( +
+ +

+ All organisation members are added to this App. You can invite more + users from the{' '} + + organisation members + {' '} + page. +

+
+
+ ) : ( + + + {({ open }) => ( + <> +
+ + + +
+ setQuery(event.target.value)} + required + displayValue={(account: ServiceAccountType) => + account ? account.name : 'Select an account' + } + /> +
+ + + +
+
+
+ + +
+ {filteredAccounts.map((account: ServiceAccountType) => ( + + {({ active, selected }) => ( +
+
+ +
+ + {account.name} + +
+ )} +
+ ))} +
+
+
+ + )} +
+ + {userCanReadEnvironments ? ( +
+ {envScope.length === 0 && showEnvHint && ( + + Select an environment scope + + )} + + {({ open }) => ( + <> + + + + +
+ + {envScope + .map((env: Partial) => env.name) + .join(' + ')} + + +
+
+ + +
+ {envOptions.map((env: Partial) => ( + + {({ active, selected }) => ( +
+ {selected ? ( + + ) : ( + + )} + + {env.name} + +
+ )} +
+ ))} +
+
+
+ + )} +
+
+ ) : ( + + You don't have permission to read Environments. This permission is + required to set an environment scope for users in this App. + + )} + +
+ + +
+ + )} +
+
+
+
+
+
+ + ) + } + + const RemoveAccountConfirmDialog = (props: { account: ServiceAccountType }) => { + const { account } = props + + const [removeMember] = useMutation(RemoveMemberFromApp) + + const [isOpen, setIsOpen] = useState(false) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const handleRemoveMember = async () => { + await removeMember({ + variables: { memberId: account.id, memberType: MemberType.Service, appId: params.app }, + refetchQueries: [ + { + query: GetAppServiceAccounts, + variables: { appId: params.app }, + }, + ], + }) + toast.success('Removed member from app', { autoClose: 2000 }) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Remove member +

+ + +
+ +
+

+ Are you sure you want to remove {account.name} from this app? +

+
+ + +
+
+
+
+
+
+
+
+ + ) + } + + const ManageAccountAccessDialog = ({ account }: { account: ServiceAccountType }) => { + const [updateScope] = useMutation(UpdateEnvScope) + + // Get environments that the active user has access to + const { data: appEnvsData } = useQuery(GetAppEnvironments, { + variables: { + appId: params.app, + }, + }) + + // Get the environemnts that the account has access to + const { data: userEnvScopeData } = useQuery(GetAppEnvironments, { + variables: { + appId: params.app, + memberId: account.id, + memberType: MemberType.Service, + }, + }) + + const envScope: Array> = useMemo(() => { + return ( + userEnvScopeData?.appEnvironments.map((env: EnvironmentType) => ({ + id: env.id, + name: env.name, + })) ?? [] + ) + }, [userEnvScopeData]) + + const envOptions = + appEnvsData?.appEnvironments.map((env: EnvironmentType) => { + const { id, name } = env + + return { + id, + name, + } + }) ?? [] + + const [isOpen, setIsOpen] = useState(false) + + const [scope, setScope] = useState>>([]) + const [showEnvHint, setShowEnvHint] = useState(false) + + const memberHasGlobalAccess = (account: ServiceAccountType) => + userHasGlobalAccess(account.role?.permissions) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + useEffect(() => { + setScope(envScope) + }, [envScope]) + + const handleUpdateScope = async (e: { preventDefault: () => void }) => { + e.preventDefault() + + if (scope.length === 0) { + setShowEnvHint(true) + return false + } + + const appEnvironments = appEnvsData.appEnvironments as EnvironmentType[] + + const envKeyPromises = appEnvironments + .filter((env) => scope.map((selectedEnv) => selectedEnv.id).includes(env.id)) + .map(async (env: EnvironmentType) => { + const { data } = await getEnvKey({ + variables: { + envId: env.id, + appId: params.app, + }, + }) + + const { + wrappedSeed: userWrappedSeed, + wrappedSalt: userWrappedSalt, + identityKey, + } = data.environmentKeys[0] + + const { seed, salt } = await unwrapEnvSecretsForUser( + userWrappedSeed, + userWrappedSalt, + keyring! + ) + + const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForAccount( + { seed, salt }, + account! + ) + + return { + envId: env.id, + userId: account!.id, + identityKey, + wrappedSeed, + wrappedSalt, + } + }) + + const envKeyInputs = await Promise.all(envKeyPromises) + + await updateScope({ + variables: { + memberId: account!.id, + memberType: MemberType.Service, + appId: params.app, + envKeys: envKeyInputs, + }, + refetchQueries: [ + { + query: GetAppEnvironments, + variables: { + appId: params.app, + memberId: account.id, + memberType: MemberType.Service, + }, + }, + ], + }) + + toast.success('Updated account access', { autoClose: 2000 }) + } + + const allowUpdateScope = userCanUpdateSAAccess + + return ( + <> +
+ {envScope.map((env) => ( + + {env.name} + + ))} + {allowUpdateScope && ( +
+ +
+ )} +
+ + + + +
+ + +
+
+ + + +

+ Manage access for {account.name} +

+ + +
+ +
+ {memberHasGlobalAccess(account) && ( + +

+ This user's role grants them access to all environments in this + App. To restrict their access, change their role from the{' '} + + organisation members + {' '} + page. +

+
+ )} + +
+ {scope.length === 0 && showEnvHint && ( + + Select an environment scope + + )} + + {({ open }) => ( + <> + + + + +
+ + {scope + .map((env: Partial) => env.name) + .join(' + ')} + + +
+
+ + +
+ {envOptions.map((env: Partial) => ( + + {({ active, selected }) => ( +
+ {selected ? ( + + ) : ( + + )} + + {env.name} + +
+ )} +
+ ))} +
+
+
+ + )} +
+
+ +
+ + +
+
+
+
+
+
+
+
+ + ) + } + + if (!organisation || loading) + return ( +
+ +
+ ) + + return ( +
+
+

Service Accounts

+
Manage access for service accounts to this App
+
+ {userCanReadAppSA ? ( +
+ {userCanAddAppSA && ( +
+ +
+ )} + + + + + + + + {userCanRemoveAppSA && } + + + + {data?.appServiceAccounts.map((account: ServiceAccountType) => ( + + + + + + {userCanRemoveAppSA && ( + + )} + + ))} + +
+ Account + + Environment Access +
+
+ +
+
+
+ {account.name} + +
+
+
+
+ +
+
+
+ +
+
+
+ ) : ( + + +
+ } + > + <> + + )} +
+ ) +} diff --git a/frontend/app/[team]/apps/[app]/tokens/page.tsx b/frontend/app/[team]/apps/[app]/access/tokens/page.tsx similarity index 100% rename from frontend/app/[team]/apps/[app]/tokens/page.tsx rename to frontend/app/[team]/apps/[app]/access/tokens/page.tsx diff --git a/frontend/app/[team]/apps/[app]/layout.tsx b/frontend/app/[team]/apps/[app]/layout.tsx index e2cf3d98..b9f528a9 100644 --- a/frontend/app/[team]/apps/[app]/layout.tsx +++ b/frontend/app/[team]/apps/[app]/layout.tsx @@ -39,21 +39,17 @@ export default function AppLayout({ link: '', }, { - name: 'Service tokens', - link: 'tokens', - }, - { - name: 'Logs', - link: 'logs', - }, - { - name: 'Members', - link: 'members', + name: 'Access', + link: 'access/members', }, { name: 'Syncing', link: 'syncing', }, + { + name: 'Logs', + link: 'logs', + }, { name: 'Settings', link: 'settings', @@ -64,7 +60,7 @@ export default function AppLayout({ const activeTabIndex = () => { if (app) { const currentUrl = path?.split('/')[4] || '' - const index = tabs.findIndex((tab) => tab.link === currentUrl) + const index = tabs.findIndex((tab) => tab.link.split('/')[0] === currentUrl) return index >= 0 ? index : 0 } return 0 diff --git a/frontend/graphql/mutations/apps/addAppMember.gql b/frontend/graphql/mutations/apps/addAppMember.gql index 04d236b3..b5f56d8a 100644 --- a/frontend/graphql/mutations/apps/addAppMember.gql +++ b/frontend/graphql/mutations/apps/addAppMember.gql @@ -1,5 +1,10 @@ -mutation AddMemberToApp($memberId: ID!, $appId: ID!, $envKeys: [EnvironmentKeyInput]) { - addAppMember(memberId: $memberId, appId: $appId, envKeys: $envKeys) { +mutation AddMemberToApp( + $memberId: ID! + $memberType: MemberType + $appId: ID! + $envKeys: [EnvironmentKeyInput] +) { + addAppMember(memberId: $memberId, memberType: $memberType, appId: $appId, envKeys: $envKeys) { app { id } diff --git a/frontend/graphql/mutations/apps/removeAppMember.gql b/frontend/graphql/mutations/apps/removeAppMember.gql index 61251f9d..ce3bf158 100644 --- a/frontend/graphql/mutations/apps/removeAppMember.gql +++ b/frontend/graphql/mutations/apps/removeAppMember.gql @@ -1,5 +1,5 @@ -mutation RemoveMemberFromApp($memberId: ID!, $appId: ID!) { - removeAppMember(memberId: $memberId, appId: $appId) { +mutation RemoveMemberFromApp($memberId: ID!, $memberType: MemberType, $appId: ID!) { + removeAppMember(memberId: $memberId, memberType: $memberType, appId: $appId) { app { id } diff --git a/frontend/graphql/mutations/apps/updateEnvScope.gql b/frontend/graphql/mutations/apps/updateEnvScope.gql index 3981df89..f85eda8d 100644 --- a/frontend/graphql/mutations/apps/updateEnvScope.gql +++ b/frontend/graphql/mutations/apps/updateEnvScope.gql @@ -1,5 +1,15 @@ -mutation UpdateEnvScope($memberId: ID!, $appId: ID!, $envKeys: [EnvironmentKeyInput]) { - updateMemberEnvironmentScope(memberId: $memberId, appId: $appId, envKeys: $envKeys) { +mutation UpdateEnvScope( + $memberId: ID! + $memberType: MemberType + $appId: ID! + $envKeys: [EnvironmentKeyInput] +) { + updateMemberEnvironmentScope( + memberId: $memberId + memberType: $memberType + appId: $appId + envKeys: $envKeys + ) { app { id } diff --git a/frontend/graphql/queries/apps/getAppServiceAccounts.gql b/frontend/graphql/queries/apps/getAppServiceAccounts.gql new file mode 100644 index 00000000..30b8ca71 --- /dev/null +++ b/frontend/graphql/queries/apps/getAppServiceAccounts.gql @@ -0,0 +1,15 @@ +query GetAppServiceAccounts($appId: ID!) { + appServiceAccounts(appId: $appId) { + id + identityKey + name + createdAt + role { + id + name + description + permissions + color + } + } +} diff --git a/frontend/graphql/queries/secrets/getAppEnvironments.gql b/frontend/graphql/queries/secrets/getAppEnvironments.gql index 32f45994..77ccd54a 100644 --- a/frontend/graphql/queries/secrets/getAppEnvironments.gql +++ b/frontend/graphql/queries/secrets/getAppEnvironments.gql @@ -1,5 +1,10 @@ -query GetAppEnvironments($appId: ID!, $memberId: ID) { - appEnvironments(appId: $appId, environmentId: null, memberId: $memberId) { +query GetAppEnvironments($appId: ID!, $memberId: ID, $memberType: MemberType) { + appEnvironments( + appId: $appId + environmentId: null + memberId: $memberId + memberType: $memberType + ) { id name envType diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index 790f6127..db645d70 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -2,6 +2,7 @@ query GetServiceAccounts($orgId: ID!, $id: ID) { serviceAccounts(orgId: $orgId, serviceAccountId: $id) { id name + identityKey role { id name diff --git a/frontend/utils/crypto/environments.ts b/frontend/utils/crypto/environments.ts index 8b62ecad..e80fc1c5 100644 --- a/frontend/utils/crypto/environments.ts +++ b/frontend/utils/crypto/environments.ts @@ -6,6 +6,7 @@ import { EnvironmentType, OrganisationMemberType, SecretType, + ServiceAccountType, } from '@/apollo/graphql' import { EnvKeypair, OrganisationKeyring } from './types' @@ -223,19 +224,19 @@ export const generateUserToken = async ( * Wraps environment secrets for a user. * * @param {{ seed: string; salt: string }} envSecrets - The environment secrets to be wrapped. - * @param {OrganisationMemberType} user - The user for whom the secrets are wrapped. + * @param {OrganisationMemberType | ServiceAccountType} account - The target account for whom the secrets are wrapped. * @returns {Promise<{ user: OrganisationMemberType; wrappedSeed: string; wrappedSalt: string }>} - An object containing the wrapped environment secrets and user information. */ -export const wrapEnvSecretsForUser = async ( +export const wrapEnvSecretsForAccount = async ( envSecrets: { seed: string; salt: string }, - user: OrganisationMemberType + account: OrganisationMemberType | ServiceAccountType ) => { - const userPubKey = await getUserKxPublicKey(user.identityKey!) + const userPubKey = await getUserKxPublicKey(account.identityKey!) const wrappedSeed = await encryptAsymmetric(envSecrets.seed, userPubKey) const wrappedSalt = await encryptAsymmetric(envSecrets.salt, userPubKey) return { - user, + user: account, wrappedSeed, wrappedSalt, } @@ -401,12 +402,12 @@ export const createNewEnv = async ( (user: OrganisationMemberType) => user.role!.name?.toLowerCase() === "owner" ) - const ownerWrappedEnv = await wrapEnvSecretsForUser({ seed, salt }, owner!) + const ownerWrappedEnv = await wrapEnvSecretsForAccount({ seed, salt }, owner!) const globalAccessUsersWrappedEnv = await Promise.all( globalAccessUsers .filter((user) => user.role!.name?.toLowerCase() !== "owner") .map(async (admin) => { - const adminWrappedEnvSecret = await wrapEnvSecretsForUser({ seed, salt }, admin) + const adminWrappedEnvSecret = await wrapEnvSecretsForAccount({ seed, salt }, admin) return adminWrappedEnvSecret }) ) From 7fd2a7aa5b98b1c9a7d1d65f2cc6b211ac126e27 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 26 Oct 2024 20:06:17 +0530 Subject: [PATCH 18/92] chore: update operation names to avoid type collisions --- frontend/apollo/gql.ts | 12 ++++++------ frontend/apollo/graphql.ts | 18 +++++++++--------- .../DeleteServiceAccountTokenDialog.tsx | 4 ++-- .../_components/CreateServiceAccountDialog.tsx | 4 ++-- .../_components/DeleteServiceAccountDialog.tsx | 4 ++-- .../service-accounts/createServiceAccount.gql | 2 +- .../service-accounts/deleteServiceAccount.gql | 2 +- .../deleteServiceAccountToken.gql | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 6d33cc01..2fdd28d5 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -50,10 +50,10 @@ const documents = { "mutation UpdateMemberRole($memberId: ID!, $roleId: ID!) {\n updateOrganisationMemberRole(memberId: $memberId, roleId: $roleId) {\n orgMember {\n id\n role {\n name\n }\n }\n }\n}": types.UpdateMemberRoleDocument, "mutation UpdateWrappedSecrets($orgId: ID!, $wrappedKeyring: String!, $wrappedRecovery: String!) {\n updateMemberWrappedSecrets(\n orgId: $orgId\n wrappedKeyring: $wrappedKeyring\n wrappedRecovery: $wrappedRecovery\n ) {\n orgMember {\n id\n }\n }\n}": types.UpdateWrappedSecretsDocument, "mutation RotateAppKey($id: ID!, $appToken: String!, $wrappedKeyShare: String!) {\n rotateAppKeys(id: $id, appToken: $appToken, wrappedKeyShare: $wrappedKeyShare) {\n app {\n id\n }\n }\n}": types.RotateAppKeyDocument, - "mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}": types.CreateServiceAccountDocument, + "mutation CreateServiceAccountOp($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}": types.CreateServiceAccountOpDocument, "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 DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountDocument, - "mutation DeleteServiceAccountToken($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}": types.DeleteServiceAccountTokenDocument, + "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 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, "mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument, "mutation DeleteProviderCreds($credentialId: ID!) {\n deleteProviderCredentials(credentialId: $credentialId) {\n ok\n }\n}": types.DeleteProviderCredsDocument, @@ -281,7 +281,7 @@ export function graphql(source: "mutation RotateAppKey($id: ID!, $appToken: Stri /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}"): (typeof documents)["mutation CreateServiceAccount($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}"]; +export function graphql(source: "mutation CreateServiceAccountOp($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}"): (typeof documents)["mutation CreateServiceAccountOp($name: String!, $orgId: ID!, $roleId: ID!, $identityKey: String!, $handlers: [ServiceAccountHandlerInput], $serverWrappedKeyring: String, $serverWrappedRecovery: String) {\n createServiceAccount(\n name: $name\n organisationId: $orgId\n roleId: $roleId\n identityKey: $identityKey\n handlers: $handlers\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -289,11 +289,11 @@ export function graphql(source: "mutation CreateSAToken($serviceAccountId: 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 DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccount($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}"]; +export function graphql(source: "mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $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 DeleteServiceAccountToken($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccountToken($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"]; +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. */ diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 9af88eb4..eab2128d 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -2069,7 +2069,7 @@ export type RotateAppKeyMutationVariables = Exact<{ export type RotateAppKeyMutation = { __typename?: 'Mutation', rotateAppKeys?: { __typename?: 'RotateAppKeysMutation', app?: { __typename?: 'AppType', id: string } | null } | null }; -export type CreateServiceAccountMutationVariables = Exact<{ +export type CreateServiceAccountOpMutationVariables = Exact<{ name: Scalars['String']['input']; orgId: Scalars['ID']['input']; roleId: Scalars['ID']['input']; @@ -2080,7 +2080,7 @@ export type CreateServiceAccountMutationVariables = Exact<{ }>; -export type CreateServiceAccountMutation = { __typename?: 'Mutation', createServiceAccount?: { __typename?: 'CreateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string } | null } | null }; +export type CreateServiceAccountOpMutation = { __typename?: 'Mutation', createServiceAccount?: { __typename?: 'CreateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string } | null } | null }; export type CreateSaTokenMutationVariables = Exact<{ serviceAccountId: Scalars['ID']['input']; @@ -2094,19 +2094,19 @@ export type CreateSaTokenMutationVariables = Exact<{ export type CreateSaTokenMutation = { __typename?: 'Mutation', createServiceAccountToken?: { __typename?: 'CreateServiceAccountTokenMutation', token?: { __typename?: 'ServiceAccountTokenType', id: string } | null } | null }; -export type DeleteServiceAccountMutationVariables = Exact<{ +export type DeleteServiceAccountOpMutationVariables = Exact<{ id: Scalars['ID']['input']; }>; -export type DeleteServiceAccountMutation = { __typename?: 'Mutation', deleteServiceAccount?: { __typename?: 'DeleteServiceAccountMutation', ok?: boolean | null } | null }; +export type DeleteServiceAccountOpMutation = { __typename?: 'Mutation', deleteServiceAccount?: { __typename?: 'DeleteServiceAccountMutation', ok?: boolean | null } | null }; -export type DeleteServiceAccountTokenMutationVariables = Exact<{ +export type DeleteServiceAccountTokenOpMutationVariables = Exact<{ id: Scalars['ID']['input']; }>; -export type DeleteServiceAccountTokenMutation = { __typename?: 'Mutation', deleteServiceAccountToken?: { __typename?: 'DeleteServiceAccountTokenMutation', ok?: boolean | null } | null }; +export type DeleteServiceAccountTokenOpMutation = { __typename?: 'Mutation', deleteServiceAccountToken?: { __typename?: 'DeleteServiceAccountTokenMutation', ok?: boolean | null } | null }; export type CreateNewAwsSecretsSyncMutationVariables = Exact<{ envId: Scalars['ID']['input']; @@ -2616,10 +2616,10 @@ export const InviteMemberDocument = {"kind":"Document","definitions":[{"kind":"O export const UpdateMemberRoleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateMemberRole"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"updateOrganisationMemberRole"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"memberId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}}},{"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":"orgMember"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateWrappedSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWrappedSecrets"},"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":"wrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateMemberWrappedSecrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orgMember"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const RotateAppKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RotateAppKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"rotateAppKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"appToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateServiceAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateServiceAccount"},"variableDefinitions":[{"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":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}},{"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"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateServiceAccountOp"},"variableDefinitions":[{"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":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}},{"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"}}]}}]}}]}}]} as unknown as DocumentNode; 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 DeleteServiceAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccount"},"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 DeleteServiceAccountTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountToken"},"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 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 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; export const CreateNewCfPagesSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfPagesSync"},"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":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflarePagesSync"},"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":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"deploymentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectEnv"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}}},{"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":"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":"id"}},{"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; export const DeleteProviderCredsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteProviderCreds"},"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":"deleteProviderCredentials"},"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":"ok"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx index 2f9f6906..866d5401 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/DeleteServiceAccountTokenDialog.tsx @@ -1,5 +1,5 @@ import { FaTrash } from 'react-icons/fa' -import { DeleteServiceAccountToken } from '@/graphql/mutations/service-accounts/deleteServiceAccountToken.gql' +import { DeleteServiceAccountTokenOp } from '@/graphql/mutations/service-accounts/deleteServiceAccountToken.gql' import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' import { useMutation } from '@apollo/client' import { toast } from 'react-toastify' @@ -15,7 +15,7 @@ export const DeleteServiceAccountTokenDialog = ({ token }: { token: ServiceAccou const dialogRef = useRef<{ closeModal: () => void }>(null) - const [deleteToken] = useMutation(DeleteServiceAccountToken) + const [deleteToken] = useMutation(DeleteServiceAccountTokenOp) const handleDelete = async () => { const deleted = await deleteToken({ diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index 3ffab343..0f2b9be0 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -6,7 +6,7 @@ import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServic import { GetServiceAccountHandlers } from '@/graphql/queries/service-accounts/getServiceAccountHandlers.gql' import { GetRoles } from '@/graphql/queries/organisation/getRoles.gql' import { GetServerKey } from '@/graphql/queries/syncing/getServerKey.gql' -import { CreateServiceAccount } from '@/graphql/mutations/service-accounts/createServiceAccount.gql' +import { CreateServiceAccountOp } from '@/graphql/mutations/service-accounts/createServiceAccount.gql' import { organisationContext } from '@/contexts/organisationContext' import { useMutation, useQuery } from '@apollo/client' import { @@ -39,7 +39,7 @@ export const CreateServiceAccountDialog = () => { skip: !organisation, }) - const [createServiceAccount] = useMutation(CreateServiceAccount) + const [createServiceAccount] = useMutation(CreateServiceAccountOp) const { data: serverKeyData } = useQuery(GetServerKey) diff --git a/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx index 5e59b8d4..368657ad 100644 --- a/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/DeleteServiceAccountDialog.tsx @@ -1,5 +1,5 @@ import { FaTrash } from 'react-icons/fa' -import { DeleteServiceAccount } from '@/graphql/mutations/service-accounts/deleteServiceAccount.gql' +import { DeleteServiceAccountOp } from '@/graphql/mutations/service-accounts/deleteServiceAccount.gql' import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' import { useMutation } from '@apollo/client' import { toast } from 'react-toastify' @@ -15,7 +15,7 @@ export const DeleteServiceAccountDialog = ({ account }: { account: ServiceAccoun const dialogRef = useRef<{ closeModal: () => void }>(null) - const [deleteAccount] = useMutation(DeleteServiceAccount) + const [deleteAccount] = useMutation(DeleteServiceAccountOp) const handleDelete = async () => { const deleted = await deleteAccount({ diff --git a/frontend/graphql/mutations/service-accounts/createServiceAccount.gql b/frontend/graphql/mutations/service-accounts/createServiceAccount.gql index b80c932b..de82a340 100644 --- a/frontend/graphql/mutations/service-accounts/createServiceAccount.gql +++ b/frontend/graphql/mutations/service-accounts/createServiceAccount.gql @@ -1,4 +1,4 @@ -mutation CreateServiceAccount( +mutation CreateServiceAccountOp( $name: String! $orgId: ID! $roleId: ID! diff --git a/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql b/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql index ae545236..62c949f1 100644 --- a/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql +++ b/frontend/graphql/mutations/service-accounts/deleteServiceAccount.gql @@ -1,4 +1,4 @@ -mutation DeleteServiceAccount($id: ID!) { +mutation DeleteServiceAccountOp($id: ID!) { deleteServiceAccount(serviceAccountId: $id) { ok } diff --git a/frontend/graphql/mutations/service-accounts/deleteServiceAccountToken.gql b/frontend/graphql/mutations/service-accounts/deleteServiceAccountToken.gql index 5361fe93..38c1091d 100644 --- a/frontend/graphql/mutations/service-accounts/deleteServiceAccountToken.gql +++ b/frontend/graphql/mutations/service-accounts/deleteServiceAccountToken.gql @@ -1,4 +1,4 @@ -mutation DeleteServiceAccountToken($id: ID!) { +mutation DeleteServiceAccountTokenOp($id: ID!) { deleteServiceAccountToken(tokenId: $id) { ok } From 86180d516451f2f04ea5ff3e1085f2cac0d5e8d6 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 26 Oct 2024 20:38:58 +0530 Subject: [PATCH 19/92] feat: add service accounts to app card --- backend/backend/graphene/types.py | 100 +++++++++++++------------ frontend/apollo/gql.ts | 4 +- frontend/apollo/graphql.ts | 5 +- frontend/apollo/schema.graphql | 75 ++++++++++--------- frontend/app/[team]/apps/page.tsx | 2 +- frontend/components/apps/AppCard.tsx | 33 +++++++- frontend/components/layout/Sidebar.tsx | 6 +- frontend/graphql/queries/getApps.gql | 4 + frontend/tailwind.config.js | 3 + 9 files changed, 137 insertions(+), 95 deletions(-) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 74363587..fd5b07a1 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -204,6 +204,54 @@ class Meta: ) +class ServiceAccountHandlerType(DjangoObjectType): + class Meta: + model = ServiceAccountHandler + fields = "__all__" + + +class ServiceAccountTokenType(DjangoObjectType): + class Meta: + model = ServiceAccountToken + fields = "__all__" + + +class MemberType(graphene.Enum): + USER = "user" + SERVICE = "service" + + +class ServiceAccountType(DjangoObjectType): + + third_party_auth_enabled = graphene.Boolean() + handlers = graphene.List(ServiceAccountHandlerType) + tokens = graphene.List(ServiceAccountTokenType) + + class Meta: + model = ServiceAccount + fields = ( + "id", + "name", + "role", + "apps", + "identity_key", + "created_at", + "updated_at", + ) + + def resolve_third_party_auth_enabled(self, info): + return ( + self.server_wrapped_keyring is not None + and self.server_wrapped_recovery is not None + ) + + def resolve_handlers(self, info): + return ServiceAccountHandler.objects.filter(service_account=self) + + def resolve_tokens(self, info): + return ServiceAccountToken.objects.filter(service_account=self) + + class ProviderType(graphene.ObjectType): id = graphene.String(required=True) name = graphene.String(required=True) @@ -322,6 +370,7 @@ def resolve_syncs(self, info): class AppType(DjangoObjectType): environments = graphene.NonNull(graphene.List(EnvironmentType)) members = graphene.NonNull(graphene.List(OrganisationMemberType)) + service_accounts = graphene.NonNull(graphene.List(ServiceAccountType)) class Meta: model = App @@ -357,6 +406,9 @@ def resolve_environments(self, info): def resolve_members(self, info): return self.members.filter(deleted_at=None) + def resolve_service_accounts(self, info): + return self.service_accounts.filter(deleted_at=None) + class EnvironmentKeyType(DjangoObjectType): class Meta: @@ -460,54 +512,6 @@ class Meta: ) -class ServiceAccountHandlerType(DjangoObjectType): - class Meta: - model = ServiceAccountHandler - fields = "__all__" - - -class ServiceAccountTokenType(DjangoObjectType): - class Meta: - model = ServiceAccountToken - fields = "__all__" - - -class MemberType(graphene.Enum): - USER = "user" - SERVICE = "service" - - -class ServiceAccountType(DjangoObjectType): - - third_party_auth_enabled = graphene.Boolean() - handlers = graphene.List(ServiceAccountHandlerType) - tokens = graphene.List(ServiceAccountTokenType) - - class Meta: - model = ServiceAccount - fields = ( - "id", - "name", - "role", - "apps", - "identity_key", - "created_at", - "updated_at", - ) - - def resolve_third_party_auth_enabled(self, info): - return ( - self.server_wrapped_keyring is not None - and self.server_wrapped_recovery is not None - ) - - def resolve_handlers(self, info): - return ServiceAccountHandler.objects.filter(service_account=self) - - def resolve_tokens(self, info): - return ServiceAccountToken.objects.filter(service_account=self) - - class SecretFolderType(DjangoObjectType): folder_count = graphene.Int() secret_count = graphene.Int() diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 2fdd28d5..91a4fdbe 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -77,7 +77,7 @@ const documents = { "query GetAppActivityChart($appId: ID!, $period: TimeRange) {\n appActivityChart(appId: $appId, period: $period) {\n index\n date\n data\n }\n}": types.GetAppActivityChartDocument, "query GetAppDetail($organisationId: ID!, $appId: ID!) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n appToken\n appSeed\n appVersion\n sseEnabled\n }\n}": types.GetAppDetailDocument, "query GetAppKmsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n kms {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n }\n kmsLogsCount(appId: $appId)\n}": types.GetAppKmsLogsDocument, - "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n environments {\n id\n name\n envType\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.GetAppsDocument, + "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n envType\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.GetAppsDocument, "query GetDashboard($organisationId: ID!) {\n apps(organisationId: $organisationId) {\n id\n sseEnabled\n }\n userTokens(organisationId: $organisationId) {\n id\n }\n organisationInvites(orgId: $organisationId) {\n id\n }\n organisationMembers(organisationId: $organisationId, role: null) {\n id\n }\n savedCredentials(orgId: $organisationId) {\n id\n }\n syncs(orgId: $organisationId) {\n id\n }\n}": types.GetDashboardDocument, "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}": types.GetOrganisationsDocument, "query CheckOrganisationNameAvailability($name: String!) {\n organisationNameAvailable(name: $name)\n}": types.CheckOrganisationNameAvailabilityDocument, @@ -389,7 +389,7 @@ export function graphql(source: "query GetAppKmsLogs($appId: ID!, $start: BigInt /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n environments {\n id\n name\n envType\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\n }\n }\n}"): (typeof documents)["query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n environments {\n id\n name\n envType\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\n }\n }\n}"]; +export function graphql(source: "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n envType\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\n }\n }\n}"): (typeof documents)["query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n envType\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\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 eab2128d..d1fb23f9 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -147,6 +147,7 @@ export type AppType = { identityKey: Scalars['String']['output']; members: Array>; name: Scalars['String']['output']; + serviceAccounts: Array>; sseEnabled: Scalars['Boolean']['output']; wrappedKeyShare: Scalars['String']['output']; }; @@ -2324,7 +2325,7 @@ export type GetAppsQueryVariables = Exact<{ }>; -export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; +export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, serviceAccounts: Array<{ __typename?: 'ServiceAccountType', id: string, name: string } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; export type GetDashboardQueryVariables = Exact<{ organisationId: Scalars['ID']['input']; @@ -2643,7 +2644,7 @@ export const GetCheckoutDetailsDocument = {"kind":"Document","definitions":[{"ki export const GetAppActivityChartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppActivityChart"},"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":"period"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"TimeRange"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appActivityChart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"period"},"value":{"kind":"Variable","name":{"kind":"Name","value":"period"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"data"}}]}}]}}]} as unknown as DocumentNode; export const GetAppDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"appToken"}},{"kind":"Field","name":{"kind":"Name","value":"appSeed"}},{"kind":"Field","name":{"kind":"Name","value":"appVersion"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}}]}}]} as unknown as DocumentNode; export const GetAppKmsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppKmsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"kms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"phaseNode"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"city"}},{"kind":"Field","name":{"kind":"Name","value":"phSize"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"kmsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]}]}}]} as unknown as DocumentNode; -export const GetAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetApps"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"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":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"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":"envType"}},{"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 GetAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetApps"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"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":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"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":"envType"}},{"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 GetDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"userTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationInvites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationMembers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"savedCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisations"},"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":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"planDetail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"userCount"}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"memberId"}},{"kind":"Field","name":{"kind":"Name","value":"keyring"}},{"kind":"Field","name":{"kind":"Name","value":"recovery"}}]}}]}}]} as unknown as DocumentNode; export const CheckOrganisationNameAvailabilityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheckOrganisationNameAvailability"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationNameAvailable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}]}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 32f91fe1..0662c2ec 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -197,6 +197,7 @@ type AppType { sseEnabled: Boolean! environments: [EnvironmentType]! members: [OrganisationMemberType]! + serviceAccounts: [ServiceAccountType]! } type EnvironmentType { @@ -312,6 +313,43 @@ enum ApiEnvironmentSyncEventStatusChoices { FAILED } +type ServiceAccountType { + id: String! + name: String! + role: RoleType + apps: [AppType!]! + identityKey: String + createdAt: DateTime + updatedAt: DateTime! + thirdPartyAuthEnabled: Boolean + handlers: [ServiceAccountHandlerType] + tokens: [ServiceAccountTokenType] +} + +type ServiceAccountHandlerType { + id: String! + serviceAccount: ServiceAccountType! + user: OrganisationMemberType! + wrappedKeyring: String! + wrappedRecovery: String! + createdAt: DateTime + updatedAt: DateTime! +} + +type ServiceAccountTokenType { + id: String! + serviceAccount: ServiceAccountType! + name: String! + identityKey: String! + token: String! + wrappedKeyShare: String! + createdBy: OrganisationMemberType + createdAt: DateTime + updatedAt: DateTime! + deletedAt: DateTime + expiresAt: DateTime +} + type LogsResponseType { kms: [KMSLogType] secrets: [SecretEventType] @@ -465,43 +503,6 @@ enum MemberType { SERVICE } -type ServiceAccountType { - id: String! - name: String! - role: RoleType - apps: [AppType!]! - identityKey: String - createdAt: DateTime - updatedAt: DateTime! - thirdPartyAuthEnabled: Boolean - handlers: [ServiceAccountHandlerType] - tokens: [ServiceAccountTokenType] -} - -type ServiceAccountHandlerType { - id: String! - serviceAccount: ServiceAccountType! - user: OrganisationMemberType! - wrappedKeyring: String! - wrappedRecovery: String! - createdAt: DateTime - updatedAt: DateTime! -} - -type ServiceAccountTokenType { - id: String! - serviceAccount: ServiceAccountType! - name: String! - identityKey: String! - token: String! - wrappedKeyShare: String! - createdBy: OrganisationMemberType - createdAt: DateTime - updatedAt: DateTime! - deletedAt: DateTime - expiresAt: DateTime -} - type EnvironmentKeyType { id: String! environment: EnvironmentType! diff --git a/frontend/app/[team]/apps/page.tsx b/frontend/app/[team]/apps/page.tsx index e047033a..2ce817c9 100644 --- a/frontend/app/[team]/apps/page.tsx +++ b/frontend/app/[team]/apps/page.tsx @@ -48,7 +48,7 @@ export default function AppsHome({ params }: { params: { team: string } }) { >

Apps

{userCanViewApps ? ( -
+
{apps?.map((app) => ( diff --git a/frontend/components/apps/AppCard.tsx b/frontend/components/apps/AppCard.tsx index b199145e..901ca8c3 100644 --- a/frontend/components/apps/AppCard.tsx +++ b/frontend/components/apps/AppCard.tsx @@ -1,4 +1,4 @@ -import { FaProjectDiagram, FaUsers } from 'react-icons/fa' +import { FaProjectDiagram, FaRobot, FaUsers } from 'react-icons/fa' import { Card } from '../common/Card' import { AppType } from '@/apollo/graphql' @@ -12,7 +12,7 @@ interface AppCardProps { } export const AppCard = (props: AppCardProps) => { - const { name, id, members, environments } = props.app + const { name, id, members, serviceAccounts, environments } = props.app const totalSyncCount = environments ? environments.reduce((acc, env) => acc + (env!.syncs?.length || 0), 0) @@ -32,6 +32,8 @@ export const AppCard = (props: AppCardProps) => { const surplusMemberCount = members.length > 5 ? members.length - 5 : 0 + const surplusServiceAccountsCount = serviceAccounts.length > 5 ? serviceAccounts.length - 5 : 0 + const surplusEnvCount = environments.length > 5 ? environments.length - 5 : 0 const surplusSynCount = providers.length > 5 ? providers.length - 5 : 0 @@ -65,6 +67,33 @@ export const AppCard = (props: AppCardProps) => {
+ {serviceAccounts.length > 0 && ( +
+
+ + {serviceAccounts.length} +
+ + {serviceAccounts.length > 1 ? 'Service Accounts' : 'Service Account'} + +
+ {serviceAccounts.slice(0, 5).map((account) => ( +
+ + {account?.name.slice(0, 1)} + +
+ ))} + {surplusMemberCount > 0 && ( + +{surplusMemberCount} + )} +
+
+ )} +
diff --git a/frontend/components/layout/Sidebar.tsx b/frontend/components/layout/Sidebar.tsx index 46494dc9..4339def0 100644 --- a/frontend/components/layout/Sidebar.tsx +++ b/frontend/components/layout/Sidebar.tsx @@ -35,10 +35,10 @@ const SidebarLink = (props: SidebarLinkT) => {
{icon}
diff --git a/frontend/graphql/queries/getApps.gql b/frontend/graphql/queries/getApps.gql index 3dc8320d..70ab2ec9 100644 --- a/frontend/graphql/queries/getApps.gql +++ b/frontend/graphql/queries/getApps.gql @@ -11,6 +11,10 @@ query GetApps($organisationId: ID!, $appId: ID) { fullName avatarUrl } + serviceAccounts { + id + name + } environments { id name diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 2db4bd33..74405047 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -25,6 +25,9 @@ module.exports = { }, typography: require('./utils/typography'), extend: { + screens: { + '1080p': '1920px', + }, colors: { primary: { 200: '#f7ff73', From 35460e953cb3d86800ba75f764b9f7473ddbc1aa Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 14:26:10 +0530 Subject: [PATCH 20/92] feat: service account auth, permissions serializers for rest --- backend/api/auth.py | 34 +++++++++-- backend/api/serializers.py | 59 +++++++++++++++++++ backend/api/utils/access/permissions.py | 29 +++++++-- backend/api/utils/rest.py | 25 +++++++- backend/api/views/auth.py | 24 +++++--- backend/api/views/secrets.py | 41 +++++++++---- .../backend/graphene/mutations/environment.py | 38 +++++++++--- .../graphene/mutations/service_accounts.py | 3 + .../graphene/queries/service_accounts.py | 11 ++-- 9 files changed, 221 insertions(+), 43 deletions(-) diff --git a/backend/api/auth.py b/backend/api/auth.py index 20df3dd3..b4b1df05 100644 --- a/backend/api/auth.py +++ b/backend/api/auth.py @@ -1,18 +1,22 @@ from api.utils.rest import ( get_org_member_from_user_token, + get_service_account_from_token, get_service_token, get_token_type, token_is_expired_or_deleted, ) from api.models import Environment -from api.utils.access.permissions import user_can_access_environment +from api.utils.access.permissions import ( + service_account_can_access_environment, + user_can_access_environment, +) from rest_framework import authentication, exceptions class PhaseTokenAuthentication(authentication.BaseAuthentication): def authenticate(self, request): - token_types = ["User", "Service"] + token_types = ["User", "Service", "ServiceAccount"] auth_token = request.headers.get("Authorization") @@ -24,7 +28,12 @@ def authenticate(self, request): if token_type not in token_types: raise exceptions.AuthenticationFailed("Invalid token") - auth = {"token": auth_token, "org_member": None, "service_token": None} + auth = { + "token": auth_token, + "org_member": None, + "service_token": None, + "service_account": None, + } if token_is_expired_or_deleted(auth_token): raise exceptions.AuthenticationFailed("Token expired or deleted") @@ -62,9 +71,26 @@ def authenticate(self, request): except Exception as ex: raise exceptions.NotFound("User not found") - else: + elif token_type == "Service": service_token = get_service_token(auth_token) auth["service_token"] = service_token user = service_token.created_by.user + if token_type == "ServiceAccount": + + try: + service_token = get_service_token(auth_token) + service_account = get_service_account_from_token(auth_token) + user = service_token.created_by.user + auth["service_account"] = service_account + + if not service_account_can_access_environment( + service_account.id, env.id + ): + raise exceptions.AuthenticationFailed( + "Service account cannot access this environment" + ) + except Exception as ex: + raise exceptions.NotFound("Service account not found") + return (user, auth) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index c1c60738..7fb3cec4 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -225,6 +225,65 @@ def to_representation(self, instance): return representation +class ServiceAccountTokenSerializer(serializers.ModelSerializer): + apps = EnvironmentKeySerializer(many=True, read_only=True) + + # New field 'userId' + account_id = serializers.UUIDField(source="service_account.id", read_only=True) + + # New field 'offline_enabled' with default value False + offline_enabled = serializers.BooleanField(default=False, read_only=True) + + organisation = OrganisationSerializer( + source="service_account.organisation", read_only=True + ) + + class Meta: + model = UserToken + fields = [ + "wrapped_key_share", + "account_id", + "offline_enabled", + "apps", + "organisation", + ] + + def to_representation(self, instance): + representation = super().to_representation(instance) + + # Filter environment_keys to include only those associated with the same user + service_account = instance.service_account + + if service_account is not None: + environment_keys = EnvironmentKey.objects.filter( + service_account=service_account, environment__app__deleted_at=None + ) + apps = [] + for key in environment_keys: + + serializer = EnvironmentKeySerializer(key) + index = find_index_by_id(apps, key.environment.app.id) + + app_data = { + "id": key.environment.app.id, + "name": key.environment.app.name, + "encryption": "E2E", + } + + if key.environment.app.sse_enabled: + app_data["encryption"] = "SSE" + + if index == -1: + app_data["environment_keys"] = [serializer.data] + apps.append(app_data) + else: + apps[index]["environment_keys"].append(serializer.data) + + representation["apps"] = apps + + return representation + + class LockboxSerializer(serializers.ModelSerializer): class Meta: model = Lockbox diff --git a/backend/api/utils/access/permissions.py b/backend/api/utils/access/permissions.py index 36fa4579..249d5343 100644 --- a/backend/api/utils/access/permissions.py +++ b/backend/api/utils/access/permissions.py @@ -3,6 +3,7 @@ Environment, EnvironmentKey, OrganisationMember, + ServiceAccount, ) from api.utils.access.roles import default_roles @@ -40,19 +41,39 @@ def user_can_access_environment(user_id, env_id): ).exists() +def service_account_can_access_environment(account_id, env_id): + env = Environment.objects.get(id=env_id) + service_account = ServiceAccount.objects.get( + organisation=env.app.organisation, id=account_id, deleted_at=None + ) + return EnvironmentKey.objects.filter( + service_account=service_account, environment_id=env_id + ).exists() + + def member_can_access_org(member_id, org_id): return OrganisationMember.objects.filter( id=member_id, organisation_id=org_id, deleted_at=None ).exists() -def user_has_permission(user, action, resource, organisation, is_app_resource=False): +def user_has_permission( + account, + action, + resource, + organisation, + is_app_resource=False, + is_service_account=False, +): """Check if the user has the specified permission for a resource in an organization.""" try: # Get the user's membership in the organization - org_member = OrganisationMember.objects.get( - user=user, organisation=organisation, deleted_at=None - ) + if is_service_account: + org_member = account + else: + org_member = OrganisationMember.objects.get( + user=account, organisation=organisation, deleted_at=None + ) role = org_member.role if not role: return False # No role assigned, hence no permissions diff --git a/backend/api/utils/rest.py b/backend/api/utils/rest.py index 3e5efa7d..fca2b29d 100644 --- a/backend/api/utils/rest.py +++ b/backend/api/utils/rest.py @@ -1,4 +1,4 @@ -from api.models import EnvironmentToken, ServiceToken, UserToken +from api.models import EnvironmentToken, ServiceAccountToken, ServiceToken, UserToken from django.utils import timezone import base64 @@ -49,13 +49,30 @@ def get_org_member_from_user_token(auth_token): return False +def get_service_account_from_token(auth_token): + token = auth_token.split(" ")[2] + + if not token: + return False + + try: + sa_token = ServiceAccountToken.objects.get(token=token) + return sa_token.service_account + except Exception as ex: + return False + + def get_service_token(auth_token): prefix, token_type, token_value = auth_token.split(" ") if token_type == "User": return None - return ServiceToken.objects.get(token=token_value) + elif token_type == "Service": + return ServiceToken.objects.get(token=token_value) + + elif token_type == "ServiceAccount": + return ServiceAccountToken.objects.get(token=token_value) def token_is_expired_or_deleted(auth_token): @@ -63,8 +80,10 @@ def token_is_expired_or_deleted(auth_token): if token_type == "User": token = UserToken.objects.get(token=token_value) - else: + elif token_type == "Service": token = ServiceToken.objects.get(token=token_value) + elif token_type == "ServiceAccount": + token = ServiceAccountToken.objects.get(token=token_value) return token.deleted_at is not None or ( token.expires_at is not None and token.expires_at < timezone.now() diff --git a/backend/api/views/auth.py b/backend/api/views/auth.py index 7594e6d2..773c6485 100644 --- a/backend/api/views/auth.py +++ b/backend/api/views/auth.py @@ -4,10 +4,11 @@ import jwt import os from api.serializers import ( + ServiceAccountTokenSerializer, ServiceTokenSerializer, UserTokenSerializer, ) -from api.models import ServiceToken, UserToken, CustomUser +from api.models import ServiceAccountToken, ServiceToken, UserToken, CustomUser from api.emails import send_login_email from api.utils.syncing.auth import store_oauth_token from backend.api.notifier import notify_slack @@ -122,7 +123,7 @@ def complete_login(self, request, app, token, response, **kwargs): raise OAuth2Error("Invalid id_token") from e login = self.get_provider().sociallogin_from_response(request, identity_data) email = identity_data.get("email") - full_name = identity_data.get("name") # Get the full name from the id_token + full_name = identity_data.get("name") # Get the full name from the id_token if CLOUD_HOSTED and not CustomUser.objects.filter(email=email).exists(): try: @@ -161,13 +162,12 @@ def complete_login(self, request, app, token, **kwargs): resp.raise_for_status() extra_data = resp.json() if app_settings.QUERY_EMAIL and not extra_data.get("email"): - extra_data["email"] = self.get_email(headers) + extra_data["email"] = self.get_email(headers) email = extra_data["email"] if CLOUD_HOSTED and not CustomUser.objects.filter(email=email).exists(): - - + try: # Notify Slack notify_slack(f"New user signup: {email}") @@ -175,7 +175,7 @@ def complete_login(self, request, app, token, **kwargs): print(f"Error notifying Slack: {e}") try: - full_name = extra_data.get("name", email.split('@')[0]) + full_name = extra_data.get("name", email.split("@")[0]) send_login_email(request, email, full_name, "GitHub") except Exception as e: print(f"Error sending email: {e}") @@ -268,9 +268,15 @@ def service_token_kms(request): token = auth_token.split(" ")[2] - service_token = ServiceToken.objects.get(token=token) + token_type = get_token_type(auth_token) - serializer = ServiceTokenSerializer(service_token) + if token_type == "Service": + service_token = ServiceToken.objects.get(token=token) + serializer = ServiceTokenSerializer(service_token) + + elif token_type == "ServiceAccount": + service_token = ServiceAccountToken.objects.get(token=token) + serializer = ServiceAccountTokenSerializer(service_token) return Response(serializer.data, status=status.HTTP_200_OK) @@ -285,7 +291,7 @@ def secrets_tokens(request): token_type = get_token_type(auth_token) - if token_type == "Service": + if token_type == "Service" or token_type == "ServiceAccount": return service_token_kms(request) elif token_type == "User": return user_token_kms(request) diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index 73fc3a79..da2eb746 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -18,6 +18,7 @@ get_environment_keys, ) from api.utils.access.permissions import ( + service_account_can_access_environment, user_can_access_environment, user_has_permission, ) @@ -56,13 +57,14 @@ def get(self, request): if not env.id: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - if request.auth["org_member"]: + if request.auth["org_member"] or request.auth["service_account"]: if not user_has_permission( - request.auth["org_member"].user, + request.auth["org_member"].user or request.auth["service_account"], "read", "Secrets", env.app.organisation, True, + request.auth["service_account"] is not None, ): return JsonResponse( { @@ -98,6 +100,7 @@ def get(self, request): SecretEvent.READ, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -115,13 +118,14 @@ def post(self, request): if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - if request.auth["org_member"]: + if request.auth["org_member"] or request.auth["service_account"]: if not user_has_permission( - request.auth["org_member"].user, + request.auth["org_member"].user or request.auth["service_account"], "create", "Secrets", env.app.organisation, True, + request.auth["service_account"] is not None, ): return JsonResponse( { @@ -183,6 +187,7 @@ def post(self, request): SecretEvent.CREATE, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -204,13 +209,14 @@ def put(self, request): if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - if request.auth["org_member"]: + if request.auth["org_member"] or request.auth["service_account"]: if not user_has_permission( - request.auth["org_member"].user, + request.auth["org_member"].user or request.auth["service_account"], "update", "Secrets", env.app.organisation, True, + request.auth["service_account"] is not None, ): return JsonResponse( { @@ -278,6 +284,7 @@ def put(self, request): SecretEvent.UPDATE, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -307,14 +314,14 @@ def delete(self, request): for secret in secrets_to_delete: if not Secret.objects.filter(id=secret.id).exists(): return JsonResponse({"error": "Secret doesn't exist"}, status=404) - if request.auth["org_member"]: - + if request.auth["org_member"] or request.auth["service_account"]: if not user_has_permission( - request.auth["org_member"].user, + request.auth["org_member"].user or request.auth["service_account"], "delete", "Secrets", - secret.environment.app.organisation, + secret.env.app.organisation, True, + request.auth["service_account"] is not None, ): return JsonResponse( { @@ -332,6 +339,15 @@ def delete(self, request): {"error": "You don't have access to this environment"}, status=403 ) + if request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, secret.environment.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + for secret in secrets_to_delete: secret.updated_at = timezone.now() secret.deleted_at = timezone.now() @@ -342,6 +358,7 @@ def delete(self, request): SecretEvent.DELETE, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -406,6 +423,7 @@ def get(self, request): SecretEvent.READ, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -507,6 +525,7 @@ def post(self, request): SecretEvent.CREATE, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -638,6 +657,7 @@ def put(self, request): SecretEvent.UPDATE, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) @@ -716,6 +736,7 @@ def delete(self, request): SecretEvent.DELETE, request.auth["org_member"], request.auth["service_token"], + request.auth["service_account"], ip_address, user_agent, ) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 0008f7d8..f4cfc489 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -755,7 +755,7 @@ def mutate(cls, root, info, secret_data): ) log_secret_event( - secret, SecretEvent.CREATE, org_member, None, ip_address, user_agent + secret, SecretEvent.CREATE, org_member, None, None, ip_address, user_agent ) return CreateSecretMutation(secret=secret) @@ -819,7 +819,13 @@ def mutate(cls, root, info, secrets_data): user=info.context.user, organisation=org, deleted_at=None ) log_secret_event( - secret, SecretEvent.CREATE, org_member, None, ip_address, user_agent + secret, + SecretEvent.CREATE, + org_member, + None, + None, + ip_address, + user_agent, ) return BulkCreateSecretMutation(secrets=created_secrets) @@ -874,7 +880,7 @@ def mutate(cls, root, info, id, secret_data): ) log_secret_event( - secret, SecretEvent.UPDATE, org_member, None, ip_address, user_agent + secret, SecretEvent.UPDATE, org_member, None, None, ip_address, user_agent ) return EditSecretMutation(secret=secret) @@ -932,7 +938,13 @@ def mutate(cls, root, info, secrets_data): user=info.context.user, organisation=org, deleted_at=None ) log_secret_event( - secret, SecretEvent.UPDATE, org_member, None, ip_address, user_agent + secret, + SecretEvent.UPDATE, + org_member, + None, + None, + ip_address, + user_agent, ) return BulkEditSecretMutation(secrets=updated_secrets) @@ -966,7 +978,7 @@ def mutate(cls, root, info, id): ) log_secret_event( - secret, SecretEvent.DELETE, org_member, None, ip_address, user_agent + secret, SecretEvent.DELETE, org_member, None, None, ip_address, user_agent ) return DeleteSecretMutation(secret=secret) @@ -1004,7 +1016,13 @@ def mutate(cls, root, info, ids): user=info.context.user, organisation=org, deleted_at=None ) log_secret_event( - secret, SecretEvent.DELETE, org_member, None, ip_address, user_agent + secret, + SecretEvent.DELETE, + org_member, + None, + None, + ip_address, + user_agent, ) return BulkDeleteSecretMutation(secrets=deleted_secrets) @@ -1032,7 +1050,13 @@ def mutate(cls, root, info, ids): ) log_secret_event( - secret, SecretEvent.READ, org_member, None, ip_address, user_agent + secret, + SecretEvent.READ, + org_member, + None, + None, + ip_address, + user_agent, ) return ReadSecretMutation(ok=True) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 186e32a6..7986a75a 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -52,6 +52,9 @@ def mutate( "You don't have the permissions required to create Service Accounts in this organisation" ) + if handlers is None or handlers.length == 0: + raise GraphQLError("At least one service account handler must be provided") + service_account = ServiceAccount.objects.create( name=name, organisation=org, diff --git a/backend/backend/graphene/queries/service_accounts.py b/backend/backend/graphene/queries/service_accounts.py index bd1d3f98..e39f89ed 100644 --- a/backend/backend/graphene/queries/service_accounts.py +++ b/backend/backend/graphene/queries/service_accounts.py @@ -28,12 +28,11 @@ def resolve_service_account_handlers(root, info, org_id): service_account_handler_roles = Role.objects.filter( Q(organisation_id=org_id) & ( - Q(name__iexact="owner") | Q(name__iexact="admin") - ) # Check for "owner" or "admin" roles - | Q(permissions__global_access=True), # Check for global access roles - Q( - permissions__permissions__ServiceAccounts__gt=0 - ), # Add condition for non-empty 'ServiceAccounts' list + Q(name__iexact="owner") + | Q(name__iexact="admin") + | Q(permissions__global_access=True) # Check for global access roles + | Q(permissions__permissions__ServiceAccounts__gt=0) + ), ) members = OrganisationMember.objects.filter( From eaf418a0e5c0dc2f4f08a6255b767f299a6f82ca Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 14:26:36 +0530 Subject: [PATCH 21/92] feat: add service account logging for secret crud --- .../0088_secretevent_service_account.py | 19 +++++++++++++++++++ backend/api/models.py | 3 +++ backend/api/utils/audit_logging.py | 9 ++++++++- backend/backend/graphene/types.py | 1 + frontend/apollo/gql.ts | 4 ++-- frontend/apollo/graphql.ts | 5 +++-- frontend/apollo/schema.graphql | 1 + .../graphql/queries/secrets/getSecrets.gql | 4 ++++ 8 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 backend/api/migrations/0088_secretevent_service_account.py diff --git a/backend/api/migrations/0088_secretevent_service_account.py b/backend/api/migrations/0088_secretevent_service_account.py new file mode 100644 index 00000000..029d05ae --- /dev/null +++ b/backend/api/migrations/0088_secretevent_service_account.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.15 on 2024-10-28 08:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0087_alter_serviceaccount_apps'), + ] + + operations = [ + migrations.AddField( + model_name='secretevent', + name='service_account', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.serviceaccount'), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 57e5611d..0786d298 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -566,6 +566,9 @@ class SecretEvent(models.Model): service_token = models.ForeignKey( ServiceToken, on_delete=models.SET_NULL, blank=True, null=True ) + service_account = models.ForeignKey( + ServiceAccount, on_delete=models.SET_NULL, blank=True, null=True + ) key = models.TextField() key_digest = models.TextField() value = models.TextField() diff --git a/backend/api/utils/audit_logging.py b/backend/api/utils/audit_logging.py index 9f315841..349a402d 100644 --- a/backend/api/utils/audit_logging.py +++ b/backend/api/utils/audit_logging.py @@ -4,7 +4,13 @@ def log_secret_event( - secret, event_type, user, service_token=None, ip_address=None, user_agent=None + secret, + event_type, + user=None, + service_token=None, + service_account=None, + ip_address=None, + user_agent=None, ): """ Utility function to log secret events. @@ -16,6 +22,7 @@ def log_secret_event( path=secret.path, user=user, service_token=service_token, + service_account=service_account, key=secret.key, key_digest=secret.key_digest, value=secret.value, diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index fd5b07a1..44f3c7e0 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -555,6 +555,7 @@ class Meta: "timestamp", "user", "service_token", + "service_account", "ip_address", "user_agent", "environment", diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 91a4fdbe..79f2d24e 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -96,7 +96,7 @@ const documents = { "query GetFolders($envId: ID!, $path: String) {\n folders(envId: $envId, path: $path) {\n id\n name\n path\n createdAt\n folderCount\n secretCount\n }\n}": types.GetFoldersDocument, "query GetEnvSecretsKV($envId: ID!) {\n folders(envId: $envId, path: \"/\") {\n id\n name\n }\n secrets(envId: $envId, path: \"/\") {\n id\n key\n value\n path\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetEnvSecretsKvDocument, "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n eventType\n }\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 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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}": types.GetServiceAccountsDocument, @@ -465,7 +465,7 @@ export function graphql(source: "query GetSecretTags($orgId: ID!) {\n secretTag /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n eventType\n }\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}"): (typeof documents)["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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n eventType\n }\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}"]; +export function graphql(source: "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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}"): (typeof documents)["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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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}"]; /** * 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 d1fb23f9..1892813a 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1525,6 +1525,7 @@ export type SecretEventType = { key: Scalars['String']['output']; path: Scalars['String']['output']; secret: SecretType; + serviceAccount?: Maybe; serviceToken?: Maybe; tags: Array; timestamp: Scalars['DateTime']['output']; @@ -2463,7 +2464,7 @@ export type GetSecretsQueryVariables = Exact<{ }>; -export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, path: string, comment: string, createdAt?: any | null, updatedAt: any, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, path: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null } | null> | null, override?: { __typename?: 'PersonalSecretType', value?: string | null, isActive: boolean } | null, environment: { __typename?: 'EnvironmentType', id: string, app: { __typename?: 'AppType', id: string } } } | null> | null, folders?: Array<{ __typename?: 'SecretFolderType', id: string, name: string, path: string, createdAt?: any | null, folderCount?: number | null, secretCount?: number | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string, app: { __typename?: 'AppType', name: string } } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null, envSyncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string, options: any, isActive: boolean, status: ApiEnvironmentSyncStatusChoices, lastSync?: any | null, createdAt?: any | null, environment: { __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices }, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null } | null } | null> | null }; +export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, path: string, comment: string, createdAt?: any | null, updatedAt: any, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, path: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string } | null } | null> | null, override?: { __typename?: 'PersonalSecretType', value?: string | null, isActive: boolean } | null, environment: { __typename?: 'EnvironmentType', id: string, app: { __typename?: 'AppType', id: string } } } | null> | null, folders?: Array<{ __typename?: 'SecretFolderType', id: string, name: string, path: string, createdAt?: any | null, folderCount?: number | null, secretCount?: number | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string, app: { __typename?: 'AppType', name: string } } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null, envSyncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string, options: any, isActive: boolean, status: ApiEnvironmentSyncStatusChoices, lastSync?: any | null, createdAt?: any | null, environment: { __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices }, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null } | null } | null> | null }; export type GetServiceTokensQueryVariables = Exact<{ appId: Scalars['ID']['input']; @@ -2663,7 +2664,7 @@ export const GetEnvironmentTokensDocument = {"kind":"Document","definitions":[{" export const GetFoldersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFolders"},"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":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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"}}]}}]}}]} as unknown as DocumentNode; export const GetEnvSecretsKvDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetEnvSecretsKV"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"StringValue","value":"/","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"StringValue","value":"/","block":false}}],"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":"environmentKeys"},"arguments":[{"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"}}]}}]}}]} as unknown as DocumentNode; 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}}]}},{"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 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"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":"eventType"}}]}},{"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 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":"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 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":"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":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 0662c2ec..d5cc1c42 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -392,6 +392,7 @@ type SecretEventType { path: String! user: OrganisationMemberType serviceToken: ServiceTokenType + serviceAccount: ServiceAccountType key: String! value: String! version: Int! diff --git a/frontend/graphql/queries/secrets/getSecrets.gql b/frontend/graphql/queries/secrets/getSecrets.gql index c6d80b43..9d75d959 100644 --- a/frontend/graphql/queries/secrets/getSecrets.gql +++ b/frontend/graphql/queries/secrets/getSecrets.gql @@ -33,6 +33,10 @@ query GetSecrets($appId: ID!, $envId: ID!, $path: String) { fullName avatarUrl } + serviceAccount { + id + name + } eventType } override { From 00856bdcb6879137c6acdd7fa19dd76c7e4624ee Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 14:35:29 +0530 Subject: [PATCH 22/92] feat: render service accounts in logs --- frontend/apollo/gql.ts | 8 +-- frontend/apollo/graphql.ts | 8 +-- frontend/components/logs/SecretLogs.tsx | 52 ++++++++++--------- .../queries/secrets/getAppSecretsLogs.gql | 6 ++- .../graphql/queries/secrets/getSecrets.gql | 4 ++ 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 79f2d24e..2f1c7b16 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -90,13 +90,13 @@ const documents = { "query GetRoles($orgId: ID!) {\n roles(orgId: $orgId) {\n id\n name\n description\n color\n permissions\n isDefault\n }\n}": types.GetRolesDocument, "query VerifyInvite($inviteId: ID!) {\n validateInvite(inviteId: $inviteId) {\n id\n organisation {\n id\n name\n }\n inviteeEmail\n invitedBy {\n email\n }\n apps {\n id\n name\n }\n }\n}": types.VerifyInviteDocument, "query GetAppEnvironments($appId: ID!, $memberId: ID, $memberType: MemberType) {\n appEnvironments(\n appId: $appId\n environmentId: null\n memberId: $memberId\n memberType: $memberType\n ) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n app {\n name\n id\n }\n secretCount\n folderCount\n index\n members {\n email\n fullName\n avatarUrl\n }\n }\n sseEnabled(appId: $appId)\n serverPublicKey\n}": types.GetAppEnvironmentsDocument, - "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}": types.GetAppSecretsLogsDocument, + "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}": types.GetAppSecretsLogsDocument, "query GetEnvironmentKey($envId: ID!, $appId: ID!) {\n environmentKeys(environmentId: $envId, appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetEnvironmentKeyDocument, "query GetEnvironmentTokens($envId: ID!) {\n environmentTokens(environmentId: $envId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetEnvironmentTokensDocument, "query GetFolders($envId: ID!, $path: String) {\n folders(envId: $envId, path: $path) {\n id\n name\n path\n createdAt\n folderCount\n secretCount\n }\n}": types.GetFoldersDocument, "query GetEnvSecretsKV($envId: ID!) {\n folders(envId: $envId, path: \"/\") {\n id\n name\n }\n secrets(envId: $envId, path: \"/\") {\n id\n key\n value\n path\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetEnvSecretsKvDocument, "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}": types.GetServiceAccountsDocument, @@ -441,7 +441,7 @@ export function graphql(source: "query GetAppEnvironments($appId: ID!, $memberId /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"): (typeof documents)["query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"]; +export function graphql(source: "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"): (typeof documents)["query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -465,7 +465,7 @@ export function graphql(source: "query GetSecretTags($orgId: ID!) {\n secretTag /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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}"): (typeof documents)["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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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}"]; +export function graphql(source: "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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}"): (typeof documents)["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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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}"]; /** * 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 1892813a..989fdd13 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -2418,7 +2418,7 @@ export type GetAppSecretsLogsQueryVariables = Exact<{ }>; -export type GetAppSecretsLogsQuery = { __typename?: 'Query', secretsLogsCount?: number | null, logs?: { __typename?: 'LogsResponseType', secrets?: Array<{ __typename?: 'SecretEventType', id: string, path: string, key: string, value: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceToken?: { __typename?: 'ServiceTokenType', id: string, name: string } | null, environment: { __typename?: 'EnvironmentType', id: string, envType: ApiEnvironmentEnvTypeChoices, name: string }, secret: { __typename?: 'SecretType', id: string, path: string } } | null> | null } | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string, environment: { __typename?: 'EnvironmentType', id: string } } | null> | null }; +export type GetAppSecretsLogsQuery = { __typename?: 'Query', secretsLogsCount?: number | null, logs?: { __typename?: 'LogsResponseType', secrets?: Array<{ __typename?: 'SecretEventType', id: string, path: string, key: string, value: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceToken?: { __typename?: 'ServiceTokenType', id: string, name: string } | null, serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string } | null, environment: { __typename?: 'EnvironmentType', id: string, envType: ApiEnvironmentEnvTypeChoices, name: string }, secret: { __typename?: 'SecretType', id: string, path: string } } | null> | null } | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string, environment: { __typename?: 'EnvironmentType', id: string } } | null> | null }; export type GetEnvironmentKeyQueryVariables = Exact<{ envId: Scalars['ID']['input']; @@ -2464,7 +2464,7 @@ export type GetSecretsQueryVariables = Exact<{ }>; -export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, path: string, comment: string, createdAt?: any | null, updatedAt: any, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, path: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string } | null } | null> | null, override?: { __typename?: 'PersonalSecretType', value?: string | null, isActive: boolean } | null, environment: { __typename?: 'EnvironmentType', id: string, app: { __typename?: 'AppType', id: string } } } | null> | null, folders?: Array<{ __typename?: 'SecretFolderType', id: string, name: string, path: string, createdAt?: any | null, folderCount?: number | null, secretCount?: number | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string, app: { __typename?: 'AppType', name: string } } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null, envSyncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string, options: any, isActive: boolean, status: ApiEnvironmentSyncStatusChoices, lastSync?: any | null, createdAt?: any | null, environment: { __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices }, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null } | null } | null> | null }; +export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, path: string, comment: string, createdAt?: any | null, updatedAt: any, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, path: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceToken?: { __typename?: 'ServiceTokenType', id: string, name: string } | null, serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string } | null } | null> | null, override?: { __typename?: 'PersonalSecretType', value?: string | null, isActive: boolean } | null, environment: { __typename?: 'EnvironmentType', id: string, app: { __typename?: 'AppType', id: string } } } | null> | null, folders?: Array<{ __typename?: 'SecretFolderType', id: string, name: string, path: string, createdAt?: any | null, folderCount?: number | null, secretCount?: number | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string, app: { __typename?: 'AppType', name: string } } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null, envSyncs?: Array<{ __typename?: 'EnvironmentSyncType', id: string, options: any, isActive: boolean, status: ApiEnvironmentSyncStatusChoices, lastSync?: any | null, createdAt?: any | null, environment: { __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices }, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null } | null } | null> | null }; export type GetServiceTokensQueryVariables = Exact<{ appId: Scalars['ID']['input']; @@ -2658,13 +2658,13 @@ export const GetOrganisationPlanDocument = {"kind":"Document","definitions":[{"k export const GetRolesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRoles"},"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":"roles"},"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}}]} as unknown as DocumentNode; export const VerifyInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"VerifyInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validateInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppEnvironments"},"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":"memberId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"memberType"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MemberType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"NullValue"}},{"kind":"Argument","name":{"kind":"Name","value":"memberId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}}},{"kind":"Argument","name":{"kind":"Name","value":"memberType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberType"}}}],"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":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretCount"}},{"kind":"Field","name":{"kind":"Name","value":"folderCount"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"serverPublicKey"}}]}}]} as unknown as DocumentNode; -export const GetAppSecretsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppSecretsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetAppSecretsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppSecretsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetEnvironmentKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetEnvironmentKey"},"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":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}}]}}]}}]} as unknown as DocumentNode; export const GetEnvironmentTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetEnvironmentTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentTokens"},"arguments":[{"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":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetFoldersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFolders"},"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":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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"}}]}}]}}]} as unknown as DocumentNode; export const GetEnvSecretsKvDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetEnvSecretsKV"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"StringValue","value":"/","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"StringValue","value":"/","block":false}}],"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":"environmentKeys"},"arguments":[{"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"}}]}}]}}]} as unknown as DocumentNode; 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"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":"eventType"}}]}},{"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 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}}]}},{"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 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":"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 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":"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":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/components/logs/SecretLogs.tsx b/frontend/components/logs/SecretLogs.tsx index 5efb2e43..d6f4def0 100644 --- a/frontend/components/logs/SecretLogs.tsx +++ b/frontend/components/logs/SecretLogs.tsx @@ -9,7 +9,7 @@ import { } from '@/apollo/graphql' import { Disclosure, Transition } from '@headlessui/react' import clsx from 'clsx' -import { FaBan, FaChevronRight, FaExternalLinkAlt, FaKey } from 'react-icons/fa' +import { FaBan, FaChevronRight, FaExternalLinkAlt, FaKey, FaRobot } from 'react-icons/fa' import { FiRefreshCw, FiChevronsDown } from 'react-icons/fi' import { dateToUnixTimestamp, relativeTimeFromDates } from '@/utils/time' import { ReactNode, useContext, useEffect, useRef, useState } from 'react' @@ -237,6 +237,31 @@ export default function SecretLogs(props: { app: string }) { if (eventType === ApiSecretEventEventTypeChoices.D) return 'Deleted secret' } + const logCreatedBy = (log: SecretEventType) => { + if (log.user) + return ( +
+ + {log.user.fullName || log.user.email} +
+ ) + else if (log.serviceToken) + return ( +
+ {log.serviceToken ? log.serviceToken.name : 'Service token'} +
+ ) + else if (log.serviceAccount) + return ( +
+
+ +
{' '} + {log.serviceAccount.name} +
+ ) + } + return ( {({ open }) => ( @@ -266,16 +291,7 @@ export default function SecretLogs(props: { app: string }) {
- {log.user ? ( -
- - {log.user.fullName || log.user.email} -
- ) : ( -
- {log.serviceToken ? log.serviceToken.name : 'Service token'} -
- )} + {logCreatedBy(log)}
@@ -333,19 +349,7 @@ export default function SecretLogs(props: { app: string }) {
- - {' '} - {log.user ? ( -
- - {log.user.fullName || log.user.email} -
- ) : ( -
- {log.serviceToken ? log.serviceToken.name : 'Service token'} -
- )} -
+ {logCreatedBy(log)} {log.ipAddress} diff --git a/frontend/graphql/queries/secrets/getAppSecretsLogs.gql b/frontend/graphql/queries/secrets/getAppSecretsLogs.gql index 9f204475..b32436bd 100644 --- a/frontend/graphql/queries/secrets/getAppSecretsLogs.gql +++ b/frontend/graphql/queries/secrets/getAppSecretsLogs.gql @@ -17,7 +17,7 @@ query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) { userAgent user { email - username + username fullName avatarUrl } @@ -25,6 +25,10 @@ query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) { id name } + serviceAccount { + id + name + } eventType environment { id diff --git a/frontend/graphql/queries/secrets/getSecrets.gql b/frontend/graphql/queries/secrets/getSecrets.gql index 9d75d959..d06f3a4c 100644 --- a/frontend/graphql/queries/secrets/getSecrets.gql +++ b/frontend/graphql/queries/secrets/getSecrets.gql @@ -33,6 +33,10 @@ query GetSecrets($appId: ID!, $envId: ID!, $path: String) { fullName avatarUrl } + serviceToken { + id + name + } serviceAccount { id name From 1e6f484a13c7075ae6cc87e85f032ccb870eeef2 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 14:53:27 +0530 Subject: [PATCH 23/92] fix: check that service account has handlers --- backend/backend/graphene/mutations/service_accounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 7986a75a..4be3ac96 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -52,7 +52,7 @@ def mutate( "You don't have the permissions required to create Service Accounts in this organisation" ) - if handlers is None or handlers.length == 0: + if handlers is None or len(handlers) == 0: raise GraphQLError("At least one service account handler must be provided") service_account = ServiceAccount.objects.create( From 62959ad83fb37fb4e0288d694dd4e8d4ed08020c Mon Sep 17 00:00:00 2001 From: Nimish Date: Mon, 28 Oct 2024 15:54:49 +0530 Subject: [PATCH 24/92] fix: spelling --- frontend/app/[team]/access/service-accounts/[account]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 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 52aac750..cb6215e6 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -88,7 +88,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc href={`/${params.team}/access/service-accounts`} className="text-neutral-500 flex items-center gap-2 text-sm hover:text-zinc-800 dark:hover:text-zinc-200 transition ease" > - Back to service accouts + Back to service accounts
From 7c02d5d50c9829620f13e2969a39afe9af3778c7 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 18:59:35 +0530 Subject: [PATCH 25/92] test: permission check --- backend/api/views/secrets.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index da2eb746..43564a83 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -58,8 +58,13 @@ def get(self, request): return JsonResponse({"error": "Environment doesn't exist"}, status=404) if request.auth["org_member"] or request.auth["service_account"]: + account = ( + request.auth["service_account"] + if "service_account" in request + else request.auth["org_member"].user + ) if not user_has_permission( - request.auth["org_member"].user or request.auth["service_account"], + account, "read", "Secrets", env.app.organisation, From d44163ca4aef38c3702ea23cfc195d0899a24e1e Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 19:04:37 +0530 Subject: [PATCH 26/92] fix: typo --- backend/api/views/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index 43564a83..7547ecac 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -60,7 +60,7 @@ def get(self, request): if request.auth["org_member"] or request.auth["service_account"]: account = ( request.auth["service_account"] - if "service_account" in request + if "service_account" in request.auth else request.auth["org_member"].user ) if not user_has_permission( From 68d70dc4341369a2683bb290fe0ad4ca0051e6e0 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 19:29:39 +0530 Subject: [PATCH 27/92] fix: permission checks --- backend/api/views/secrets.py | 237 ++++++++++++++++++++++++++++------- 1 file changed, 190 insertions(+), 47 deletions(-) diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index 7547ecac..ee1e6325 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -78,6 +78,22 @@ def get(self, request): status=403, ) + if request.auth["org_member"] is not None and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + ip_address, user_agent = get_resolver_request_meta(request) secrets_filter = {"environment": env, "deleted_at": None} @@ -124,8 +140,13 @@ def post(self, request): return JsonResponse({"error": "Environment doesn't exist"}, status=404) if request.auth["org_member"] or request.auth["service_account"]: + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) if not user_has_permission( - request.auth["org_member"].user or request.auth["service_account"], + account, "create", "Secrets", env.app.organisation, @@ -139,6 +160,22 @@ def post(self, request): status=403, ) + if request.auth["org_member"] is not None and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + request_body = json.loads(request.body) ip_address, user_agent = get_resolver_request_meta(request) @@ -215,8 +252,14 @@ def put(self, request): return JsonResponse({"error": "Environment doesn't exist"}, status=404) if request.auth["org_member"] or request.auth["service_account"]: + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) + if not user_has_permission( - request.auth["org_member"].user or request.auth["service_account"], + account, "update", "Secrets", env.app.organisation, @@ -230,6 +273,22 @@ def put(self, request): status=403, ) + if request.auth["org_member"] is not None and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + request_body = json.loads(request.body) ip_address, user_agent = get_resolver_request_meta(request) @@ -320,8 +379,13 @@ def delete(self, request): if not Secret.objects.filter(id=secret.id).exists(): return JsonResponse({"error": "Secret doesn't exist"}, status=404) if request.auth["org_member"] or request.auth["service_account"]: + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) if not user_has_permission( - request.auth["org_member"].user or request.auth["service_account"], + account, "delete", "Secrets", secret.env.app.organisation, @@ -385,13 +449,19 @@ def dispatch(self, request, *args): def get(self, request): env = request.auth["environment"] - if request.auth["org_member"]: + if request.auth["org_member"] or request.auth["service_account"]: + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) if not user_has_permission( - request.auth["org_member"].user, + account, "read", "Secrets", env.app.organisation, True, + request.auth["service_account"] is not None, ): return JsonResponse( { @@ -400,6 +470,22 @@ def get(self, request): status=403, ) + if request.auth["org_member"] is not None and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + # Check if SSE is enabled for this environment if not env.app.sse_enabled: return Response({"error": "SSE is not enabled for this App"}, status=400) @@ -445,20 +531,41 @@ def post(self, request): env = request.auth["environment"] - if request.auth["org_member"]: - if not user_has_permission( - request.auth["org_member"].user, - "create", - "Secrets", - env.app.organisation, - True, - ): - return JsonResponse( - { - "error": "You don't have permission to create secrets in this environment" - }, - status=403, - ) + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) + if not user_has_permission( + account, + "create", + "Secrets", + env.app.organisation, + True, + request.auth["service_account"] is not None, + ): + return JsonResponse( + { + "error": "You don't have permission to create secrets in this environment" + }, + status=403, + ) + + if request.auth["org_member"] is not None and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -557,20 +664,42 @@ def put(self, request): env = request.auth["environment"] - if request.auth["org_member"]: - if not user_has_permission( - request.auth["org_member"].user, - "update", - "Secrets", - env.app.organisation, - True, - ): - return JsonResponse( - { - "error": "You don't have permission to update secrets in this environment" - }, - status=403, - ) + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) + + if not user_has_permission( + account, + "update", + "Secrets", + env.app.organisation, + True, + request.auth["service_account"] is not None, + ): + return JsonResponse( + { + "error": "You don't have permission to update secrets in this environment" + }, + status=403, + ) + + if request.auth["org_member"] is not None and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -693,20 +822,25 @@ def delete(self, request): env = request.auth["environment"] - if request.auth["org_member"]: - if not user_has_permission( - request.auth["org_member"].user, - "delete", - "Secrets", - env.app.organisation, - True, - ): - return JsonResponse( - { - "error": "You don't have permission to delete secrets in this environment" - }, - status=403, - ) + account = ( + request.auth["service_account"] + if "service_account" in request.auth + else request.auth["org_member"].user + ) + if not user_has_permission( + account, + "delete", + "Secrets", + env.app.organisation, + True, + request.auth["service_account"] is not None, + ): + return JsonResponse( + { + "error": "You don't have permission to delete secrets in this environment" + }, + status=403, + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -731,6 +865,15 @@ def delete(self, request): {"error": "You don't have access to this environment"}, status=403 ) + if request.auth[ + "service_account" + ] is not None and not service_account_can_access_environment( + request.auth["service_account"].user.userId, secret.environment.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + for secret in secrets_to_delete: secret.updated_at = timezone.now() secret.deleted_at = timezone.now() From d92b5fd64e711232c7ea4a1aa9583c92cdccce15 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 28 Oct 2024 19:34:33 +0530 Subject: [PATCH 28/92] fix: typo --- backend/api/views/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index ee1e6325..0ef7987d 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -388,7 +388,7 @@ def delete(self, request): account, "delete", "Secrets", - secret.env.app.organisation, + secret.environment.app.organisation, True, request.auth["service_account"] is not None, ): From 2bb3c27240793f0623502d7f04b197f2720127bd Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 30 Oct 2024 14:45:31 +0530 Subject: [PATCH 29/92] feat: manage service account handler updates when updating, assigning roles --- ...r_serviceaccounthandler_service_account.py | 19 +++ .../0090_alter_serviceaccount_organisation.py | 19 +++ backend/api/models.py | 8 +- .../graphene/mutations/service_accounts.py | 26 ++-- frontend/apollo/gql.ts | 5 + frontend/apollo/graphql.ts | 14 +- frontend/apollo/schema.graphql | 5 +- frontend/app/[team]/access/members/page.tsx | 120 +++++++++++------- .../CreateServiceAccountTokenDialog.tsx | 11 +- .../CreateServiceAccountDialog.tsx | 7 +- .../components/access/ManageRoleDialog.tsx | 52 +++++--- .../service-accounts/updateHandlerKeys.gql | 5 + .../getServiceAccountHandlers.gql | 1 + .../service-accounts/getServiceAccounts.gql | 1 + frontend/utils/crypto/service-accounts.ts | 91 ++++++++++++- 15 files changed, 297 insertions(+), 87 deletions(-) create mode 100644 backend/api/migrations/0089_alter_serviceaccounthandler_service_account.py create mode 100644 backend/api/migrations/0090_alter_serviceaccount_organisation.py create mode 100644 frontend/graphql/mutations/service-accounts/updateHandlerKeys.gql diff --git a/backend/api/migrations/0089_alter_serviceaccounthandler_service_account.py b/backend/api/migrations/0089_alter_serviceaccounthandler_service_account.py new file mode 100644 index 00000000..c5b7e28d --- /dev/null +++ b/backend/api/migrations/0089_alter_serviceaccounthandler_service_account.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.15 on 2024-10-30 07:10 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0088_secretevent_service_account'), + ] + + operations = [ + migrations.AlterField( + model_name='serviceaccounthandler', + name='service_account', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='handlers', to='api.serviceaccount'), + ), + ] diff --git a/backend/api/migrations/0090_alter_serviceaccount_organisation.py b/backend/api/migrations/0090_alter_serviceaccount_organisation.py new file mode 100644 index 00000000..6c0911ac --- /dev/null +++ b/backend/api/migrations/0090_alter_serviceaccount_organisation.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.15 on 2024-10-30 07:11 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0089_alter_serviceaccounthandler_service_account'), + ] + + operations = [ + migrations.AlterField( + model_name='serviceaccount', + name='organisation', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='service_accounts', to='api.organisation'), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 0786d298..3a9e0f8a 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -218,7 +218,9 @@ def delete(self, *args, **kwargs): class ServiceAccount(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) name = models.CharField(max_length=255) - organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) + organisation = models.ForeignKey( + Organisation, on_delete=models.CASCADE, related_name="service_accounts" + ) role = models.ForeignKey( Role, on_delete=models.SET_NULL, @@ -236,7 +238,9 @@ class ServiceAccount(models.Model): class ServiceAccountHandler(models.Model): id = models.TextField(default=uuid4, primary_key=True) - service_account = models.ForeignKey(ServiceAccount, on_delete=models.CASCADE) + service_account = models.ForeignKey( + ServiceAccount, on_delete=models.CASCADE, related_name="handlers" + ) user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE) wrapped_keyring = models.TextField() wrapped_recovery = models.TextField() diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 4be3ac96..4493949e 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -8,12 +8,13 @@ ServiceAccountHandler, ServiceAccountToken, ) -from api.utils.access.permissions import user_has_permission +from api.utils.access.permissions import user_has_permission, user_is_org_member from backend.graphene.types import ServiceAccountTokenType, ServiceAccountType from datetime import datetime class ServiceAccountHandlerInput(graphene.InputObjectType): + service_account_id = graphene.ID(required=False) member_id = graphene.ID(required=False) wrapped_keyring = graphene.String(required=True) wrapped_recovery = graphene.String(required=True) @@ -141,24 +142,27 @@ def mutate(cls, root, info, service_account_id, name, role_id): class UpdateServiceAccountHandlersMutation(graphene.Mutation): class Arguments: - service_account_id = graphene.ID() + organisation_id = graphene.ID() handlers = graphene.List(ServiceAccountHandlerInput) - service_account = graphene.Field(ServiceAccountType) + ok = graphene.Boolean() @classmethod - def mutate(cls, root, info, service_account_id, handlers): + def mutate(cls, root, info, organisation_id, handlers): user = info.context.user - service_account = ServiceAccount.objects.get(id=service_account_id) + org = Organisation.objects.get(id=organisation_id) - if not user_has_permission( - user, "update", "ServiceAccounts", service_account.organisation - ): + if not user_is_org_member(user.userId, organisation_id): raise GraphQLError( - "You don't have the permissions required to update Service Accounts in this organisation" + "You are not a member of this organisation and cannot perform this operation" ) + for account in org.service_accounts.all(): + [handler.delete() for handler in account.handlers.all()] + for handler in handlers: + service_account = ServiceAccount.objects.get(id=handler.service_account_id) + if not ServiceAccountHandler.objects.filter( service_account=service_account, user_id=handler.member_id ).exists(): @@ -169,9 +173,7 @@ def mutate(cls, root, info, service_account_id, handlers): wrapped_recovery=handler.wrapped_recovery, ) - return EnableServiceAccountThirdPartyAuthMutation( - service_account=service_account - ) + return UpdateServiceAccountHandlersMutation(ok=True) class DeleteServiceAccountMutation(graphene.Mutation): diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 2f1c7b16..1a1bdda9 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -54,6 +54,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 UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}": types.UpdateServiceAccountHandlerKeysDocument, "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, "mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument, "mutation DeleteProviderCreds($credentialId: ID!) {\n deleteProviderCredentials(credentialId: $credentialId) {\n ok\n }\n}": types.DeleteProviderCredsDocument, @@ -294,6 +295,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 UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}"): (typeof documents)["mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\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 989fdd13..4777f5bc 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1064,7 +1064,7 @@ export type MutationUpdateProviderCredentialsArgs = { export type MutationUpdateServiceAccountHandlersArgs = { handlers?: InputMaybe>>; - serviceAccountId?: InputMaybe; + organisationId?: InputMaybe; }; @@ -1595,6 +1595,7 @@ export type ServerEnvironmentKeyType = { export type ServiceAccountHandlerInput = { memberId?: InputMaybe; + serviceAccountId?: InputMaybe; wrappedKeyring: Scalars['String']['input']; wrappedRecovery: Scalars['String']['input']; }; @@ -1717,7 +1718,7 @@ export type UpdateProviderCredentials = { export type UpdateServiceAccountHandlersMutation = { __typename?: 'UpdateServiceAccountHandlersMutation'; - serviceAccount?: Maybe; + ok?: Maybe; }; export type UpdateSyncAuthentication = { @@ -2110,6 +2111,14 @@ export type DeleteServiceAccountTokenOpMutationVariables = Exact<{ export type DeleteServiceAccountTokenOpMutation = { __typename?: 'Mutation', deleteServiceAccountToken?: { __typename?: 'DeleteServiceAccountTokenMutation', ok?: boolean | null } | null }; +export type UpdateServiceAccountHandlerKeysMutationVariables = Exact<{ + orgId: Scalars['ID']['input']; + handlers?: InputMaybe> | InputMaybe>; +}>; + + +export type UpdateServiceAccountHandlerKeysMutation = { __typename?: 'Mutation', updateServiceAccountHandlers?: { __typename?: 'UpdateServiceAccountHandlersMutation', ok?: boolean | null } | null }; + export type CreateNewAwsSecretsSyncMutationVariables = Exact<{ envId: Scalars['ID']['input']; path: Scalars['String']['input']; @@ -2622,6 +2631,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 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 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; export const CreateNewCfPagesSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfPagesSync"},"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":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflarePagesSync"},"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":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"deploymentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectEnv"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}}},{"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":"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":"id"}},{"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; export const DeleteProviderCredsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteProviderCreds"},"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":"deleteProviderCredentials"},"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":"ok"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index d5cc1c42..3a961f78 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -659,7 +659,7 @@ type Mutation { deleteCustomRole(id: ID!): DeleteCustomRoleMutation createServiceAccount(handlers: [ServiceAccountHandlerInput], identityKey: String, name: String, organisationId: ID, roleId: ID, serverWrappedKeyring: String, serverWrappedRecovery: String): CreateServiceAccountMutation enableServiceAccountThirdPartyAuth(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountThirdPartyAuthMutation - updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], serviceAccountId: ID): UpdateServiceAccountHandlersMutation + updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], organisationId: ID): UpdateServiceAccountHandlersMutation deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation createServiceAccountToken(expiry: BigInt, identityKey: String!, name: String!, serviceAccountId: ID, token: String!, wrappedKeyShare: String!): CreateServiceAccountTokenMutation deleteServiceAccountToken(tokenId: ID): DeleteServiceAccountTokenMutation @@ -808,6 +808,7 @@ type CreateServiceAccountMutation { } input ServiceAccountHandlerInput { + serviceAccountId: ID memberId: ID wrappedKeyring: String! wrappedRecovery: String! @@ -818,7 +819,7 @@ type EnableServiceAccountThirdPartyAuthMutation { } type UpdateServiceAccountHandlersMutation { - serviceAccount: ServiceAccountType + ok: Boolean } type DeleteServiceAccountMutation { diff --git a/frontend/app/[team]/access/members/page.tsx b/frontend/app/[team]/access/members/page.tsx index 4a17bef9..d7639739 100644 --- a/frontend/app/[team]/access/members/page.tsx +++ b/frontend/app/[team]/access/members/page.tsx @@ -40,7 +40,7 @@ import clsx from 'clsx' import { copyToClipBoard } from '@/utils/clipboard' import { toast } from 'react-toastify' import { Avatar } from '@/components/common/Avatar' -import { userHasGlobalAccess, userIsAdmin } from '@/utils/access/permissions' +import { PermissionPolicy, userHasGlobalAccess, userIsAdmin } from '@/utils/access/permissions' import { RoleLabel } from '@/components/users/RoleLabel' import { KeyringContext } from '@/contexts/keyringContext' @@ -54,6 +54,7 @@ import { useSearchParams } from 'next/navigation' import { userHasPermission } from '@/utils/access/permissions' import { EmptyState } from '@/components/common/EmptyState' import Spinner from '@/components/common/Spinner' +import { updateServiceAccountHandlers } from '@/utils/crypto/service-accounts' const handleCopy = (val: string) => { copyToClipBoard(val) @@ -97,24 +98,28 @@ const RoleSelector = (props: { member: OrganisationMemberType }) => { /** * Handles the assignment of a user to a global access role. - * Env keys for all apps, are fetched and decrypted by the active user, then each key is re-encrypted for the new user and saved on the backend via the addMemberToApp mutation + * Env keys for all apps are fetched and decrypted by the active user, + * then each key is re-encrypted for the new user and saved on the backend via the addMemberToApp mutation. * - * @returns {void} + * @returns {Promise} */ - const assignGlobalAccess = () => { - if (appsData) { - const apps = appsData.apps + const assignGlobalAccess = async (): Promise => { + if (!appsData) { + return Promise.reject(new Error('No apps data available')) + } - // Function to process an individual app - const processApp = async (app: AppType) => { - // fetch envs for the app - const { data: appEnvsData } = await getAppEnvs({ variables: { appId: app.id } }) + const apps = appsData.apps + // Function to process an individual app + const processApp = async (app: AppType) => { + try { + // Fetch envs for the app + const { data: appEnvsData } = await getAppEnvs({ variables: { appId: app.id } }) const appEnvironments = appEnvsData.appEnvironments as EnvironmentType[] - // construct promises to encrypt each env key for the target user + // Construct promises to encrypt each env key for the target user const envKeyPromises = appEnvironments.map(async (env: EnvironmentType) => { - // fetch the current wrapped key for the environment + // Fetch the current wrapped key for the environment const { data } = await getEnvKey({ variables: { envId: env.id, @@ -128,20 +133,20 @@ const RoleSelector = (props: { member: OrganisationMemberType }) => { identityKey, } = data.environmentKeys[0] - // unwrap env keys for current logged in user + // Unwrap env keys for current logged in user const { seed, salt } = await unwrapEnvSecretsForUser( userWrappedSeed, userWrappedSalt, keyring! ) - // re-encrypt the env key for the target user + // Re-encrypt the env key for the target user const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForAccount( { seed, salt }, member ) - // resolve the promise with the mutation payload + // Return the mutation payload return { envId: env.id, userId: member.id, @@ -151,40 +156,43 @@ const RoleSelector = (props: { member: OrganisationMemberType }) => { } }) - // get mutation payloads with wrapped keys for each environment + // Get mutation payloads with wrapped keys for each environment const envKeyInputs = await Promise.all(envKeyPromises) - // add the user to this app, with wrapped keys for each environment + // Add the user to this app, with wrapped keys for each environment await addMemberToApp({ variables: { memberId: member.id, appId: app.id, envKeys: envKeyInputs }, }) + } catch (error) { + console.error(`Error processing app ${app.id}:`, error) + throw error // Propagate the error to be caught later } + } - // Process each app sequentially - const processAppsSequentially = async () => { - for (const app of apps) { - await processApp(app) - } + try { + // Process each app sequentially using for...of to ensure async operations complete in order + for (const app of apps) { + await processApp(app) } - // Call the function to process all apps sequentially - processAppsSequentially() - .then(async () => { - // All apps have been processed - const adminRole = roleOptions.find( - (option: RoleType) => option.name?.toLowerCase() === 'admin' - ) - await updateRole({ - variables: { - memberId: member.id, - roleId: adminRole.id, - }, - }) - toast.success('Updated member role', { autoClose: 2000 }) - }) - .catch((error) => { - console.error('Error processing apps:', error) + // After all apps have been processed, assign the global admin role + const adminRole = roleOptions.find( + (option: RoleType) => option.name?.toLowerCase() === 'admin' + ) + + if (adminRole) { + await updateRole({ + variables: { + memberId: member.id, + roleId: adminRole.id, + }, }) + } else { + throw new Error('Admin role not found') + } + } catch (error) { + console.error('Error assigning global access:', error) + throw error // Ensure the promise rejects if any error occurs } } @@ -192,6 +200,9 @@ const RoleSelector = (props: { member: OrganisationMemberType }) => { const newRoleHasGlobalAccess = userHasGlobalAccess(newRole.permissions) const currentUserHasGlobalAccess = userHasGlobalAccess(organisation?.role?.permissions) + const newRolePolicy: PermissionPolicy = JSON.parse(newRole.permissions) + const newRoleHasServiceAccountAccess = newRolePolicy.permissions['ServiceAccounts'].length > 0 + if (newRoleHasGlobalAccess && !currentUserHasGlobalAccess) { toast.error('You cannot assign users to this role as it requires global access!', { autoClose: 5000, @@ -209,16 +220,31 @@ const RoleSelector = (props: { member: OrganisationMemberType }) => { setRole(newRole) - if (newRoleHasGlobalAccess) assignGlobalAccess() - else { - await updateRole({ - variables: { - memberId: member.id, - roleId: newRole.id, - }, + const processUpdate = async () => { + return new Promise(async (resolve, reject) => { + try { + if (newRoleHasGlobalAccess) await assignGlobalAccess() + else { + await updateRole({ + variables: { + memberId: member.id, + roleId: newRole.id, + }, + }) + } + //if (newRoleHasServiceAccountAccess) + await updateServiceAccountHandlers(organisation!.id, keyring!) + resolve(true) + } catch (error) { + reject(error) + } }) - toast.success('Updated member role', { autoClose: 2000 }) } + await toast.promise(processUpdate, { + pending: 'Updating role...', + success: 'Updated role!', + error: 'Something went wrong!', + }) } const roleOptions = roleData?.roles.filter((option: RoleType) => option.name !== 'Owner') || [] diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx index c53c6b57..a901da3f 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx @@ -7,7 +7,7 @@ import { humanReadableExpiry, tokenExpiryOptions, } from '@/utils/tokens' -import { Fragment, useContext, useRef, useState } from 'react' +import { Fragment, useContext, useEffect, useRef, useState } from 'react' import { FaCheckCircle, FaCircle, FaExternalLinkSquareAlt, FaPlus } from 'react-icons/fa' import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' import { CreateSAToken } from '@/graphql/mutations/service-accounts/createServiceAccountToken.gql' @@ -21,7 +21,7 @@ import { import { useMutation } from '@apollo/client' import { toast } from 'react-toastify' import { KeyringContext } from '@/contexts/keyringContext' -import { generateSAToken } from '@/utils/crypto/service-accounts' +import { generateSAToken, updateServiceAccountHandlers } from '@/utils/crypto/service-accounts' import { Alert } from '@/components/common/Alert' import CopyButton from '@/components/common/CopyButton' import { CliCommand } from '@/components/dashboard/CliCommand' @@ -29,6 +29,7 @@ import { getApiHost } from '@/utils/appConfig' import { Tab, RadioGroup } from '@headlessui/react' import clsx from 'clsx' import Link from 'next/link' +import { log } from 'console' export const CreateServiceAccountTokenDialog = ({ serviceAccount, @@ -38,6 +39,12 @@ export const CreateServiceAccountTokenDialog = ({ const { activeOrganisation: organisation } = useContext(organisationContext) const { keyring } = useContext(KeyringContext) + useEffect(() => { + if (organisation && keyring) { + updateServiceAccountHandlers(organisation.id, keyring) + } + }, [organisation, keyring]) + const serviceAccountHandler = serviceAccount.handlers?.find( (handler) => handler?.user.self === true ) diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index 0f2b9be0..66bed738 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -1,6 +1,6 @@ import { OrganisationMemberType, RoleType } from '@/apollo/graphql' import GenericDialog from '@/components/common/GenericDialog' -import { Fragment, useContext, useRef, useState } from 'react' +import { Fragment, useContext, useEffect, useRef, useState } from 'react' import { FaChevronDown, FaPlus } from 'react-icons/fa' import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' import { GetServiceAccountHandlers } from '@/graphql/queries/service-accounts/getServiceAccountHandlers.gql' @@ -21,7 +21,6 @@ import { Listbox } from '@headlessui/react' import clsx from 'clsx' import { ToggleSwitch } from '@/components/common/ToggleSwitch' import { Button } from '@/components/common/Button' -import { identity } from 'lodash' import { toast } from 'react-toastify' const bip39 = require('bip39') @@ -179,12 +178,12 @@ export const CreateServiceAccountDialog = () => { )}
-
+ {/*
setThirdParty(!thirdParty)} /> -
+
*/}
+
+ )} + +
diff --git a/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx b/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx index 5a16d96d..6df92ffc 100644 --- a/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx @@ -13,8 +13,11 @@ import { GetApps } from '@/graphql/queries/getApps.gql' import { GetRoles } from '@/graphql/queries/organisation/getRoles.gql' import { UpdateServiceAccountOp } from '@/graphql/mutations/service-accounts/updateServiceAccount.gql' -export const ServiceAccountRoleSelector = (props: { account: ServiceAccountType }) => { - const { account } = props +export const ServiceAccountRoleSelector = (props: { + account: ServiceAccountType + displayOnly?: boolean +}) => { + const { account, displayOnly } = props const { activeOrganisation: organisation } = useContext(organisationContext) const { keyring } = useContext(KeyringContext) @@ -74,7 +77,7 @@ export const ServiceAccountRoleSelector = (props: { account: ServiceAccountType (option: RoleType) => option.name !== 'Owner' && option.name !== 'Admin' ) || [] - const disabled = isOwner || !userCanUpdateAccountRoles + const disabled = isOwner || !userCanUpdateAccountRoles || displayOnly if (roleDataPending) return <> diff --git a/frontend/app/[team]/access/service-accounts/page.tsx b/frontend/app/[team]/access/service-accounts/page.tsx index 3fa34472..64eed872 100644 --- a/frontend/app/[team]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/access/service-accounts/page.tsx @@ -73,7 +73,7 @@ export default function ServiceAccounts({ params }: { params: { team: string } } - + diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index 69687db4..ba511b2d 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -6,6 +6,7 @@ query GetServiceAccounts($orgId: ID!, $id: ID) { role { id name + description permissions } createdAt From d144303d6e8deb762f7ec0a293c90250a6c38524 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 12:08:48 +0530 Subject: [PATCH 42/92] feat: add link to account tokens to app access --- .../[app]/access/service-accounts/page.tsx | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx index 86fc7481..0f4d4040 100644 --- a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx @@ -1,25 +1,17 @@ 'use client' import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql' - import AddMemberToApp from '@/graphql/mutations/apps/addAppMember.gql' import RemoveMemberFromApp from '@/graphql/mutations/apps/removeAppMember.gql' import UpdateEnvScope from '@/graphql/mutations/apps/updateEnvScope.gql' - import { GetAppServiceAccounts } from '@/graphql/queries/apps/getAppServiceAccounts.gql' import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' import { GetEnvironmentKey } from '@/graphql/queries/secrets/getEnvironmentKey.gql' import { useLazyQuery, useMutation, useQuery } from '@apollo/client' import { Fragment, useContext, useEffect, useMemo, useState } from 'react' -import { - EnvironmentType, - ServiceAccountType, - MemberType, - OrganisationMemberType, -} from '@/apollo/graphql' +import { EnvironmentType, ServiceAccountType, MemberType } from '@/apollo/graphql' import { Button } from '@/components/common/Button' import { organisationContext } from '@/contexts/organisationContext' -import { relativeTimeFromDates } from '@/utils/time' import { Combobox, Dialog, Listbox, Transition } from '@headlessui/react' import { FaBan, @@ -31,22 +23,18 @@ import { FaSquare, FaTimes, FaTrash, - FaUserCog, - FaUserTimes, } from 'react-icons/fa' import clsx from 'clsx' import { toast } from 'react-toastify' import { useSession } from 'next-auth/react' -import { Avatar } from '@/components/common/Avatar' import { KeyringContext } from '@/contexts/keyringContext' -import { userHasGlobalAccess, userHasPermission, userIsAdmin } from '@/utils/access/permissions' +import { userHasGlobalAccess, userHasPermission } from '@/utils/access/permissions' import { RoleLabel } from '@/components/users/RoleLabel' import { Alert } from '@/components/common/Alert' import Link from 'next/link' import { unwrapEnvSecretsForUser, wrapEnvSecretsForAccount } from '@/utils/crypto' import { EmptyState } from '@/components/common/EmptyState' import Spinner from '@/components/common/Spinner' -import loading from '@/app/loading' export default function ServiceAccounts({ params }: { params: { team: string; app: string } }) { const { keyring } = useContext(KeyringContext) @@ -759,7 +747,7 @@ export default function ServiceAccounts({ params }: { params: { team: string; ap <>
-
+
+ + + +
+ +
From 974e1f34338f3aabe1f31892aa5bccf6377a7c63 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 14:33:43 +0530 Subject: [PATCH 43/92] fix: enforce permissions for account property updates --- .../[team]/access/service-accounts/[account]/page.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 19634dac..7c5c36c8 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -34,6 +34,10 @@ export default function ServiceAccount({ params }: { params: { team: string; acc ? userHasPermission(organisation.role?.permissions, 'ServiceAccountTokens', 'read') : false + const userCanUpdateSA = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'update') + : false + const userCanDeleteSA = organisation ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'delete') : false @@ -50,6 +54,9 @@ export default function ServiceAccount({ params }: { params: { team: string; acc const nameUpdated = account ? account.name !== name : false const updateName = async () => { + if (!userCanUpdateSA) { + toast.error("You don't have the permissions requried to update Service Accounts") + } await updateAccount({ variables: { serviceAccountId: account.id, @@ -126,7 +133,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
{' '}

- + {nameUpdated && (

- +
From 642a0024f85f79d8f399c3ba2265f1373f6019e8 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 14:34:02 +0530 Subject: [PATCH 44/92] fix: reset create account dialog after success --- .../_components/CreateServiceAccountDialog.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index 79b7f260..c709ca2d 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -49,6 +49,11 @@ export const CreateServiceAccountDialog = () => { const [thirdParty, setThirdParty] = useState(false) const [createPending, setCreatePending] = useState(false) + const reset = () => { + setName('') + setThirdParty(false) + } + const roleOptions = roleData?.roles.filter( (option: RoleType) => option.name !== 'Owner' && option.name !== 'Admin' @@ -123,6 +128,7 @@ export const CreateServiceAccountDialog = () => { }) setCreatePending(false) + reset() if (dialogRef.current) dialogRef.current.closeModal() From f0a6d4d8616bb2eddcf00607754d84aa12b39fe7 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 14:34:32 +0530 Subject: [PATCH 45/92] feat: add quota logic for service accounts --- .../graphene/mutations/service_accounts.py | 6 +++ backend/backend/graphene/types.py | 5 +++ backend/backend/quotas.py | 31 +++++++++++++ frontend/apollo/gql.ts | 17 +++++--- frontend/apollo/graphql.ts | 36 +++++++++++++--- frontend/apollo/schema.graphql | 1 + .../[team]/access/service-accounts/page.tsx | 2 +- .../settings/organisation/PlanInfo.tsx | 43 ++++++++++++++++++- frontend/graphql/queries/getOrganisations.gql | 1 + .../organisation/getOrganisationPlan.gql | 1 + frontend/utils/crypto/service-accounts.ts | 4 +- 11 files changed, 130 insertions(+), 17 deletions(-) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 025a1383..9bad68aa 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -1,3 +1,4 @@ +from backend.quotas import can_add_service_account import graphene from graphql import GraphQLError from api.models import ( @@ -53,6 +54,11 @@ def mutate( "You don't have the permissions required to create Service Accounts in this organisation" ) + if not can_add_service_account(org): + raise GraphQLError( + "You cannot add any more service accounts to this organisation" + ) + if handlers is None or len(handlers) == 0: raise GraphQLError("At least one service account handler must be provided") diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 44f3c7e0..500405b5 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -45,6 +45,7 @@ class OrganisationPlanType(ObjectType): max_apps = graphene.Int() max_envs_per_app = graphene.Int() user_count = graphene.Int() + service_account_count = graphene.Int() app_count = graphene.Int() @@ -134,6 +135,10 @@ def resolve_plan_detail(self, info): ).count() ) + plan["service_account_count"] = ServiceAccount.objects.filter( + organisation=self, deleted_at=None + ).count() + plan["app_count"] = App.objects.filter( organisation=self, deleted_at=None ).count() diff --git a/backend/backend/quotas.py b/backend/backend/quotas.py index afd36289..3642b4dc 100644 --- a/backend/backend/quotas.py +++ b/backend/backend/quotas.py @@ -91,6 +91,37 @@ def can_add_user(organisation): return current_user_count < user_limit +def can_add_service_account(organisation): + """Check if a new service account can be added to the organisation.""" + + ServiceAccount = apps.get_model("api", "ServiceAccount") + ActivatedPhaseLicense = apps.get_model("api", "ActivatedPhaseLicense") + + plan_limits = PLAN_CONFIG[organisation.plan] + license_exists = ActivatedPhaseLicense.objects.filter( + organisation=organisation + ).exists() + + current_account_count = ServiceAccount.objects.filter( + organisation=organisation, deleted_at=None + ).count() + + if license_exists: + license = ( + ActivatedPhaseLicense.objects.filter(organisation=organisation) + .order_by("-activated_at") + .first() + ) + user_limit = license.seats + + else: + user_limit = plan_limits["max_users"] + + if user_limit is None: + return True + return current_account_count < user_limit + + def can_add_environment(app): """Check if a new environment can be added to the app.""" diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 1a1bdda9..192f6da6 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -55,6 +55,7 @@ const documents = { "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 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 }\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, "mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument, "mutation DeleteProviderCreds($credentialId: ID!) {\n deleteProviderCredentials(credentialId: $credentialId) {\n ok\n }\n}": types.DeleteProviderCredsDocument, @@ -80,7 +81,7 @@ const documents = { "query GetAppKmsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n kms {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n }\n kmsLogsCount(appId: $appId)\n}": types.GetAppKmsLogsDocument, "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n envType\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.GetAppsDocument, "query GetDashboard($organisationId: ID!) {\n apps(organisationId: $organisationId) {\n id\n sseEnabled\n }\n userTokens(organisationId: $organisationId) {\n id\n }\n organisationInvites(orgId: $organisationId) {\n id\n }\n organisationMembers(organisationId: $organisationId, role: null) {\n id\n }\n savedCredentials(orgId: $organisationId) {\n id\n }\n syncs(orgId: $organisationId) {\n id\n }\n}": types.GetDashboardDocument, - "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}": types.GetOrganisationsDocument, + "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}": types.GetOrganisationsDocument, "query CheckOrganisationNameAvailability($name: String!) {\n organisationNameAvailable(name: $name)\n}": types.CheckOrganisationNameAvailabilityDocument, "query GetGlobalAccessUsers($organisationId: ID!) {\n organisationGlobalAccessUsers(organisationId: $organisationId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetGlobalAccessUsersDocument, "query GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n fullName\n self\n }\n inviteeEmail\n }\n}": types.GetInvitesDocument, @@ -99,8 +100,8 @@ 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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, - "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}": types.GetServiceAccountsDocument, + "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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\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 }\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, "query GetCfPages($credentialId: ID!) {\n cloudflarePagesProjects(credentialId: $credentialId) {\n name\n deploymentId\n environments\n }\n}": types.GetCfPagesDocument, @@ -299,6 +300,10 @@ 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 UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}"): (typeof documents)["mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\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 UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n }\n }\n}"): (typeof documents)["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 }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -402,7 +407,7 @@ export function graphql(source: "query GetDashboard($organisationId: ID!) {\n a /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"]; +export function graphql(source: "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -478,11 +483,11 @@ 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 GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}"): (typeof documents)["query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}"]; +export function graphql(source: "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}"): (typeof documents)["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}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}"]; +export function graphql(source: "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\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 4777f5bc..5c291e9b 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -642,6 +642,7 @@ export type Mutation = { updateMemberWrappedSecrets?: Maybe; updateOrganisationMemberRole?: Maybe; updateProviderCredentials?: Maybe; + updateServiceAccount?: Maybe; updateServiceAccountHandlers?: Maybe; updateSyncAuthentication?: Maybe; }; @@ -1062,6 +1063,13 @@ export type MutationUpdateProviderCredentialsArgs = { }; +export type MutationUpdateServiceAccountArgs = { + name?: InputMaybe; + roleId?: InputMaybe; + serviceAccountId?: InputMaybe; +}; + + export type MutationUpdateServiceAccountHandlersArgs = { handlers?: InputMaybe>>; organisationId?: InputMaybe; @@ -1122,6 +1130,7 @@ export type OrganisationPlanType = { maxEnvsPerApp?: Maybe; maxUsers?: Maybe; name?: Maybe; + serviceAccountCount?: Maybe; userCount?: Maybe; }; @@ -1721,6 +1730,11 @@ export type UpdateServiceAccountHandlersMutation = { ok?: Maybe; }; +export type UpdateServiceAccountMutation = { + __typename?: 'UpdateServiceAccountMutation'; + serviceAccount?: Maybe; +}; + export type UpdateSyncAuthentication = { __typename?: 'UpdateSyncAuthentication'; sync?: Maybe; @@ -2119,6 +2133,15 @@ export type UpdateServiceAccountHandlerKeysMutationVariables = Exact<{ export type UpdateServiceAccountHandlerKeysMutation = { __typename?: 'Mutation', updateServiceAccountHandlers?: { __typename?: 'UpdateServiceAccountHandlersMutation', ok?: boolean | null } | null }; +export type UpdateServiceAccountOpMutationVariables = Exact<{ + serviceAccountId: Scalars['ID']['input']; + name: Scalars['String']['input']; + roleId: Scalars['ID']['input']; +}>; + + +export type UpdateServiceAccountOpMutation = { __typename?: 'Mutation', updateServiceAccount?: { __typename?: 'UpdateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string } | null } | null }; + export type CreateNewAwsSecretsSyncMutationVariables = Exact<{ envId: Scalars['ID']['input']; path: Scalars['String']['input']; @@ -2347,7 +2370,7 @@ export type GetDashboardQuery = { __typename?: 'Query', apps?: Array<{ __typenam export type GetOrganisationsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetOrganisationsQuery = { __typename?: 'Query', organisations?: Array<{ __typename?: 'OrganisationType', id: string, name: string, identityKey: string, createdAt?: any | null, plan: ApiOrganisationPlanChoices, memberId?: string | null, keyring?: string | null, recovery?: string | null, planDetail?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, userCount?: number | null, appCount?: number | null } | null, role?: { __typename?: 'RoleType', name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null } | null> | null }; +export type GetOrganisationsQuery = { __typename?: 'Query', organisations?: Array<{ __typename?: 'OrganisationType', id: string, name: string, identityKey: string, createdAt?: any | null, plan: ApiOrganisationPlanChoices, memberId?: string | null, keyring?: string | null, recovery?: string | null, planDetail?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, userCount?: number | null, serviceAccountCount?: number | null, appCount?: number | null } | null, role?: { __typename?: 'RoleType', name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null } | null> | null }; export type CheckOrganisationNameAvailabilityQueryVariables = Exact<{ name: Scalars['String']['input']; @@ -2487,7 +2510,7 @@ export type GetServiceAccountHandlersQueryVariables = Exact<{ }>; -export type GetServiceAccountHandlersQuery = { __typename?: 'Query', serviceAccountHandlers?: Array<{ __typename?: 'OrganisationMemberType', id: string, identityKey?: string | null, self?: boolean | null, role?: { __typename?: 'RoleType', name?: string | null, permissions?: any | null } | null } | null> | null }; +export type GetServiceAccountHandlersQuery = { __typename?: 'Query', serviceAccountHandlers?: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, identityKey?: string | null, self?: boolean | null, role?: { __typename?: 'RoleType', name?: string | null, permissions?: any | null } | null } | null> | null }; export type GetServiceAccountsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2495,7 +2518,7 @@ export type GetServiceAccountsQueryVariables = Exact<{ }>; -export type GetServiceAccountsQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null } | null> | null }; +export type GetServiceAccountsQuery = { __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, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null } | null> | null }; export type GetOrganisationSyncsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2632,6 +2655,7 @@ export const CreateSaTokenDocument = {"kind":"Document","definitions":[{"kind":" 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 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"}}]}}]}}]}}]} 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; export const CreateNewCfPagesSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfPagesSync"},"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":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflarePagesSync"},"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":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"deploymentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectEnv"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}}},{"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":"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":"id"}},{"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; export const DeleteProviderCredsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteProviderCreds"},"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":"deleteProviderCredentials"},"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":"ok"}}]}}]}}]} as unknown as DocumentNode; @@ -2657,7 +2681,7 @@ export const GetAppDetailDocument = {"kind":"Document","definitions":[{"kind":"O export const GetAppKmsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppKmsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"kms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"phaseNode"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"city"}},{"kind":"Field","name":{"kind":"Name","value":"phSize"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"kmsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]}]}}]} as unknown as DocumentNode; export const GetAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetApps"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"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":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"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":"envType"}},{"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 GetDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"userTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationInvites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationMembers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"savedCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; -export const GetOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisations"},"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":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"planDetail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"userCount"}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"memberId"}},{"kind":"Field","name":{"kind":"Name","value":"keyring"}},{"kind":"Field","name":{"kind":"Name","value":"recovery"}}]}}]}}]} as unknown as DocumentNode; +export const GetOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisations"},"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":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"planDetail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"userCount"}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccountCount"}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"memberId"}},{"kind":"Field","name":{"kind":"Name","value":"keyring"}},{"kind":"Field","name":{"kind":"Name","value":"recovery"}}]}}]}}]} as unknown as DocumentNode; export const CheckOrganisationNameAvailabilityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheckOrganisationNameAvailability"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationNameAvailable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}]}]}}]} as unknown as DocumentNode; export const GetGlobalAccessUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetGlobalAccessUsers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationGlobalAccessUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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 GetInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetInvites"},"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":"organisationInvites"},"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}}]}}]}}]} as unknown as DocumentNode; @@ -2676,8 +2700,8 @@ 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}}]}},{"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 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":"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 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":"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":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"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"}}]}}]}}]}}]}}]} 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 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":"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":"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"}}]}}]}}]}}]}}]} 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":"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; export const GetCfPagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCfPages"},"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":"cloudflarePagesProjects"},"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":"deploymentId"}},{"kind":"Field","name":{"kind":"Name","value":"environments"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index fd4bf8d4..1d738e07 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -99,6 +99,7 @@ type OrganisationPlanType { maxApps: Int maxEnvsPerApp: Int userCount: Int + serviceAccountCount: Int appCount: Int } diff --git a/frontend/app/[team]/access/service-accounts/page.tsx b/frontend/app/[team]/access/service-accounts/page.tsx index 64eed872..30c369b4 100644 --- a/frontend/app/[team]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/access/service-accounts/page.tsx @@ -65,7 +65,7 @@ export default function ServiceAccounts({ params }: { params: { team: string } } {data?.serviceAccounts.map((account: ServiceAccountType) => ( - +
diff --git a/frontend/components/settings/organisation/PlanInfo.tsx b/frontend/components/settings/organisation/PlanInfo.tsx index 5255ae46..f3d51686 100644 --- a/frontend/components/settings/organisation/PlanInfo.tsx +++ b/frontend/components/settings/organisation/PlanInfo.tsx @@ -8,7 +8,15 @@ import { PlanLabel } from './PlanLabel' import Spinner from '@/components/common/Spinner' import { calculatePercentage } from '@/utils/dataUnits' import { Button } from '@/components/common/Button' -import { FaCheckCircle, FaCube, FaCubes, FaTimesCircle, FaUser, FaUsersCog } from 'react-icons/fa' +import { + FaCheckCircle, + FaCog, + FaCube, + FaCubes, + FaTimesCircle, + FaUser, + FaUsersCog, +} from 'react-icons/fa' import Link from 'next/link' import { ActivatedPhaseLicenseType, ApiOrganisationPlanChoices } from '@/apollo/graphql' import { isCloudHosted } from '@/utils/appConfig' @@ -148,6 +156,13 @@ export const PlanInfo = () => { ) : 0 + const serviceAccountQuotaUsage = data + ? calculatePercentage( + data.organisationPlan.serviceAccountCount, + license()?.seats || data.organisationPlan.maxUsers + ) + : 0 + const progressBarColor = (value: number, maxValue: number) => value >= maxValue ? 'bg-red-500' : value === maxValue - 1 ? 'bg-amber-500' : 'bg-emerald-500' @@ -263,13 +278,37 @@ export const PlanInfo = () => { /> )}
- +
+ +
+
+
Service Accounts
+
{`${data.organisationPlan.serviceAccountCount} ${license()?.seats || data.organisationPlan.maxUsers ? `of ${license()?.seats || data.organisationPlan.maxUsers}` : ''} Seats used`}
+
+ {(activeOrganisation.plan === ApiOrganisationPlanChoices.Fr || license()?.seats) && ( + + )} +
+ + + +
+
{searchParams?.get('stripe_session_id') && ( diff --git a/frontend/graphql/queries/getOrganisations.gql b/frontend/graphql/queries/getOrganisations.gql index 54519634..7a168447 100644 --- a/frontend/graphql/queries/getOrganisations.gql +++ b/frontend/graphql/queries/getOrganisations.gql @@ -11,6 +11,7 @@ query GetOrganisations { maxApps maxEnvsPerApp userCount + serviceAccountCount appCount } role { diff --git a/frontend/graphql/queries/organisation/getOrganisationPlan.gql b/frontend/graphql/queries/organisation/getOrganisationPlan.gql index bee9c89b..f8f7e1fb 100644 --- a/frontend/graphql/queries/organisation/getOrganisationPlan.gql +++ b/frontend/graphql/queries/organisation/getOrganisationPlan.gql @@ -5,6 +5,7 @@ query GetOrganisationPlan($organisationId: ID!) { maxApps maxEnvsPerApp userCount + serviceAccountCount appCount } } diff --git a/frontend/utils/crypto/service-accounts.ts b/frontend/utils/crypto/service-accounts.ts index 967e9c0b..797479c5 100644 --- a/frontend/utils/crypto/service-accounts.ts +++ b/frontend/utils/crypto/service-accounts.ts @@ -59,11 +59,11 @@ export const generateSAToken = async ( export const updateServiceAccountHandlers = async (orgId: string, userKeyring: OrganisationKeyring) => { return new Promise(async (resolve, reject) => { // Fetch service accounts - const { data: serviceAccountsData } = await graphQlClient.query({ query: GetServiceAccounts, variables: { orgId}, fetchPolicy: 'network-only' }) + const { data: serviceAccountsData } = await graphQlClient.query({ query: GetServiceAccounts, variables: { orgId }, fetchPolicy: 'network-only' }) const serviceAccounts = serviceAccountsData?.serviceAccounts || [] // Fetch service account handlers - const { data: handlersData } = await graphQlClient.query({ query: GetServiceAccountHandlers, variables: { orgId}, fetchPolicy: 'network-only' }) + const { data: handlersData } = await graphQlClient.query({ query: GetServiceAccountHandlers, variables: { orgId }, fetchPolicy: 'network-only' }) const handlers = handlersData.serviceAccountHandlers // Current user kx keys From 4c4e971aed6654b3a69478f288bb2535580a4e97 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 14:39:43 +0530 Subject: [PATCH 46/92] fix: copy --- .../[team]/apps/[app]/access/service-accounts/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx index 0f4d4040..b2ccad45 100644 --- a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx @@ -243,13 +243,13 @@ export default function ServiceAccounts({ params }: { params: { team: string; ap

- All organisation members are added to this App. You can invite more - users from the{' '} + All Service Accounts have been added to this App. You can create more + Service Accounts from the{' '} - organisation members + organisation access {' '} page.

From 354a1fc114d491a671636441f1a7d2ce0f1be3ee Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 14:42:17 +0530 Subject: [PATCH 47/92] fix: update app settings danger zone style for consistency --- frontend/app/[team]/apps/[app]/settings/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/settings/page.tsx b/frontend/app/[team]/apps/[app]/settings/page.tsx index 1073937b..d65c4b3c 100644 --- a/frontend/app/[team]/apps/[app]/settings/page.tsx +++ b/frontend/app/[team]/apps/[app]/settings/page.tsx @@ -131,9 +131,9 @@ export default function AppSettings({ params }: { params: { team: string; app: s

Danger Zone

These actions may result in permanent loss of data

-
+
-

Delete App

+

Delete App

Permanently delete this App

From 5a8e2f51910fbeda77c3e85d2f128ed49684fb4c Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 15:08:29 +0530 Subject: [PATCH 48/92] feat: misc tweaks to tab ui --- frontend/app/[team]/access/layout.tsx | 8 +++++--- frontend/app/[team]/apps/[app]/access/layout.tsx | 6 +++--- .../app/[team]/apps/[app]/access/tokens/page.tsx | 4 ++-- frontend/app/[team]/apps/[app]/layout.tsx | 6 ++++-- frontend/components/layout/Sidebar.tsx | 16 ++++++++-------- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/frontend/app/[team]/access/layout.tsx b/frontend/app/[team]/access/layout.tsx index 51944cc3..37f86245 100644 --- a/frontend/app/[team]/access/layout.tsx +++ b/frontend/app/[team]/access/layout.tsx @@ -55,7 +55,7 @@ export default function AccessLayout({ return (
@@ -70,8 +70,10 @@ export default function AccessLayout({ {tab.name} diff --git a/frontend/app/[team]/apps/[app]/access/layout.tsx b/frontend/app/[team]/apps/[app]/access/layout.tsx index d268e1b5..9ca372b9 100644 --- a/frontend/app/[team]/apps/[app]/access/layout.tsx +++ b/frontend/app/[team]/apps/[app]/access/layout.tsx @@ -69,10 +69,10 @@ export default function AccessLayout({ {tab.name}{' '} diff --git a/frontend/app/[team]/apps/[app]/access/tokens/page.tsx b/frontend/app/[team]/apps/[app]/access/tokens/page.tsx index 797fb57b..5feace5e 100644 --- a/frontend/app/[team]/apps/[app]/access/tokens/page.tsx +++ b/frontend/app/[team]/apps/[app]/access/tokens/page.tsx @@ -259,7 +259,7 @@ export default function Tokens({ params }: { params: { team: string; app: string role="button" onClick={() => setActivePanel('secrets')} className={clsx( - 'p-4 cursor-pointer border-l transition ease -ml-px w-60', + 'p-4 cursor-pointer border-l rounded-r-lg transition ease -ml-px w-60', activePanel === 'secrets' ? 'bg-zinc-300 dark:bg-zinc-800 font-semibold border-emerald-500' : 'bg-zinc-200 dark:bg-zinc-900 hover:font-semibold border-neutral-500/40' @@ -272,7 +272,7 @@ export default function Tokens({ params }: { params: { team: string; app: string role="button" onClick={() => setActivePanel('kms')} className={clsx( - 'p-4 cursor-pointer border-l transition ease -ml-px w-60', + 'p-4 cursor-pointer border-l rounded-r-lg transition ease -ml-px w-60', activePanel === 'kms' ? 'bg-zinc-300 dark:bg-zinc-800 font-semibold border-emerald-500' : 'bg-zinc-200 dark:bg-zinc-900 hover:font-semibold border-neutral-500/40' diff --git a/frontend/app/[team]/apps/[app]/layout.tsx b/frontend/app/[team]/apps/[app]/layout.tsx index b9f528a9..e9229122 100644 --- a/frontend/app/[team]/apps/[app]/layout.tsx +++ b/frontend/app/[team]/apps/[app]/layout.tsx @@ -93,8 +93,10 @@ export default function AppLayout({ {tab.name} diff --git a/frontend/components/layout/Sidebar.tsx b/frontend/components/layout/Sidebar.tsx index e046e8bf..61bdda7c 100644 --- a/frontend/components/layout/Sidebar.tsx +++ b/frontend/components/layout/Sidebar.tsx @@ -35,10 +35,10 @@ const SidebarLink = (props: SidebarLinkT) => {
{icon}
@@ -148,31 +148,31 @@ const Sidebar = () => { { name: 'Home', href: `/${team}`, - icon: , + icon: , active: usePathname() === `/${team}`, }, { name: 'Apps', href: `/${team}/apps`, - icon: , + icon: , active: usePathname()?.split('/')[2] === 'apps', }, { name: 'Integrations', href: `/${team}/integrations`, - icon: , + icon: , active: usePathname() === `/${team}/integrations`, }, { name: 'Access Control', href: `/${team}/access/members`, - icon: , + icon: , active: usePathname()?.split('/')[2] === `access`, }, { name: 'Settings', href: `/${team}/settings`, - icon: , + icon: , active: usePathname() === `/${team}/settings`, }, ] From 7c7304cdf5814b4cc5d8e7086065850f453b8785 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 15:11:23 +0530 Subject: [PATCH 49/92] fix: command palette commands --- frontend/components/common/CommandPalette.tsx | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/frontend/components/common/CommandPalette.tsx b/frontend/components/common/CommandPalette.tsx index afb496a6..4e51c19d 100644 --- a/frontend/components/common/CommandPalette.tsx +++ b/frontend/components/common/CommandPalette.tsx @@ -14,6 +14,7 @@ import { FaMoon, FaPlus, FaProjectDiagram, + FaRobot, FaSearch, FaSun, FaUserPlus, @@ -96,6 +97,13 @@ const CommandPalette: React.FC = () => { icon: , action: () => handleNavigation(`/${activeOrganisation?.name}/access/members`), }, + { + id: 'go-members', + name: 'Go to Service Accounts', + description: 'Manage organization service accounts', + icon: , + action: () => handleNavigation(`/${activeOrganisation?.name}/access/service-accounts`), + }, { id: 'go-integrations', name: 'Go to Integrations', @@ -207,14 +215,24 @@ const CommandPalette: React.FC = () => { name: `Service tokens`, description: `Manage service tokens for ${app.name}`, icon: , - action: () => handleNavigation(`/${activeOrganisation?.name}/apps/${app.id}/tokens`), + action: () => + handleNavigation(`/${activeOrganisation?.name}/apps/${app.id}/access/tokens`), }, { id: `${app.id}-members`, name: `Members`, description: `Manage members in ${app.name}`, icon: , - action: () => handleNavigation(`/${activeOrganisation?.name}/apps/${app.id}/members`), + action: () => + handleNavigation(`/${activeOrganisation?.name}/apps/${app.id}/access/members`), + }, + { + id: `${app.id}-service-accounts`, + name: `Service Accounts`, + description: `Manage service accounts in ${app.name}`, + icon: , + action: () => + handleNavigation(`/${activeOrganisation?.name}/apps/${app.id}/access/service-accounts`), }, { id: `${app.id}-syncing`, From 4708a8d94f4075370a2a7fa72ba0ffb3454b1790 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 5 Nov 2024 19:22:56 +0530 Subject: [PATCH 50/92] fix: misc fixes and updates to navbar --- frontend/components/layout/Navbar.tsx | 44 ++++++++++++++++++++------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/frontend/components/layout/Navbar.tsx b/frontend/components/layout/Navbar.tsx index 6b210e42..84f21e59 100644 --- a/frontend/components/layout/Navbar.tsx +++ b/frontend/components/layout/Navbar.tsx @@ -27,6 +27,8 @@ export const NavBar = (props: { team: string }) => { }) const [getAppEnvs, { data: appEnvsData }] = useLazyQuery(GetAppEnvironments) + const orgContext = usePathname()?.split('/')[2] + const apps = appsData?.apps as AppType[] const envs: EnvironmentType[] = appEnvsData?.appEnvironments ?? [] @@ -37,7 +39,7 @@ export const NavBar = (props: { team: string }) => { const appPage = usePathname()?.split('/')[4] - const activeApp = apps?.find((app) => app.id === appId) + const activeApp = orgContext === 'apps' ? apps?.find((app) => app.id === appId) : undefined useEffect(() => { if (activeApp) getAppEnvs({ variables: { appId: activeApp.id } }) @@ -47,29 +49,42 @@ export const NavBar = (props: { team: string }) => { const activeEnv = activeApp ? envs.find((env) => env.id === envId) : undefined return ( -
+
- + / - {!activeApp && ({props.team})} - - {activeApp && ({props.team})} + + {props.team} + {activeApp && /} {activeApp && (appPage ? ( - {activeApp.name} + + {activeApp.name} + ) : ( - {activeApp.name} + + {activeApp.name} + ))} - {appPage && /} + {activeApp && appPage && /} - {appPage && ( + {activeApp && appPage && ( { {activeEnv && /} - {activeEnv && ({activeEnv.name})} + {activeEnv && ( + + {activeEnv.name} + + )} + + {!activeApp && orgContext && /} + {!activeApp && {orgContext}}
From ab8ce99c1029aa45eb06239f4a4fb785d6787f67 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 6 Nov 2024 14:13:22 +0530 Subject: [PATCH 51/92] fix: misc styling fixes --- frontend/app/[team]/access/layout.tsx | 2 +- frontend/components/layout/Sidebar.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/app/[team]/access/layout.tsx b/frontend/app/[team]/access/layout.tsx index 37f86245..4e922191 100644 --- a/frontend/app/[team]/access/layout.tsx +++ b/frontend/app/[team]/access/layout.tsx @@ -70,7 +70,7 @@ export default function AccessLayout({ {
{icon}
From 7f2043889a459c6ccb846000c6cd9eeacb25e71d Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 6 Nov 2024 14:32:38 +0530 Subject: [PATCH 52/92] feat: update stripe subscription count when adding or removing service accounts --- backend/api/utils/organisations.py | 5 ++++- .../backend/graphene/mutations/service_accounts.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/api/utils/organisations.py b/backend/api/utils/organisations.py index 444d2226..f4949ce3 100644 --- a/backend/api/utils/organisations.py +++ b/backend/api/utils/organisations.py @@ -1,4 +1,4 @@ -from api.models import OrganisationMember, OrganisationMemberInvite +from api.models import OrganisationMember, OrganisationMemberInvite, ServiceAccount from django.utils import timezone @@ -10,6 +10,9 @@ def get_organisation_seats(organisation): + OrganisationMemberInvite.objects.filter( organisation=organisation, valid=True, expires_at__gte=timezone.now() ).count() + + ServiceAccount.objects.filter( + organisation=organisation, deleted_at=None + ).count() ) return seats diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 9bad68aa..834c7d71 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -12,6 +12,7 @@ from api.utils.access.permissions import user_has_permission, user_is_org_member from backend.graphene.types import ServiceAccountTokenType, ServiceAccountType from datetime import datetime +from django.conf import settings class ServiceAccountHandlerInput(graphene.InputObjectType): @@ -79,6 +80,11 @@ def mutate( wrapped_recovery=handler.wrapped_recovery, ) + if settings.APP_HOST == "cloud": + from ee.billing.stripe import update_stripe_subscription_seats + + update_stripe_subscription_seats(org) + return CreateServiceAccountMutation(service_account=service_account) @@ -202,6 +208,11 @@ def mutate(cls, root, info, service_account_id): service_account.delete() + if settings.APP_HOST == "cloud": + from ee.billing.stripe import update_stripe_subscription_seats + + update_stripe_subscription_seats(service_account.organisation) + return DeleteServiceAccountMutation(ok=True) From 8f3655e3798363cb94fbd8be4aa35096d8f1b6e3 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 7 Nov 2024 12:08:51 +0530 Subject: [PATCH 53/92] feat: update acccount management ui --- .../service-accounts/[account]/page.tsx | 53 +++++++++++++------ .../_components/RoleSelector.tsx | 2 +- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 7c5c36c8..d594e276 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -9,7 +9,7 @@ import { relativeTimeFromDates } from '@/utils/time' import { useMutation, useQuery } from '@apollo/client' import Link from 'next/link' import { useContext, useEffect, useState } from 'react' -import { FaBan, FaChevronLeft, FaKey, FaRobot } from 'react-icons/fa' +import { FaBan, FaChevronLeft, FaEdit, FaKey, FaRobot } from 'react-icons/fa' import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog' import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog' import { ServiceAccountTokenType } from '@/apollo/graphql' @@ -17,7 +17,6 @@ import { Avatar } from '@/components/common/Avatar' import { EmptyState } from '@/components/common/EmptyState' import { DeleteServiceAccountTokenDialog } from './_components/DeleteServiceAccountTokenDialog' import { ServiceAccountRoleSelector } from '../_components/RoleSelector' -import { Input } from '@/components/common/Input' import { Button } from '@/components/common/Button' import { toast } from 'react-toastify' @@ -71,6 +70,8 @@ export default function ServiceAccount({ params }: { params: { team: string; acc toast.success('Updated account name!') } + const resetName = () => setName(account.name) + useEffect(() => { if (account) setName(account.name) }, [account]) @@ -127,25 +128,47 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
-
+
{' '} -
-

- - {nameUpdated && ( -
- -
- )} -

+

+ setName(e.target.value)} + readOnly={!userCanUpdateSA} + maxLength={64} + /> + + {nameUpdated ? ( +
+ + + +
+ ) : ( +
+ +
+ )} +

+
-
+
+
+
Role
+
Manage the role for this account
+
+
+
+
{account.role.description}
diff --git a/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx b/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx index 6df92ffc..1629889a 100644 --- a/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/RoleSelector.tsx @@ -91,7 +91,7 @@ export const ServiceAccountRoleSelector = (props: {
From cc121ee7a90fd27b0fea9e172a84cbae83c87e00 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 7 Nov 2024 12:30:02 +0530 Subject: [PATCH 54/92] feat: add warning for legacy service tokens --- .../app/[team]/apps/[app]/access/layout.tsx | 4 -- .../components/apps/tokens/SecretTokens.tsx | 55 ++++++++++++++++++- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/access/layout.tsx b/frontend/app/[team]/apps/[app]/access/layout.tsx index 9ca372b9..38d3614b 100644 --- a/frontend/app/[team]/apps/[app]/access/layout.tsx +++ b/frontend/app/[team]/apps/[app]/access/layout.tsx @@ -5,7 +5,6 @@ import { Tab } from '@headlessui/react' import clsx from 'clsx' import Link from 'next/link' import { usePathname } from 'next/navigation' -import { organisationContext } from '@/contexts/organisationContext' export default function AccessLayout({ params, @@ -15,9 +14,6 @@ export default function AccessLayout({ children: React.ReactNode }) { const path = usePathname() - //const router = useRouter() - - const { activeOrganisation } = useContext(organisationContext) const [tabIndex, setTabIndex] = useState(0) diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx index 1a7f19d9..fb1699e8 100644 --- a/frontend/components/apps/tokens/SecretTokens.tsx +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -5,7 +5,7 @@ import { EnvironmentType, ServiceTokenType, UserTokenType } from '@/apollo/graph import { useLazyQuery, useMutation, useQuery } from '@apollo/client' import { useState, useEffect, useContext, Fragment } from 'react' import { Button } from '@/components/common/Button' -import { FaKey, FaTimes, FaTrashAlt } from 'react-icons/fa' +import { FaBan, FaKey, FaTimes, FaTrashAlt } from 'react-icons/fa' import { relativeTimeFromDates } from '@/utils/time' import { Dialog, Transition } from '@headlessui/react' import { clsx } from 'clsx' @@ -15,6 +15,10 @@ import { Avatar } from '@/components/common/Avatar' import { CreateServiceTokenDialog } from './CreateServiceTokenDialog' import { MdKey } from 'react-icons/md' import { toast } from 'react-toastify' +import Spinner from '@/components/common/Spinner' +import { EmptyState } from '@/components/common/EmptyState' +import Link from 'next/link' +import { Alert } from '@/components/common/Alert' export const SecretTokens = (props: { organisationId: string; appId: string }) => { const { organisationId, appId } = props @@ -34,7 +38,7 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) = userHasPermission(organisation?.role?.permissions, 'Tokens', 'create', true) && userHasPermission(organisation?.role?.permissions, 'Environments', 'read', true) - const { data: serviceTokensData } = useQuery(GetServiceTokens, { + const { data: serviceTokensData, loading } = useQuery(GetServiceTokens, { variables: { appId, }, @@ -218,6 +222,38 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) = ) } + if (loading) + return ( +
+ +
+ ) + + if (serviceTokensData?.serviceTokens.length === 0) + return ( +
+ + +
+ } + > +
+
+ Service Accounts give you better control over access to secrets across apps, and let + you manage permissions more easily via defined roles. +
+ + + +
+ +
+ ) + return (
@@ -229,6 +265,21 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) =

+ +
+

+ Service Tokens are being deprecated in favour of Service Accounts. +

+

+ Service Accounts give you better control over access to secrets across apps, and let + you manage permissions more easily via defined roles. +

+ + + +
+
+ {usercanCreateTokens && (
From 8615bcf790c59f6befdcc16a982be6a2c223bcd6 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 7 Nov 2024 13:04:39 +0530 Subject: [PATCH 55/92] fix: misc ui tweaks --- .../[team]/access/service-accounts/[account]/page.tsx | 2 +- frontend/components/apps/tokens/SecretTokens.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index d594e276..41496cfe 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -142,7 +142,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc /> {nameUpdated ? ( -
+
diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx index fb1699e8..e4bcbeb2 100644 --- a/frontend/components/apps/tokens/SecretTokens.tsx +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -2,15 +2,15 @@ import { RevokeServiceToken } from '@/graphql/mutations/environments/deleteServi import { GetServiceTokens } from '@/graphql/queries/secrets/getServiceTokens.gql' import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' import { EnvironmentType, ServiceTokenType, UserTokenType } from '@/apollo/graphql' -import { useLazyQuery, useMutation, useQuery } from '@apollo/client' -import { useState, useEffect, useContext, Fragment } from 'react' +import { useMutation, useQuery } from '@apollo/client' +import { useState, useContext, Fragment } from 'react' import { Button } from '@/components/common/Button' -import { FaBan, FaKey, FaTimes, FaTrashAlt } from 'react-icons/fa' +import { FaExclamationTriangle, FaTimes, FaTrashAlt } from 'react-icons/fa' import { relativeTimeFromDates } from '@/utils/time' import { Dialog, Transition } from '@headlessui/react' import { clsx } from 'clsx' import { organisationContext } from '@/contexts/organisationContext' -import { userHasPermission, userIsAdmin } from '@/utils/access/permissions' +import { userHasPermission } from '@/utils/access/permissions' import { Avatar } from '@/components/common/Avatar' import { CreateServiceTokenDialog } from './CreateServiceTokenDialog' import { MdKey } from 'react-icons/md' @@ -237,7 +237,7 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) = subtitle="Service tokens are deprecated. Please use a Service Account instead" graphic={
- +
} > From 9b62a9a8735a74071a3750557c99ed7c5bc9f251 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 7 Nov 2024 13:39:03 +0530 Subject: [PATCH 56/92] fix: add empty state for service accounts --- .../[team]/access/service-accounts/page.tsx | 98 +++++++++++-------- .../[app]/access/service-accounts/page.tsx | 98 +++++++++++-------- 2 files changed, 114 insertions(+), 82 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/page.tsx b/frontend/app/[team]/access/service-accounts/page.tsx index 30c369b4..9b4432c0 100644 --- a/frontend/app/[team]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/access/service-accounts/page.tsx @@ -40,57 +40,73 @@ export default function ServiceAccounts({ params }: { params: { team: string } }

Manage service accounts.

- {userCanCreateSA && ( + {userCanCreateSA && data?.serviceAccounts.length > 0 && (
)} {userCanReadSA ? ( - - - - - + data?.serviceAccounts.length === 0 ? ( + + + + } + > + <> + + + + ) : ( +
- Account name - - Role -
+ + + + - - - - - {data?.serviceAccounts.map((account: ServiceAccountType) => ( - - + + + + + {data?.serviceAccounts.map((account: ServiceAccountType) => ( + + - + - + - - - ))} - -
+ Account name + + Role + - Created -
-
- -
- {account.name} -
+ Created +
+
+ +
+ {account.name} +
- - + + - {relativeTimeFromDates(new Date(account.createdAt))} - + {relativeTimeFromDates(new Date(account.createdAt))} + - - - -
+ + + + + + + ))} + + + ) ) : ( {userCanReadAppSA ? (
- {userCanAddAppSA && ( + {userCanAddAppSA && data?.appServiceAccounts.length > 0 && (
)} - - - - - - - {userCanRemoveAppSA && } - - - - {data?.appServiceAccounts.map((account: ServiceAccountType) => ( - - + )} + + ))} + +
- Account - - Environment Access -
-
- -
-
-
- {account.name} - + {data?.appServiceAccounts.length === 0 ? ( + + +
+ } + > + <> + + + + ) : ( + + + + + + + {userCanRemoveAppSA && } + + + + {data?.appServiceAccounts.map((account: ServiceAccountType) => ( + + - - +
+
+ {account.name} + +
+
+ - {userCanRemoveAppSA && ( - )} - - ))} - -
+ Account + + Environment Access +
+
+
- -
-
- -
-
-
- +
+
+ + {userCanRemoveAppSA && ( +
+
+ +
+
+ )}
) : ( Date: Thu, 7 Nov 2024 13:43:01 +0530 Subject: [PATCH 57/92] fix: overflow in create account dialog --- .../_components/CreateServiceAccountDialog.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index c709ca2d..b0878342 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -152,7 +152,7 @@ export const CreateServiceAccountDialog = () => { ref={dialogRef} > -
+
+ +
+
+
Apps
+
+ Manage the Apps and Environments that this account has access to +
+
+ +
+ {account.apps.map((app) => ( + +
+
+
{app.name}
+ + + +
+
+ {app.environments.map((app) => app!.name).join(' + ')} +
+
+
+ ))}
@@ -183,7 +218,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc {userCanReadTokens ? (
- {account.tokens?.map((token: ServiceAccountTokenType) => ( + {account.tokens!.map((token) => (
{token!.name} @@ -191,7 +226,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
Created {relativeTimeFromDates(new Date(token?.createdAt))} by{' '} - + {token?.createdBy?.fullName} @@ -199,11 +234,11 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
Expires{' '} - {token.expiresAt ? relativeTimeFromDates(new Date(token?.expiresAt)) : 'never'} + {token!.expiresAt ? relativeTimeFromDates(new Date(token?.expiresAt)) : 'never'}
- +
))} diff --git a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx index 5bd3e0af..0e032bb9 100644 --- a/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx +++ b/frontend/app/[team]/apps/[app]/access/service-accounts/page.tsx @@ -19,6 +19,7 @@ import { FaCheckSquare, FaChevronDown, FaCog, + FaKey, FaPlus, FaRobot, FaSquare, @@ -704,16 +705,21 @@ export default function ServiceAccounts({ params }: { params: { team: string; ap > -

- Manage access for {account.name} -

+
+

+ Manage access for {account.name} +

+

+ Manage the environment scope for this service account +

+
- + {memberHasGlobalAccess(account) && (

@@ -730,100 +736,111 @@ export default function ServiceAccounts({ params }: { params: { team: string; ap )} -

- {scope.length === 0 && showEnvHint && ( - - Select an environment scope - - )} - - {({ open }) => ( - <> - - - - -
- - {scope - .map((env: Partial) => env.name) - .join(' + ')} - - +
+ {scope.length === 0 && showEnvHint && ( + + Select an environment scope + + )} + + {({ open }) => ( + <> + + + + +
-
-
- - -
- {envOptions.map((env: Partial) => ( - - {({ active, selected }) => ( -
- {selected ? ( - - ) : ( - - )} - - {env.name} - -
- )} -
- ))} + > + + {scope + .map((env: Partial) => env.name) + .join(' + ')} + +
-
-
- - )} -
-
+ + + +
+ {envOptions.map((env: Partial) => ( + + {({ active, selected }) => ( +
+ {selected ? ( + + ) : ( + + )} + + {env.name} + +
+ )} +
+ ))} +
+
+
+ + )} + +
-
- - - +
+
+ +
+ {account.tokens?.length! > 0 ? account.tokens?.length! : 'No'} active + tokens +
+
+ + + +
@@ -902,7 +919,7 @@ export default function ServiceAccounts({ params }: { params: { team: string; ap {data?.appServiceAccounts.map((account: ServiceAccountType) => ( -
+
diff --git a/frontend/graphql/queries/apps/getAppServiceAccounts.gql b/frontend/graphql/queries/apps/getAppServiceAccounts.gql index 30b8ca71..e6b5d642 100644 --- a/frontend/graphql/queries/apps/getAppServiceAccounts.gql +++ b/frontend/graphql/queries/apps/getAppServiceAccounts.gql @@ -11,5 +11,9 @@ query GetAppServiceAccounts($appId: ID!) { permissions color } + tokens { + id + name + } } } diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index ba511b2d..cbf96ef0 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -29,5 +29,13 @@ query GetServiceAccounts($orgId: ID!, $id: ID) { self } } + apps { + id + name + environments { + id + name + } + } } } From 2e4e52dde12b53110489c2a19d0b71c89fc8e46f Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 13 Nov 2024 21:05:59 +0530 Subject: [PATCH 66/92] feat: log and display service account token usage --- backend/api/auth.py | 2 + .../0092_secretevent_service_account_token.py | 19 +++++ backend/api/models.py | 3 + backend/api/utils/audit_logging.py | 8 +- backend/api/views/secrets.py | 16 ++-- backend/backend/graphene/types.py | 19 ++++- frontend/apollo/gql.ts | 12 +-- frontend/apollo/graphql.ts | 15 ++-- frontend/apollo/schema.graphql | 73 ++++++++++--------- .../service-accounts/[account]/page.tsx | 7 +- frontend/components/logs/SecretLogs.tsx | 3 +- .../queries/secrets/getAppSecretsLogs.gql | 4 + .../service-accounts/getServiceAccounts.gql | 1 + 13 files changed, 123 insertions(+), 59 deletions(-) create mode 100644 backend/api/migrations/0092_secretevent_service_account_token.py diff --git a/backend/api/auth.py b/backend/api/auth.py index b4b1df05..d60c6d7a 100644 --- a/backend/api/auth.py +++ b/backend/api/auth.py @@ -33,6 +33,7 @@ def authenticate(self, request): "org_member": None, "service_token": None, "service_account": None, + "service_account_token": None, } if token_is_expired_or_deleted(auth_token): @@ -83,6 +84,7 @@ def authenticate(self, request): service_account = get_service_account_from_token(auth_token) user = service_token.created_by.user auth["service_account"] = service_account + auth["service_account_token"] = service_token if not service_account_can_access_environment( service_account.id, env.id diff --git a/backend/api/migrations/0092_secretevent_service_account_token.py b/backend/api/migrations/0092_secretevent_service_account_token.py new file mode 100644 index 00000000..a0dacd70 --- /dev/null +++ b/backend/api/migrations/0092_secretevent_service_account_token.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.15 on 2024-11-13 14:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0091_add_managed_manager_service_roles'), + ] + + operations = [ + migrations.AddField( + model_name='secretevent', + name='service_account_token', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.serviceaccounttoken'), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 3a9e0f8a..1b494048 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -573,6 +573,9 @@ class SecretEvent(models.Model): service_account = models.ForeignKey( ServiceAccount, on_delete=models.SET_NULL, blank=True, null=True ) + service_account_token = models.ForeignKey( + ServiceAccountToken, on_delete=models.SET_NULL, blank=True, null=True + ) key = models.TextField() key_digest = models.TextField() value = models.TextField() diff --git a/backend/api/utils/audit_logging.py b/backend/api/utils/audit_logging.py index 349a402d..e70d3d48 100644 --- a/backend/api/utils/audit_logging.py +++ b/backend/api/utils/audit_logging.py @@ -8,13 +8,18 @@ def log_secret_event( event_type, user=None, service_token=None, - service_account=None, + service_account_token=None, ip_address=None, user_agent=None, ): """ Utility function to log secret events. """ + + service_account = None + if service_account_token is not None: + service_account = service_account_token.service_account + event = SecretEvent.objects.create( secret=secret, environment=secret.environment, @@ -23,6 +28,7 @@ def log_secret_event( user=user, service_token=service_token, service_account=service_account, + service_account_token=service_account_token, key=secret.key, key_digest=secret.key_digest, value=secret.value, diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index 6e03ebf0..bb08dbae 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -123,7 +123,7 @@ def get(self, request): SecretEvent.READ, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -232,7 +232,7 @@ def post(self, request): SecretEvent.CREATE, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -352,7 +352,7 @@ def put(self, request): SecretEvent.UPDATE, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -432,7 +432,7 @@ def delete(self, request): SecretEvent.DELETE, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -521,7 +521,7 @@ def get(self, request): SecretEvent.READ, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -645,7 +645,7 @@ def post(self, request): SecretEvent.CREATE, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -800,7 +800,7 @@ def put(self, request): SecretEvent.UPDATE, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) @@ -894,7 +894,7 @@ def delete(self, request): SecretEvent.DELETE, request.auth["org_member"], request.auth["service_token"], - request.auth["service_account"], + request.auth["service_account_token"], ip_address, user_agent, ) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index d9712cc2..d37ba285 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -34,8 +34,8 @@ UserToken, ) from logs.dynamodb_models import KMSLog -from allauth.socialaccount.models import SocialAccount from django.utils import timezone +from datetime import datetime from api.utils.access.roles import default_roles @@ -216,10 +216,22 @@ class Meta: class ServiceAccountTokenType(DjangoObjectType): + + last_used = graphene.DateTime() + class Meta: model = ServiceAccountToken fields = "__all__" + def resolve_last_used(self, info): + event = ( + SecretEvent.objects.filter(service_account_token=self) + .order_by("-timestamp") + .last() + ) + if event: + return event.timestamp + class MemberType(graphene.Enum): USER = "user" @@ -586,6 +598,7 @@ class Meta: "user", "service_token", "service_account", + "service_account_token", "ip_address", "user_agent", "environment", @@ -609,6 +622,10 @@ def resolve_user(self, info): ): return self.user + def resolve_service_account(self, info): + if self.service_account_token: + return self.service_account_token.service_account + class PersonalSecretType(DjangoObjectType): class Meta: diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index ee09b11d..50f5a1ff 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -74,7 +74,7 @@ const documents = { "mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n ok\n }\n}": types.CreateNewUserTokenDocument, "mutation RevokeUserToken($tokenId: ID!) {\n deleteUserToken(tokenId: $tokenId) {\n ok\n }\n}": types.RevokeUserTokenDocument, "query GetAppMembers($appId: ID!) {\n appUsers(appId: $appId) {\n id\n identityKey\n email\n fullName\n avatarUrl\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n }\n}": types.GetAppMembersDocument, - "query GetAppServiceAccounts($appId: ID!) {\n appServiceAccounts(appId: $appId) {\n id\n identityKey\n name\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n }\n}": types.GetAppServiceAccountsDocument, + "query GetAppServiceAccounts($appId: ID!) {\n appServiceAccounts(appId: $appId) {\n id\n identityKey\n name\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n tokens {\n id\n name\n }\n }\n}": types.GetAppServiceAccountsDocument, "query GetCheckoutDetails($stripeSessionId: String!) {\n stripeCheckoutDetails(stripeSessionId: $stripeSessionId) {\n paymentStatus\n customerEmail\n billingStartDate\n billingEndDate\n subscriptionId\n planName\n }\n}": types.GetCheckoutDetailsDocument, "query GetAppActivityChart($appId: ID!, $period: TimeRange) {\n appActivityChart(appId: $appId, period: $period) {\n index\n date\n data\n }\n}": types.GetAppActivityChartDocument, "query GetAppDetail($organisationId: ID!, $appId: ID!) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n appToken\n appSeed\n appVersion\n sseEnabled\n }\n}": types.GetAppDetailDocument, @@ -92,7 +92,7 @@ const documents = { "query GetRoles($orgId: ID!) {\n roles(orgId: $orgId) {\n id\n name\n description\n color\n permissions\n isDefault\n }\n}": types.GetRolesDocument, "query VerifyInvite($inviteId: ID!) {\n validateInvite(inviteId: $inviteId) {\n id\n organisation {\n id\n name\n }\n inviteeEmail\n invitedBy {\n email\n }\n apps {\n id\n name\n }\n }\n}": types.VerifyInviteDocument, "query GetAppEnvironments($appId: ID!, $memberId: ID, $memberType: MemberType) {\n appEnvironments(\n appId: $appId\n environmentId: null\n memberId: $memberId\n memberType: $memberType\n ) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n app {\n name\n id\n }\n secretCount\n folderCount\n index\n members {\n email\n fullName\n avatarUrl\n }\n }\n sseEnabled(appId: $appId)\n serverPublicKey\n}": types.GetAppEnvironmentsDocument, - "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}": types.GetAppSecretsLogsDocument, + "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n serviceAccountToken {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}": types.GetAppSecretsLogsDocument, "query GetEnvironmentKey($envId: ID!, $appId: ID!) {\n environmentKeys(environmentId: $envId, appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetEnvironmentKeyDocument, "query GetEnvironmentTokens($envId: ID!) {\n environmentTokens(environmentId: $envId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetEnvironmentTokensDocument, "query GetFolders($envId: ID!, $path: String) {\n folders(envId: $envId, path: $path) {\n id\n name\n path\n createdAt\n folderCount\n secretCount\n }\n}": types.GetFoldersDocument, @@ -101,7 +101,7 @@ const documents = { "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}": types.GetServiceAccountsDocument, + "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n apps {\n id\n name\n environments {\n id\n name\n }\n }\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 }\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, "query GetCfPages($credentialId: ID!) {\n cloudflarePagesProjects(credentialId: $credentialId) {\n name\n deploymentId\n environments\n }\n}": types.GetCfPagesDocument, @@ -379,7 +379,7 @@ export function graphql(source: "query GetAppMembers($appId: ID!) {\n appUsers( /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetAppServiceAccounts($appId: ID!) {\n appServiceAccounts(appId: $appId) {\n id\n identityKey\n name\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n }\n}"): (typeof documents)["query GetAppServiceAccounts($appId: ID!) {\n appServiceAccounts(appId: $appId) {\n id\n identityKey\n name\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n }\n}"]; +export function graphql(source: "query GetAppServiceAccounts($appId: ID!) {\n appServiceAccounts(appId: $appId) {\n id\n identityKey\n name\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n tokens {\n id\n name\n }\n }\n}"): (typeof documents)["query GetAppServiceAccounts($appId: ID!) {\n appServiceAccounts(appId: $appId) {\n id\n identityKey\n name\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n tokens {\n id\n name\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -451,7 +451,7 @@ export function graphql(source: "query GetAppEnvironments($appId: ID!, $memberId /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"): (typeof documents)["query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"]; +export function graphql(source: "query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n serviceAccountToken {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"): (typeof documents)["query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n secrets {\n id\n path\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n serviceAccountToken {\n id\n name\n }\n eventType\n environment {\n id\n envType\n name\n }\n secret {\n id\n path\n }\n }\n }\n secretsLogsCount(appId: $appId)\n environmentKeys(appId: $appId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n environment {\n id\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -487,7 +487,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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n }\n}"]; +export function graphql(source: "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n apps {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n apps {\n id\n name\n environments {\n id\n name\n }\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 535f94f0..43be2465 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1535,6 +1535,7 @@ export type SecretEventType = { path: Scalars['String']['output']; secret: SecretType; serviceAccount?: Maybe; + serviceAccountToken?: Maybe; serviceToken?: Maybe; tags: Array; timestamp: Scalars['DateTime']['output']; @@ -1628,7 +1629,9 @@ export type ServiceAccountTokenType = { expiresAt?: Maybe; id: Scalars['String']['output']; identityKey: Scalars['String']['output']; + lastUsed?: Maybe; name: Scalars['String']['output']; + secreteventSet: Array; serviceAccount: ServiceAccountType; token: Scalars['String']['output']; updatedAt: Scalars['DateTime']['output']; @@ -2318,7 +2321,7 @@ export type GetAppServiceAccountsQueryVariables = Exact<{ }>; -export type GetAppServiceAccountsQuery = { __typename?: 'Query', appServiceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, identityKey?: string | null, name: string, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, permissions?: any | null, color?: string | null } | null } | null> | null }; +export type GetAppServiceAccountsQuery = { __typename?: 'Query', appServiceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, identityKey?: string | null, name: string, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, permissions?: any | null, color?: string | null } | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string } | null> | null } | null> | null }; export type GetCheckoutDetailsQueryVariables = Exact<{ stripeSessionId: Scalars['String']['input']; @@ -2450,7 +2453,7 @@ export type GetAppSecretsLogsQueryVariables = Exact<{ }>; -export type GetAppSecretsLogsQuery = { __typename?: 'Query', secretsLogsCount?: number | null, logs?: { __typename?: 'LogsResponseType', secrets?: Array<{ __typename?: 'SecretEventType', id: string, path: string, key: string, value: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceToken?: { __typename?: 'ServiceTokenType', id: string, name: string } | null, serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string } | null, environment: { __typename?: 'EnvironmentType', id: string, envType: ApiEnvironmentEnvTypeChoices, name: string }, secret: { __typename?: 'SecretType', id: string, path: string } } | null> | null } | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string, environment: { __typename?: 'EnvironmentType', id: string } } | null> | null }; +export type GetAppSecretsLogsQuery = { __typename?: 'Query', secretsLogsCount?: number | null, logs?: { __typename?: 'LogsResponseType', secrets?: Array<{ __typename?: 'SecretEventType', id: string, path: string, key: string, value: string, version: number, comment: string, timestamp: any, ipAddress?: string | null, userAgent?: string | null, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null, fullName?: string | null, avatarUrl?: string | null } | null, serviceToken?: { __typename?: 'ServiceTokenType', id: string, name: string } | null, serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string } | null, serviceAccountToken?: { __typename?: 'ServiceAccountTokenType', id: string, name: string } | null, environment: { __typename?: 'EnvironmentType', id: string, envType: ApiEnvironmentEnvTypeChoices, name: string }, secret: { __typename?: 'SecretType', id: string, path: string } } | null> | null } | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string, environment: { __typename?: 'EnvironmentType', id: string } } | null> | null }; export type GetEnvironmentKeyQueryVariables = Exact<{ envId: Scalars['ID']['input']; @@ -2518,7 +2521,7 @@ export type GetServiceAccountsQueryVariables = Exact<{ }>; -export type GetServiceAccountsQuery = { __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, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null } | null> | null }; +export type GetServiceAccountsQuery = { __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, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null, apps: Array<{ __typename?: 'AppType', id: string, name: string, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> } | null> | null }; export type GetOrganisationSyncsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2674,7 +2677,7 @@ export const CreateNewVaultSyncDocument = {"kind":"Document","definitions":[{"ki export const CreateNewUserTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewUserToken"},"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":"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":"createUserToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"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":"ok"}}]}}]}}]} as unknown as DocumentNode; export const RevokeUserTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeUserToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tokenId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteUserToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tokenId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tokenId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const GetAppMembersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppMembers"},"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":"appUsers"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"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"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetAppServiceAccountsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppServiceAccounts"},"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":"appServiceAccounts"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"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"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetAppServiceAccountsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppServiceAccounts"},"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":"appServiceAccounts"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"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"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"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"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetCheckoutDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCheckoutDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stripeSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeCheckoutDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"stripeSessionId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stripeSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"paymentStatus"}},{"kind":"Field","name":{"kind":"Name","value":"customerEmail"}},{"kind":"Field","name":{"kind":"Name","value":"billingStartDate"}},{"kind":"Field","name":{"kind":"Name","value":"billingEndDate"}},{"kind":"Field","name":{"kind":"Name","value":"subscriptionId"}},{"kind":"Field","name":{"kind":"Name","value":"planName"}}]}}]}}]} as unknown as DocumentNode; export const GetAppActivityChartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppActivityChart"},"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":"period"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"TimeRange"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appActivityChart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"period"},"value":{"kind":"Variable","name":{"kind":"Name","value":"period"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"data"}}]}}]}}]} as unknown as DocumentNode; export const GetAppDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"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":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"appToken"}},{"kind":"Field","name":{"kind":"Name","value":"appSeed"}},{"kind":"Field","name":{"kind":"Name","value":"appVersion"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}}]}}]} as unknown as DocumentNode; @@ -2692,7 +2695,7 @@ export const GetOrganisationPlanDocument = {"kind":"Document","definitions":[{"k export const GetRolesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRoles"},"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":"roles"},"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}}]} as unknown as DocumentNode; export const VerifyInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"VerifyInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validateInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppEnvironments"},"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":"memberId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"memberType"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MemberType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"NullValue"}},{"kind":"Argument","name":{"kind":"Name","value":"memberId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}}},{"kind":"Argument","name":{"kind":"Name","value":"memberType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberType"}}}],"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":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretCount"}},{"kind":"Field","name":{"kind":"Name","value":"folderCount"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"serverPublicKey"}}]}}]} as unknown as DocumentNode; -export const GetAppSecretsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppSecretsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetAppSecretsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppSecretsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"serviceAccountToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetEnvironmentKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetEnvironmentKey"},"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":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}}]}}]}}]} as unknown as DocumentNode; export const GetEnvironmentTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetEnvironmentTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentTokens"},"arguments":[{"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":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetFoldersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFolders"},"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":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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"}}]}}]}}]} as unknown as DocumentNode; @@ -2701,7 +2704,7 @@ export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":" 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}}]}},{"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 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 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":"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":"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"}}]}}]}}]}}]}}]} 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":"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":"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":"apps"},"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"}}]}}]}}]}}]}}]} 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":"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; export const GetCfPagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCfPages"},"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":"cloudflarePagesProjects"},"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":"deploymentId"}},{"kind":"Field","name":{"kind":"Name","value":"environments"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 1d738e07..9ecb00d7 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -349,43 +349,10 @@ type ServiceAccountTokenType { updatedAt: DateTime! deletedAt: DateTime expiresAt: DateTime + secreteventSet: [SecretEventType!]! + lastUsed: DateTime } -type LogsResponseType { - kms: [KMSLogType] - secrets: [SecretEventType] -} - -type KMSLogType implements Node { - id: ID! - timestamp: BigInt - appId: String - phaseNode: String - eventType: String - ipAddress: String - phSize: Int - asn: Int - isp: String - edgeLocation: String - country: String - city: String - latitude: Float - longitude: Float -} - -"""An object with an ID""" -interface Node { - """The ID of the object""" - id: ID! -} - -""" -The `BigInt` scalar type represents non-fractional whole numeric values. -`BigInt` is not constrained to 32-bit like the `Int` type and thus is a less -compatible type. -""" -scalar BigInt - type SecretEventType { id: String! secret: SecretType! @@ -394,6 +361,7 @@ type SecretEventType { user: OrganisationMemberType serviceToken: ServiceTokenType serviceAccount: ServiceAccountType + serviceAccountToken: ServiceAccountTokenType key: String! value: String! version: Int! @@ -485,6 +453,41 @@ enum ApiSecretEventEventTypeChoices { D } +type LogsResponseType { + kms: [KMSLogType] + secrets: [SecretEventType] +} + +type KMSLogType implements Node { + id: ID! + timestamp: BigInt + appId: String + phaseNode: String + eventType: String + ipAddress: String + phSize: Int + asn: Int + isp: String + edgeLocation: String + country: String + city: String + latitude: Float + longitude: Float +} + +"""An object with an ID""" +interface Node { + """The ID of the object""" + id: ID! +} + +""" +The `BigInt` scalar type represents non-fractional whole numeric values. +`BigInt` is not constrained to 32-bit like the `Int` type and thus is a less +compatible type. +""" +scalar BigInt + type ChartDataPointType { index: Int date: BigInt diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 537903d8..1f9103d7 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -219,7 +219,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc {userCanReadTokens ? (
{account.tokens!.map((token) => ( -
+
{token!.name}
@@ -237,6 +237,11 @@ export default function ServiceAccount({ params }: { params: { team: string; acc {token!.expiresAt ? relativeTimeFromDates(new Date(token?.expiresAt)) : 'never'}
+
+ Last used{' '} + {token!.lastUsed ? relativeTimeFromDates(new Date(token?.lastUsed)) : 'never'} +
+
diff --git a/frontend/components/logs/SecretLogs.tsx b/frontend/components/logs/SecretLogs.tsx index d6f4def0..7d1e98fa 100644 --- a/frontend/components/logs/SecretLogs.tsx +++ b/frontend/components/logs/SecretLogs.tsx @@ -258,6 +258,7 @@ export default function SecretLogs(props: { app: string }) {
{' '} {log.serviceAccount.name} + {log.serviceAccountToken && ` - ${log.serviceAccountToken.name}`}
) } @@ -430,7 +431,7 @@ export default function SecretLogs(props: { app: string }) { - User + Account Event Environment Secret diff --git a/frontend/graphql/queries/secrets/getAppSecretsLogs.gql b/frontend/graphql/queries/secrets/getAppSecretsLogs.gql index b32436bd..6ecee8e1 100644 --- a/frontend/graphql/queries/secrets/getAppSecretsLogs.gql +++ b/frontend/graphql/queries/secrets/getAppSecretsLogs.gql @@ -29,6 +29,10 @@ query GetAppSecretsLogs($appId: ID!, $start: BigInt, $end: BigInt) { id name } + serviceAccountToken { + id + name + } eventType environment { id diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index cbf96ef0..ae4be61e 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -28,6 +28,7 @@ query GetServiceAccounts($orgId: ID!, $id: ID) { avatarUrl self } + lastUsed } apps { id From e379198905e0787e15b7557f70ab8aa84c678bc9 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 14 Nov 2024 12:27:53 +0530 Subject: [PATCH 67/92] fix: misc fixes to service account app memberships --- backend/backend/graphene/types.py | 120 +++++++------- frontend/apollo/gql.ts | 4 +- frontend/apollo/graphql.ts | 10 +- frontend/apollo/schema.graphql | 152 +++++++++--------- .../service-accounts/[account]/page.tsx | 65 +++++--- .../service-accounts/getServiceAccounts.gql | 2 +- 6 files changed, 189 insertions(+), 164 deletions(-) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index d37ba285..d69e6a3e 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -238,62 +238,6 @@ class MemberType(graphene.Enum): SERVICE = "service" -class ServiceAccountType(DjangoObjectType): - - third_party_auth_enabled = graphene.Boolean() - handlers = graphene.List(ServiceAccountHandlerType) - tokens = graphene.List(ServiceAccountTokenType) - - class Meta: - model = ServiceAccount - fields = ( - "id", - "name", - "role", - "apps", - "identity_key", - "created_at", - "updated_at", - ) - - def resolve_third_party_auth_enabled(self, info): - return ( - self.server_wrapped_keyring is not None - and self.server_wrapped_recovery is not None - ) - - def resolve_handlers(self, info): - return ServiceAccountHandler.objects.filter(service_account=self) - - def resolve_tokens(self, info): - return ServiceAccountToken.objects.filter(service_account=self) - - def resolve_apps(self, info): - # Fetch all apps that this service account is related to - apps = self.apps.all() - - # For each app, filter environments that this ServiceAccount has access to - filtered_apps = [] - for app in apps: - # Get environments for the app - app_environments = Environment.objects.filter(app=app).order_by("index") - - # Check which environments the service account has access to - accessible_environments = [ - env - for env in app_environments - if EnvironmentKey.objects.filter( - service_account=self, environment=env - ).exists() - ] - - # Set the environments field manually to the filtered environments - app.environments = accessible_environments - filtered_apps.append(app) - - return filtered_apps - - class ProviderType(graphene.ObjectType): id = graphene.String(required=True) name = graphene.String(required=True) @@ -412,7 +356,6 @@ def resolve_syncs(self, info): class AppType(DjangoObjectType): environments = graphene.NonNull(graphene.List(EnvironmentType)) members = graphene.NonNull(graphene.List(OrganisationMemberType)) - service_accounts = graphene.NonNull(graphene.List(ServiceAccountType)) class Meta: model = App @@ -426,9 +369,14 @@ class Meta: "app_seed", "app_version", "sse_enabled", + "service_accounts", ) def resolve_environments(self, info): + + if hasattr(self, "filtered_environments"): + return self.filtered_environments + org_member = OrganisationMember.objects.get( organisation=self.organisation, user_id=info.context.user.userId, @@ -448,8 +396,62 @@ def resolve_environments(self, info): def resolve_members(self, info): return self.members.filter(deleted_at=None) - def resolve_service_accounts(self, info): - return self.service_accounts.filter(deleted_at=None) + +class ServiceAccountType(DjangoObjectType): + + third_party_auth_enabled = graphene.Boolean() + handlers = graphene.List(ServiceAccountHandlerType) + tokens = graphene.List(ServiceAccountTokenType) + app_memberships = graphene.List(graphene.NonNull(AppType)) + + class Meta: + model = ServiceAccount + fields = ( + "id", + "name", + "role", + "identity_key", + "created_at", + "updated_at", + ) + + def resolve_third_party_auth_enabled(self, info): + return ( + self.server_wrapped_keyring is not None + and self.server_wrapped_recovery is not None + ) + + def resolve_handlers(self, info): + return ServiceAccountHandler.objects.filter(service_account=self) + + def resolve_tokens(self, info): + return ServiceAccountToken.objects.filter(service_account=self) + + def resolve_app_memberships(self, info): + # Fetch all apps that this service account is related to + apps = self.apps.all() + + filtered_apps = [] + for app in apps: + # Get environments for the app + app_environments = Environment.objects.filter(app=app).order_by("index") + + # Check which environments the service account has access to + accessible_environments = [ + env + for env in app_environments + if EnvironmentKey.objects.filter( + service_account=self, environment=env + ).exists() + ] + + # Manually override the 'environments' field for this app instance + app.filtered_environments = accessible_environments + + # Add this app to the filtered list + filtered_apps.append(app) + + return filtered_apps class EnvironmentKeyType(DjangoObjectType): diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 50f5a1ff..0ca38364 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -101,7 +101,7 @@ const documents = { "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n apps {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}": types.GetServiceAccountsDocument, + "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\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 }\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, "query GetCfPages($credentialId: ID!) {\n cloudflarePagesProjects(credentialId: $credentialId) {\n name\n deploymentId\n environments\n }\n}": types.GetCfPagesDocument, @@ -487,7 +487,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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n apps {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n }\n apps {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"]; +export function graphql(source: "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\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 43be2465..e794b54a 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -147,7 +147,7 @@ export type AppType = { identityKey: Scalars['String']['output']; members: Array>; name: Scalars['String']['output']; - serviceAccounts: Array>; + serviceAccounts: Array; sseEnabled: Scalars['Boolean']['output']; wrappedKeyShare: Scalars['String']['output']; }; @@ -1640,7 +1640,7 @@ export type ServiceAccountTokenType = { export type ServiceAccountType = { __typename?: 'ServiceAccountType'; - apps: Array; + appMemberships?: Maybe>; createdAt?: Maybe; handlers?: Maybe>>; id: Scalars['String']['output']; @@ -2361,7 +2361,7 @@ export type GetAppsQueryVariables = Exact<{ }>; -export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, serviceAccounts: Array<{ __typename?: 'ServiceAccountType', id: string, name: string } | null>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; +export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, serviceAccounts: Array<{ __typename?: 'ServiceAccountType', id: string, name: string }>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; export type GetDashboardQueryVariables = Exact<{ organisationId: Scalars['ID']['input']; @@ -2521,7 +2521,7 @@ export type GetServiceAccountsQueryVariables = Exact<{ }>; -export type GetServiceAccountsQuery = { __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, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null, apps: Array<{ __typename?: 'AppType', id: string, name: string, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> } | null> | null }; +export type GetServiceAccountsQuery = { __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, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, 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, appMemberships?: Array<{ __typename?: 'AppType', id: string, name: string, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null } | null> | null }; export type GetOrganisationSyncsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2704,7 +2704,7 @@ export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":" 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}}]}},{"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 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 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":"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":"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":"apps"},"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"}}]}}]}}]}}]}}]} 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":"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":"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"}}]}},{"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"}}]}}]}}]}}]}}]} 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":"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; export const GetCfPagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCfPages"},"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":"cloudflarePagesProjects"},"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":"deploymentId"}},{"kind":"Field","name":{"kind":"Name","value":"environments"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 9ecb00d7..4ca00f67 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -196,9 +196,84 @@ type AppType { wrappedKeyShare: String! createdAt: DateTime sseEnabled: Boolean! + serviceAccounts: [ServiceAccountType!]! environments: [EnvironmentType]! members: [OrganisationMemberType]! - serviceAccounts: [ServiceAccountType]! +} + +type ServiceAccountType { + id: String! + name: String! + role: RoleType + identityKey: String + createdAt: DateTime + updatedAt: DateTime! + thirdPartyAuthEnabled: Boolean + handlers: [ServiceAccountHandlerType] + tokens: [ServiceAccountTokenType] + appMemberships: [AppType!] +} + +type ServiceAccountHandlerType { + id: String! + serviceAccount: ServiceAccountType! + user: OrganisationMemberType! + wrappedKeyring: String! + wrappedRecovery: String! + createdAt: DateTime + updatedAt: DateTime! +} + +type ServiceAccountTokenType { + id: String! + serviceAccount: ServiceAccountType! + name: String! + identityKey: String! + token: String! + wrappedKeyShare: String! + createdBy: OrganisationMemberType + createdAt: DateTime + updatedAt: DateTime! + deletedAt: DateTime + expiresAt: DateTime + secreteventSet: [SecretEventType!]! + lastUsed: DateTime +} + +type SecretEventType { + id: String! + secret: SecretType! + environment: EnvironmentType! + path: String! + user: OrganisationMemberType + serviceToken: ServiceTokenType + serviceAccount: ServiceAccountType + serviceAccountToken: ServiceAccountTokenType + key: String! + value: String! + version: Int! + tags: [SecretTagType!]! + comment: String! + eventType: ApiSecretEventEventTypeChoices! + timestamp: DateTime! + ipAddress: String + userAgent: String +} + +type SecretType { + id: String! + environment: EnvironmentType! + folder: SecretFolderType + path: String! + key: String! + value: String! + version: Int! + tags: [SecretTagType!]! + comment: String! + createdAt: DateTime + updatedAt: DateTime! + history: [SecretEventType] + override: PersonalSecretType } type EnvironmentType { @@ -314,81 +389,6 @@ enum ApiEnvironmentSyncEventStatusChoices { FAILED } -type ServiceAccountType { - id: String! - name: String! - role: RoleType - apps: [AppType!]! - identityKey: String - createdAt: DateTime - updatedAt: DateTime! - thirdPartyAuthEnabled: Boolean - handlers: [ServiceAccountHandlerType] - tokens: [ServiceAccountTokenType] -} - -type ServiceAccountHandlerType { - id: String! - serviceAccount: ServiceAccountType! - user: OrganisationMemberType! - wrappedKeyring: String! - wrappedRecovery: String! - createdAt: DateTime - updatedAt: DateTime! -} - -type ServiceAccountTokenType { - id: String! - serviceAccount: ServiceAccountType! - name: String! - identityKey: String! - token: String! - wrappedKeyShare: String! - createdBy: OrganisationMemberType - createdAt: DateTime - updatedAt: DateTime! - deletedAt: DateTime - expiresAt: DateTime - secreteventSet: [SecretEventType!]! - lastUsed: DateTime -} - -type SecretEventType { - id: String! - secret: SecretType! - environment: EnvironmentType! - path: String! - user: OrganisationMemberType - serviceToken: ServiceTokenType - serviceAccount: ServiceAccountType - serviceAccountToken: ServiceAccountTokenType - key: String! - value: String! - version: Int! - tags: [SecretTagType!]! - comment: String! - eventType: ApiSecretEventEventTypeChoices! - timestamp: DateTime! - ipAddress: String - userAgent: String -} - -type SecretType { - id: String! - environment: EnvironmentType! - folder: SecretFolderType - path: String! - key: String! - value: String! - version: Int! - tags: [SecretTagType!]! - comment: String! - createdAt: DateTime - updatedAt: DateTime! - history: [SecretEventType] - override: PersonalSecretType -} - type SecretFolderType { id: String! environment: EnvironmentType! diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 1f9103d7..57d3264c 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -36,6 +36,10 @@ export default function ServiceAccount({ params }: { params: { team: string; acc ? userHasPermission(organisation.role?.permissions, 'ServiceAccountTokens', 'read') : false + const userCanReadAppMemberships = organisation + ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'read', true) + : false + const userCanUpdateSA = organisation ? userHasPermission(organisation?.role?.permissions, 'ServiceAccounts', 'update') : false @@ -183,28 +187,47 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
-
- {account.apps.map((app) => ( - -
-
-
{app.name}
- - - -
-
- {app.environments.map((app) => app!.name).join(' + ')} + {userCanReadAppMemberships ? ( +
+ {account.appMemberships?.map((app) => ( + +
+
+
{app.name}
+ + + +
+
+
+ Environments +
+
+ {app.environments.map((app) => app!.name).join(' + ')} +
+
+
+ ))} +
+ ) : ( + +
- - ))} -
+ } + > + <> + + )}
@@ -251,7 +274,7 @@ export default function ServiceAccount({ params }: { params: { team: string; acc ) : ( diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index ae4be61e..6246609e 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -30,7 +30,7 @@ query GetServiceAccounts($orgId: ID!, $id: ID) { } lastUsed } - apps { + appMemberships { id name environments { From 91a55c6fd75b2d27df2eddd56016334052ebaaab Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 14 Nov 2024 12:31:10 +0530 Subject: [PATCH 68/92] fix: update env scope display --- frontend/app/[team]/apps/[app]/access/members/page.tsx | 9 ++++----- .../[team]/apps/[app]/access/service-accounts/page.tsx | 8 +++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/access/members/page.tsx b/frontend/app/[team]/apps/[app]/access/members/page.tsx index 26f5505e..1395b59d 100644 --- a/frontend/app/[team]/apps/[app]/access/members/page.tsx +++ b/frontend/app/[team]/apps/[app]/access/members/page.tsx @@ -656,11 +656,10 @@ export default function Members({ params }: { params: { team: string; app: strin return ( <>
- {envScope.map((env) => ( - - {env.name} - - ))} + + {envScope.map((env) => env.name).join(' + ')} + + {allowUpdateScope && (
{' '} {log.serviceAccount.name} - {log.serviceAccountToken && ` - ${log.serviceAccountToken.name}`} + {log.serviceAccountToken && ` (${log.serviceAccountToken.name})`}
) } @@ -350,7 +357,21 @@ export default function SecretLogs(props: { app: string }) {
- {logCreatedBy(log)} + +
+ {logCreatedBy(log)}{' '} + {log.serviceAccount && ( + + + + )} +
+
{log.ipAddress} From ca59ebe13515ab91580ca5bb43f120786bcb8a27 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 14 Nov 2024 18:48:11 +0530 Subject: [PATCH 70/92] fix: command palette duplicate id --- frontend/components/common/CommandPalette.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/common/CommandPalette.tsx b/frontend/components/common/CommandPalette.tsx index 4e51c19d..9259182c 100644 --- a/frontend/components/common/CommandPalette.tsx +++ b/frontend/components/common/CommandPalette.tsx @@ -98,7 +98,7 @@ const CommandPalette: React.FC = () => { action: () => handleNavigation(`/${activeOrganisation?.name}/access/members`), }, { - id: 'go-members', + id: 'go-service-accounts', name: 'Go to Service Accounts', description: 'Manage organization service accounts', icon: , From 69db49df586d737c0ae5d64f734e7b9746344922 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 Nov 2024 12:16:34 +0530 Subject: [PATCH 71/92] fix: access template listbox positioning --- frontend/components/access/AccessTemplateSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/access/AccessTemplateSelector.tsx b/frontend/components/access/AccessTemplateSelector.tsx index 407678b7..d906fb03 100644 --- a/frontend/components/access/AccessTemplateSelector.tsx +++ b/frontend/components/access/AccessTemplateSelector.tsx @@ -95,7 +95,7 @@ export const AccessTemplateSelector = ({ } return ( -
+
{({ open }) => ( <> From 25514ffa5b39147c9d65fcdc69442ee2ce9c5d33 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 Nov 2024 12:18:03 +0530 Subject: [PATCH 72/92] fix: add legacy label to service tokens in role manager --- frontend/components/access/CreateRoleDialog.tsx | 1 + frontend/components/access/ManageRoleDialog.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/components/access/CreateRoleDialog.tsx b/frontend/components/access/CreateRoleDialog.tsx index a98f1dca..467bd65d 100644 --- a/frontend/components/access/CreateRoleDialog.tsx +++ b/frontend/components/access/CreateRoleDialog.tsx @@ -364,6 +364,7 @@ export const CreateRoleDialog = () => { {camelCaseToSpaces(resource)} + {resource === 'Tokens' && '(Legacy)'} ( - {camelCaseToSpaces(resource)} + {camelCaseToSpaces(resource)}{' '} + {resource === 'Tokens' && '(Legacy)'} Date: Fri, 15 Nov 2024 12:22:06 +0530 Subject: [PATCH 73/92] fix: tweak listbox open state styling, position --- frontend/components/access/AccessTemplateSelector.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/components/access/AccessTemplateSelector.tsx b/frontend/components/access/AccessTemplateSelector.tsx index d906fb03..603f17d8 100644 --- a/frontend/components/access/AccessTemplateSelector.tsx +++ b/frontend/components/access/AccessTemplateSelector.tsx @@ -101,7 +101,12 @@ export const AccessTemplateSelector = ({ <> {({ value }) => ( -
+
{value.icon} {value.name} @@ -116,7 +121,7 @@ export const AccessTemplateSelector = ({
)} - + {accessTemplates.map((template) => ( {({ active, selected }) => ( From 4bdbcf268dfc53efa70311132e4e83599b8935f1 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 Nov 2024 15:21:06 +0530 Subject: [PATCH 74/92] fix: misc fixes to rest api permission checking --- backend/api/auth.py | 1 + backend/api/views/secrets.py | 318 ++++++++++++++++++----------------- 2 files changed, 163 insertions(+), 156 deletions(-) diff --git a/backend/api/auth.py b/backend/api/auth.py index d60c6d7a..dcd12668 100644 --- a/backend/api/auth.py +++ b/backend/api/auth.py @@ -30,6 +30,7 @@ def authenticate(self, request): auth = { "token": auth_token, + "auth_type": token_type, "org_member": None, "service_token": None, "service_account": None, diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index bb08dbae..73ebf7db 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -57,13 +57,17 @@ def get(self, request): if not env.id: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - if request.auth["org_member"] or request.auth["service_account"]: - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) + account = None + + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + # Check permissions + if account is not None: if not user_has_permission( account, @@ -80,7 +84,8 @@ def get(self, request): status=403, ) - if request.auth["org_member"] is not None and not user_can_access_environment( + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( request.auth["org_member"].user.userId, env.id ): return JsonResponse( @@ -88,8 +93,8 @@ def get(self, request): ) elif request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( request.auth["service_account"].id, env.id ): return JsonResponse( @@ -141,16 +146,21 @@ def post(self, request): if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - if request.auth["org_member"] or request.auth["service_account"]: - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) + account = None + + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + # Check permissions + if account is not None: + if not user_has_permission( account, - "create", + "read", "Secrets", env.app.organisation, True, @@ -158,12 +168,13 @@ def post(self, request): ): return JsonResponse( { - "error": "You don't have permission to create secrets in this environment" + "error": "You don't have permission to read secrets in this environment" }, status=403, ) - if request.auth["org_member"] is not None and not user_can_access_environment( + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( request.auth["org_member"].user.userId, env.id ): return JsonResponse( @@ -171,8 +182,8 @@ def post(self, request): ) elif request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( request.auth["service_account"].id, env.id ): return JsonResponse( @@ -254,17 +265,21 @@ def put(self, request): if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - if request.auth["org_member"] or request.auth["service_account"]: - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) + account = None + + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + # Check permissions + if account is not None: if not user_has_permission( account, - "update", + "read", "Secrets", env.app.organisation, True, @@ -272,12 +287,13 @@ def put(self, request): ): return JsonResponse( { - "error": "You don't have permission to update secrets in this environment" + "error": "You don't have permission to read secrets in this environment" }, status=403, ) - if request.auth["org_member"] is not None and not user_can_access_environment( + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( request.auth["org_member"].user.userId, env.id ): return JsonResponse( @@ -285,8 +301,8 @@ def put(self, request): ) elif request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( request.auth["service_account"].id, env.id ): return JsonResponse( @@ -379,19 +395,25 @@ def delete(self, request): secrets_to_delete = Secret.objects.filter(id__in=request_body["secrets"]) + account = None + + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + for secret in secrets_to_delete: if not Secret.objects.filter(id=secret.id).exists(): return JsonResponse({"error": "Secret doesn't exist"}, status=404) - if request.auth["org_member"] or request.auth["service_account"]: - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) + + # Check permissions + if account is not None: + if not user_has_permission( account, - "delete", + "read", "Secrets", secret.environment.app.organisation, True, @@ -399,29 +421,11 @@ def delete(self, request): ): return JsonResponse( { - "error": "You don't have permission to delete secrets in this environment" + "error": "You don't have permission to read secrets in this environment" }, status=403, ) - if request.auth[ - "org_member" - ] is not None and not user_can_access_environment( - request.auth["org_member"].user.userId, secret.environment.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - if request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( - request.auth["service_account"].id, secret.environment.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - for secret in secrets_to_delete: secret.updated_at = timezone.now() secret.deleted_at = timezone.now() @@ -453,14 +457,17 @@ def dispatch(self, request, *args): def get(self, request): env = request.auth["environment"] + account = None - if request.auth["org_member"] or request.auth["service_account"]: - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + # Check permissions + if account is not None: if not user_has_permission( account, @@ -477,7 +484,8 @@ def get(self, request): status=403, ) - if request.auth["org_member"] is not None and not user_can_access_environment( + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( request.auth["org_member"].user.userId, env.id ): return JsonResponse( @@ -485,8 +493,8 @@ def get(self, request): ) elif request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( request.auth["service_account"].id, env.id ): return JsonResponse( @@ -537,29 +545,35 @@ def get(self, request): def post(self, request): env = request.auth["environment"] + account = None - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) - if not user_has_permission( - account, - "create", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to create secrets in this environment" - }, - status=403, - ) + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] - if request.auth["org_member"] is not None and not user_can_access_environment( + # Check permissions + if account is not None: + + if not user_has_permission( + account, + "read", + "Secrets", + env.app.organisation, + True, + request.auth["service_account"] is not None, + ): + return JsonResponse( + { + "error": "You don't have permission to read secrets in this environment" + }, + status=403, + ) + + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( request.auth["org_member"].user.userId, env.id ): return JsonResponse( @@ -567,8 +581,8 @@ def post(self, request): ) elif request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( request.auth["service_account"].id, env.id ): return JsonResponse( @@ -671,44 +685,32 @@ def post(self, request): def put(self, request): env = request.auth["environment"] + account = None - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user - if not user_has_permission( - account, - "update", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to update secrets in this environment" - }, - status=403, - ) + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] - if request.auth["org_member"] is not None and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + # Check permissions + if account is not None: - elif request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + if not user_has_permission( + account, + "read", + "Secrets", + env.app.organisation, + True, + request.auth["service_account"] is not None, + ): + return JsonResponse( + { + "error": "You don't have permission to read secrets in this environment" + }, + status=403, + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -830,27 +832,32 @@ def put(self, request): def delete(self, request): env = request.auth["environment"] + account = None - account = ( - request.auth["service_account"] - if "service_account" in request.auth - and request.auth["service_account"] is not None - else request.auth["org_member"].user - ) - if not user_has_permission( - account, - "delete", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to delete secrets in this environment" - }, - status=403, - ) + # Determine account to check permissions for + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + # Check permissions + if account is not None: + + if not user_has_permission( + account, + "read", + "Secrets", + env.app.organisation, + True, + request.auth["service_account"] is not None, + ): + return JsonResponse( + { + "error": "You don't have permission to read secrets in this environment" + }, + status=403, + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -866,19 +873,18 @@ def delete(self, request): if not Secret.objects.filter(id=secret.id).exists(): return JsonResponse({"error": "Secret does not exist"}, status=404) - if request.auth[ - "org_member" - ] is not None and not user_can_access_environment( - request.auth["org_member"].user.userId, secret.environment.id + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id ): return JsonResponse( {"error": "You don't have access to this environment"}, status=403 ) - if request.auth[ - "service_account" - ] is not None and not service_account_can_access_environment( - request.auth["service_account"].id, secret.environment.id + elif request.auth[ + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( + request.auth["service_account"].id, env.id ): return JsonResponse( {"error": "You don't have access to this environment"}, status=403 From 121334ad35f1cf49bbdcda0084d53aa01d98f40e Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 Nov 2024 15:43:39 +0530 Subject: [PATCH 75/92] refactor: move api permission checks to middleware --- backend/api/utils/access/permissions.py | 68 ++++ backend/api/views/secrets.py | 398 +++++------------------- 2 files changed, 146 insertions(+), 320 deletions(-) diff --git a/backend/api/utils/access/permissions.py b/backend/api/utils/access/permissions.py index 249d5343..34de3492 100644 --- a/backend/api/utils/access/permissions.py +++ b/backend/api/utils/access/permissions.py @@ -7,6 +7,11 @@ ) from api.utils.access.roles import default_roles +# middlewares.py +from django.http import JsonResponse +from django.utils.deprecation import MiddlewareMixin + + admin_roles = ["Owner", "Admin"] @@ -96,3 +101,66 @@ def user_has_permission( except OrganisationMember.DoesNotExist: return False # User is not a member of the organization + + +class PermissionMiddleware(MiddlewareMixin): + + def process_view(self, request, view_func, view_args, view_kwargs): + """ + This method gets called before calling the view. + """ + + account = None + + # Determine account type + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + # Check permissions + if account is not None: + # Customize for each view using the view's kwargs (like `resource`, `action`, etc.) + action = view_kwargs.get("action", "read") # Default to 'read' + resource = view_kwargs.get("resource", "Secrets") # Default to 'Secrets' + organisation = view_kwargs.get( + "organisation", None + ) # Get the organisation from view kwargs or None + env = view_kwargs.get( + "env", None + ) # Assuming environment is passed in view kwargs + + if env and not user_has_permission( + account, + action, + resource, + organisation, + True, + request.auth.get("service_account") is not None, + ): + return JsonResponse( + { + "error": f"You don't have permission to {action} {resource.lower()} in this environment" + }, + status=403, + ) + + # Check env access + if request.auth["auth_type"] == "User" and not user_can_access_environment( + request.auth["org_member"].user.userId, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + elif request.auth[ + "auth_type" + ] == "ServiceAccount" and not service_account_can_access_environment( + request.auth["service_account"].id, env.id + ): + return JsonResponse( + {"error": "You don't have access to this environment"}, status=403 + ) + + # If everything is fine, proceed to the view + return None diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index 73ebf7db..edc50c15 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -18,6 +18,7 @@ get_environment_keys, ) from api.utils.access.permissions import ( + PermissionMiddleware, service_account_can_access_environment, user_can_access_environment, user_has_permission, @@ -40,6 +41,7 @@ from djangorestframework_camel_case.render import ( CamelCaseJSONRenderer, ) +from django.utils.decorators import method_decorator class E2EESecretsView(APIView): @@ -50,56 +52,21 @@ class E2EESecretsView(APIView): def dispatch(self, request, *args): return super(E2EESecretsView, self).dispatch(request, *args) - def get(self, request): + def get(self, request, *args, **kwargs): env_id = request.headers["environment"] env = Environment.objects.get(id=env_id) if not env.id: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) - - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + kwargs.update( + { + "action": "read", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) ip_address, user_agent = get_resolver_request_meta(request) @@ -139,56 +106,21 @@ def get(self, request): return Response(serializer.data, status=status.HTTP_200_OK) - def post(self, request): + def post(self, request, *args, **kwargs): env_id = request.headers["environment"] env = Environment.objects.get(id=env_id) if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) - - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + kwargs.update( + { + "action": "create", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) request_body = json.loads(request.body) @@ -258,56 +190,21 @@ def post(self, request): return Response(status=status.HTTP_200_OK) - def put(self, request): + def put(self, request, *args, **kwargs): env_id = request.headers["environment"] env = Environment.objects.get(id=env_id) if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) - - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + kwargs.update( + { + "action": "update", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) request_body = json.loads(request.body) @@ -387,7 +284,7 @@ def put(self, request): return Response(status=status.HTTP_200_OK) - def delete(self, request): + def delete(self, request, *args, **kwargs): request_body = json.loads(request.body) @@ -395,36 +292,19 @@ def delete(self, request): secrets_to_delete = Secret.objects.filter(id__in=request_body["secrets"]) - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user + if not secrets_to_delete.exists(): + return Response(status=status.HTTP_200_OK) - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] + env = secrets_to_delete[0].environment - for secret in secrets_to_delete: - if not Secret.objects.filter(id=secret.id).exists(): - return JsonResponse({"error": "Secret doesn't exist"}, status=404) - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - secret.environment.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) + kwargs.update( + { + "action": "delete", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) for secret in secrets_to_delete: secret.updated_at = timezone.now() @@ -444,6 +324,7 @@ def delete(self, request): return Response(status=status.HTTP_200_OK) +@method_decorator(PermissionMiddleware, name="dispatch") class PublicSecretsView(APIView): authentication_classes = [PhaseTokenAuthentication] permission_classes = [IsAuthenticated] @@ -455,51 +336,16 @@ class PublicSecretsView(APIView): def dispatch(self, request, *args): return super(PublicSecretsView, self).dispatch(request, *args) - def get(self, request): + def get(self, request, *args, **kwargs): env = request.auth["environment"] - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) - - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + kwargs.update( + { + "action": "read", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -542,52 +388,17 @@ def get(self, request): return Response(serializer.data, status=status.HTTP_200_OK) - def post(self, request): + def post(self, request, *args, **kwargs): env = request.auth["environment"] - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) - - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) + kwargs.update( + { + "action": "create", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -682,35 +493,17 @@ def post(self, request): return Response(serializer.data, status=status.HTTP_200_OK) - def put(self, request): + def put(self, request, *args, **kwargs): env = request.auth["environment"] - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) + kwargs.update( + { + "action": "update", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -829,35 +622,17 @@ def put(self, request): return Response(serializer.data, status=status.HTTP_200_OK) - def delete(self, request): + def delete(self, request, *args, **kwargs): env = request.auth["environment"] - account = None - - # Determine account to check permissions for - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - - if not user_has_permission( - account, - "read", - "Secrets", - env.app.organisation, - True, - request.auth["service_account"] is not None, - ): - return JsonResponse( - { - "error": "You don't have permission to read secrets in this environment" - }, - status=403, - ) + kwargs.update( + { + "action": "delete", + "resource": "Secrets", + "organisation": env.app.organisation, + "env": env, + } + ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -873,23 +648,6 @@ def delete(self, request): if not Secret.objects.filter(id=secret.id).exists(): return JsonResponse({"error": "Secret does not exist"}, status=404) - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - for secret in secrets_to_delete: secret.updated_at = timezone.now() secret.deleted_at = timezone.now() From 1e14b98527643999459e60bc1b64cd1e68168943 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 Nov 2024 16:01:51 +0530 Subject: [PATCH 76/92] chore: regenerate types --- frontend/apollo/graphql.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index eb51c912..e914a31a 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -2410,10 +2410,8 @@ export type GetAppsQueryVariables = Exact<{ }>; - export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, sseEnabled: boolean, members: Array<{ __typename?: 'OrganisationMemberType', id: string, email?: string | null, fullName?: string | null, avatarUrl?: string | null } | null>, serviceAccounts: Array<{ __typename?: 'ServiceAccountType', id: string, name: string }>, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, syncs: Array<{ __typename?: 'EnvironmentSyncType', id: string, status: ApiEnvironmentSyncStatusChoices, serviceInfo?: { __typename?: 'ServiceType', id?: string | null, name?: string | null, provider?: { __typename?: 'ProviderType', id: string, name: string } | null } | null } | null> } | null> } | null> | null }; - export type GetDashboardQueryVariables = Exact<{ organisationId: Scalars['ID']['input']; }>; From e3566ae7f2608c669c647359c3826a52489904e5 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 Nov 2024 16:10:51 +0530 Subject: [PATCH 77/92] feat: add empty state for service account app membership --- .../access/service-accounts/[account]/page.tsx | 17 ++++++++++++++++- frontend/components/common/EmptyState.tsx | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 57d3264c..9fc01e72 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -9,7 +9,7 @@ import { relativeTimeFromDates } from '@/utils/time' import { useMutation, useQuery } from '@apollo/client' import Link from 'next/link' import { useContext, useEffect, useState } from 'react' -import { FaBan, FaChevronLeft, FaCog, FaEdit, FaKey, FaRobot } from 'react-icons/fa' +import { FaBan, FaBoxOpen, FaChevronLeft, FaCog, FaEdit, FaKey, FaRobot } from 'react-icons/fa' import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog' import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog' import { ServiceAccountTokenType, ServiceAccountType } from '@/apollo/graphql' @@ -214,6 +214,21 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
))} + {account.appMemberships?.length === 0 && ( +
+ + +
+ } + > + <> + +
+ )}
) : (
{title}
-
{subtitle}
+
{subtitle}
{children}
From a26219b2c37aba76dee3616200f8fd098fb576b9 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 16 Nov 2024 13:15:02 +0530 Subject: [PATCH 78/92] fix: merge migrations --- backend/api/migrations/0093_merge_20241116_0744.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 backend/api/migrations/0093_merge_20241116_0744.py diff --git a/backend/api/migrations/0093_merge_20241116_0744.py b/backend/api/migrations/0093_merge_20241116_0744.py new file mode 100644 index 00000000..80596b52 --- /dev/null +++ b/backend/api/migrations/0093_merge_20241116_0744.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.15 on 2024-11-16 07:44 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0086_alter_environmentsync_service'), + ('api', '0092_secretevent_service_account_token'), + ] + + operations = [ + ] From ca6928321ba01ec84aecb1d7723f5c6b49704602 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 16 Nov 2024 15:18:50 +0530 Subject: [PATCH 79/92] refactor: rest permissions --- backend/api/utils/access/permissions.py | 63 ----------- backend/api/utils/rest.py | 8 ++ backend/api/views/secrets.py | 143 ++++++++++-------------- 3 files changed, 70 insertions(+), 144 deletions(-) diff --git a/backend/api/utils/access/permissions.py b/backend/api/utils/access/permissions.py index 34de3492..8db6e383 100644 --- a/backend/api/utils/access/permissions.py +++ b/backend/api/utils/access/permissions.py @@ -101,66 +101,3 @@ def user_has_permission( except OrganisationMember.DoesNotExist: return False # User is not a member of the organization - - -class PermissionMiddleware(MiddlewareMixin): - - def process_view(self, request, view_func, view_args, view_kwargs): - """ - This method gets called before calling the view. - """ - - account = None - - # Determine account type - if request.auth["auth_type"] == "User": - account = request.auth["org_member"].user - elif request.auth["auth_type"] == "ServiceAccount": - account = request.auth["service_account"] - - # Check permissions - if account is not None: - # Customize for each view using the view's kwargs (like `resource`, `action`, etc.) - action = view_kwargs.get("action", "read") # Default to 'read' - resource = view_kwargs.get("resource", "Secrets") # Default to 'Secrets' - organisation = view_kwargs.get( - "organisation", None - ) # Get the organisation from view kwargs or None - env = view_kwargs.get( - "env", None - ) # Assuming environment is passed in view kwargs - - if env and not user_has_permission( - account, - action, - resource, - organisation, - True, - request.auth.get("service_account") is not None, - ): - return JsonResponse( - { - "error": f"You don't have permission to {action} {resource.lower()} in this environment" - }, - status=403, - ) - - # Check env access - if request.auth["auth_type"] == "User" and not user_can_access_environment( - request.auth["org_member"].user.userId, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - elif request.auth[ - "auth_type" - ] == "ServiceAccount" and not service_account_can_access_environment( - request.auth["service_account"].id, env.id - ): - return JsonResponse( - {"error": "You don't have access to this environment"}, status=403 - ) - - # If everything is fine, proceed to the view - return None diff --git a/backend/api/utils/rest.py b/backend/api/utils/rest.py index fca2b29d..d133efa5 100644 --- a/backend/api/utils/rest.py +++ b/backend/api/utils/rest.py @@ -2,6 +2,14 @@ from django.utils import timezone import base64 +# Map HTTP methods to permission actions +METHOD_TO_ACTION = { + "GET": "read", + "POST": "create", + "PUT": "update", + "DELETE": "delete", +} + def get_client_ip(request): x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") diff --git a/backend/api/views/secrets.py b/backend/api/views/secrets.py index edc50c15..75b80794 100644 --- a/backend/api/views/secrets.py +++ b/backend/api/views/secrets.py @@ -5,7 +5,6 @@ Secret, SecretEvent, SecretTag, - ServerEnvironmentKey, ) from api.serializers import ( SecretSerializer, @@ -18,39 +17,63 @@ get_environment_keys, ) from api.utils.access.permissions import ( - PermissionMiddleware, - service_account_can_access_environment, - user_can_access_environment, user_has_permission, ) from api.utils.audit_logging import log_secret_event from api.utils.crypto import encrypt_asymmetric, validate_encrypted_string from api.utils.rest import ( + METHOD_TO_ACTION, get_resolver_request_meta, ) import json from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated +from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response from rest_framework import status -from django.views.decorators.csrf import csrf_exempt from django.http import JsonResponse from django.utils import timezone from djangorestframework_camel_case.render import ( CamelCaseJSONRenderer, ) -from django.utils.decorators import method_decorator class E2EESecretsView(APIView): authentication_classes = [PhaseTokenAuthentication] permission_classes = [IsAuthenticated] - @csrf_exempt - def dispatch(self, request, *args): - return super(E2EESecretsView, self).dispatch(request, *args) + def initial(self, request, *args, **kwargs): + super().initial(request, *args, **kwargs) + + # Determine the action based on the request method + action = METHOD_TO_ACTION.get(request.method) + if not action: + raise PermissionDenied(f"Unsupported HTTP method: {request.method}") + + # Perform permission check + account = None + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + if account is not None: + env = request.auth["environment"] + organisation = env.app.organisation + + if not user_has_permission( + account, + action, + "Secrets", + organisation, + True, + request.auth.get("service_account") is not None, + ): + raise PermissionDenied( + f"You don't have permission to {action} secrets in this environment." + ) def get(self, request, *args, **kwargs): @@ -59,15 +82,6 @@ def get(self, request, *args, **kwargs): if not env.id: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - kwargs.update( - { - "action": "read", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) - ip_address, user_agent = get_resolver_request_meta(request) secrets_filter = {"environment": env, "deleted_at": None} @@ -113,15 +127,6 @@ def post(self, request, *args, **kwargs): if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - kwargs.update( - { - "action": "create", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) - request_body = json.loads(request.body) ip_address, user_agent = get_resolver_request_meta(request) @@ -197,15 +202,6 @@ def put(self, request, *args, **kwargs): if not env: return JsonResponse({"error": "Environment doesn't exist"}, status=404) - kwargs.update( - { - "action": "update", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) - request_body = json.loads(request.body) ip_address, user_agent = get_resolver_request_meta(request) @@ -297,15 +293,6 @@ def delete(self, request, *args, **kwargs): env = secrets_to_delete[0].environment - kwargs.update( - { - "action": "delete", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) - for secret in secrets_to_delete: secret.updated_at = timezone.now() secret.deleted_at = timezone.now() @@ -324,7 +311,6 @@ def delete(self, request, *args, **kwargs): return Response(status=status.HTTP_200_OK) -@method_decorator(PermissionMiddleware, name="dispatch") class PublicSecretsView(APIView): authentication_classes = [PhaseTokenAuthentication] permission_classes = [IsAuthenticated] @@ -332,20 +318,39 @@ class PublicSecretsView(APIView): CamelCaseJSONRenderer, ] - @csrf_exempt - def dispatch(self, request, *args): - return super(PublicSecretsView, self).dispatch(request, *args) + def initial(self, request, *args, **kwargs): + super().initial(request, *args, **kwargs) + + # Determine the action based on the request method + action = METHOD_TO_ACTION.get(request.method) + if not action: + raise PermissionDenied(f"Unsupported HTTP method: {request.method}") + + # Perform permission check + account = None + if request.auth["auth_type"] == "User": + account = request.auth["org_member"].user + elif request.auth["auth_type"] == "ServiceAccount": + account = request.auth["service_account"] + + if account is not None: + env = request.auth["environment"] + organisation = env.app.organisation + + if not user_has_permission( + account, + action, + "Secrets", + organisation, + True, + request.auth.get("service_account") is not None, + ): + raise PermissionDenied( + f"You don't have permission to {action} secrets in this environment." + ) def get(self, request, *args, **kwargs): env = request.auth["environment"] - kwargs.update( - { - "action": "read", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -391,14 +396,6 @@ def get(self, request, *args, **kwargs): def post(self, request, *args, **kwargs): env = request.auth["environment"] - kwargs.update( - { - "action": "create", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -496,14 +493,6 @@ def post(self, request, *args, **kwargs): def put(self, request, *args, **kwargs): env = request.auth["environment"] - kwargs.update( - { - "action": "update", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: @@ -625,14 +614,6 @@ def put(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs): env = request.auth["environment"] - kwargs.update( - { - "action": "delete", - "resource": "Secrets", - "organisation": env.app.organisation, - "env": env, - } - ) # Check if SSE is enabled for this environment if not env.app.sse_enabled: From 8bcd512807e825f2f46ff6f906170466cee4a5c3 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 16 Nov 2024 15:31:32 +0530 Subject: [PATCH 80/92] fix: exception response for 403s --- backend/backend/exceptions.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/backend/backend/exceptions.py b/backend/backend/exceptions.py index 61e138ce..402cf057 100644 --- a/backend/backend/exceptions.py +++ b/backend/backend/exceptions.py @@ -1,18 +1,26 @@ from rest_framework.views import exception_handler -from django.http import HttpResponse +from django.core.exceptions import PermissionDenied +from django.http import JsonResponse, HttpResponse def custom_exception_handler(exc, context): - # Call REST framework's default exception handler first, - # to get the standard error response. - response = exception_handler(exc, context) - print("EXCEPTION", exc) + """ + Custom exception handler to modify 'PermissionDenied' responses. + """ + # Handle PermissionDenied + if isinstance(exc, PermissionDenied): + # Extract the custom message and replace the key + error_message = str(exc) if exc else "Permission denied." + return JsonResponse( + {"error": error_message}, # Change "detail" to "error" + status=403, + ) - # set 404 as default response code - status_code = 404 + # Call REST framework's default exception handler for other exceptions + response = exception_handler(exc, context) - # Now add the HTTP status code to the response. - if response is not None: - status_code = response.status_code + if response is not None and "detail" in response.data: + # If "detail" exists in other exceptions, you can modify it too + response.data["error"] = response.data.pop("detail") - return HttpResponse(status=status_code) + return response From 195678e1dc6d23a9f6aeacd4277883c7f9be35de Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 13:56:45 +0530 Subject: [PATCH 81/92] fix: last used resolver --- backend/backend/graphene/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index d69e6a3e..0b310f1e 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -226,7 +226,7 @@ class Meta: def resolve_last_used(self, info): event = ( SecretEvent.objects.filter(service_account_token=self) - .order_by("-timestamp") + .order_by("timestamp") .last() ) if event: From 936af8a3878a484cf2bbc2d518cd6b8f0c9be02b Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 13:57:20 +0530 Subject: [PATCH 82/92] fix: get role color --- frontend/graphql/queries/service-accounts/getServiceAccounts.gql | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql index 6246609e..755774b9 100644 --- a/frontend/graphql/queries/service-accounts/getServiceAccounts.gql +++ b/frontend/graphql/queries/service-accounts/getServiceAccounts.gql @@ -7,6 +7,7 @@ query GetServiceAccounts($orgId: ID!, $id: ID) { id name description + color permissions } createdAt From 2bc19a5b1c026795d170dfdf96251f9991f0ab80 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 14:01:39 +0530 Subject: [PATCH 83/92] fix: invite user link in command palette --- frontend/components/common/CommandPalette.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/common/CommandPalette.tsx b/frontend/components/common/CommandPalette.tsx index 9259182c..f43b6d59 100644 --- a/frontend/components/common/CommandPalette.tsx +++ b/frontend/components/common/CommandPalette.tsx @@ -163,7 +163,7 @@ const CommandPalette: React.FC = () => { name: 'Invite a User', description: 'Invite a new user to the organization', icon: , - action: () => handleNavigation(`/${activeOrganisation?.name}/members?invite=true`), + action: () => handleNavigation(`/${activeOrganisation?.name}/access/members?invite=true`), }) const externalResources: CommandItem[] = [ From ef46b4c4d82790185c3168486c17dafbc4de985c Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 14:01:50 +0530 Subject: [PATCH 84/92] chore: regenerate types --- frontend/apollo/gql.ts | 4 ++-- frontend/apollo/graphql.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index cd5164ed..b6365de6 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -102,7 +102,7 @@ const documents = { "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 history {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n ipAddress\n userAgent\n user {\n email\n username\n fullName\n avatarUrl\n }\n serviceToken {\n id\n name\n }\n serviceAccount {\n id\n name\n }\n eventType\n }\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 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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}": types.GetServiceAccountsDocument, + "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\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 }\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, "query GetCfPages($credentialId: ID!) {\n cloudflarePagesProjects(credentialId: $credentialId) {\n name\n deploymentId\n environments\n }\n}": types.GetCfPagesDocument, @@ -493,7 +493,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 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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"]; +export function graphql(source: "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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n }\n }\n}"): (typeof documents)["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 permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\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 e914a31a..16ecf890 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -2570,7 +2570,7 @@ export type GetServiceAccountsQueryVariables = Exact<{ }>; -export type GetServiceAccountsQuery = { __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, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, 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, appMemberships?: Array<{ __typename?: 'AppType', id: string, name: string, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null } | null> | null }; +export type GetServiceAccountsQuery = { __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, 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, appMemberships?: Array<{ __typename?: 'AppType', id: string, name: string, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null } | null> | null }; export type GetOrganisationSyncsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2761,7 +2761,7 @@ export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":" 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":"history"},"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":"version"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"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":"eventType"}}]}},{"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 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 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":"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":"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"}}]}},{"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"}}]}}]}}]}}]}}]} 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":"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":"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"}}]}},{"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"}}]}}]}}]}}]}}]} 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":"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; export const GetCfPagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCfPages"},"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":"cloudflarePagesProjects"},"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":"deploymentId"}},{"kind":"Field","name":{"kind":"Name","value":"environments"}}]}}]}}]} as unknown as DocumentNode; From 41a38cc80f3a73b727a1cb26dcc33e13d71ea80f Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 14:04:49 +0530 Subject: [PATCH 85/92] fix: only render "danger zone" if delete allowed --- .../service-accounts/[account]/page.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index 9fc01e72..c2d61f6c 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -301,14 +301,15 @@ export default function ServiceAccount({ params }: { params: { team: string; acc )}
-
-
-
Danger Zone
-
- These actions are destructive and cannot be reversed + {userCanDeleteSA && ( +
+
+
Danger Zone
+
+ These actions are destructive and cannot be reversed +
-
- {userCanDeleteSA && ( +
Delete account
@@ -318,8 +319,8 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
- )} -
+
+ )}
) From 4fbbb90d992ee08177b1bf69c7c678bf2ac1b7f9 Mon Sep 17 00:00:00 2001 From: Nimish Date: Tue, 19 Nov 2024 14:48:59 +0530 Subject: [PATCH 86/92] fix: get started onboarding link --- frontend/components/dashboard/GetStarted.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/dashboard/GetStarted.tsx b/frontend/components/dashboard/GetStarted.tsx index ec67fbee..73e92b8e 100644 --- a/frontend/components/dashboard/GetStarted.tsx +++ b/frontend/components/dashboard/GetStarted.tsx @@ -416,7 +416,7 @@ export const GetStarted = (props: { organisation: OrganisationType }) => { {!memberAdded && (
- + Date: Tue, 19 Nov 2024 19:03:35 +0530 Subject: [PATCH 87/92] fix: log row border width, alignment --- frontend/components/logs/SecretLogs.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/components/logs/SecretLogs.tsx b/frontend/components/logs/SecretLogs.tsx index 738aedfb..b4490d27 100644 --- a/frontend/components/logs/SecretLogs.tsx +++ b/frontend/components/logs/SecretLogs.tsx @@ -286,7 +286,7 @@ export default function SecretLogs(props: { app: string }) { {/* */} @@ -334,8 +334,10 @@ export default function SecretLogs(props: { app: string }) {
From 1b46ab2e5746d81f34f6bc6a1df6247d77323e45 Mon Sep 17 00:00:00 2001 From: Nimish <85357445+nimish-ks@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:19:11 +0530 Subject: [PATCH 88/92] Feat: collapsible sidebar (#389) * feat: added sidebar state local storage context util * feat: collapsible sidebar * feat: sidebar provider in providers * fix: misc updates to smooth transitions between states * feat: misc tweaks to orgs menu * fix: load stored state after component mount to prevent hydration mismatch --------- Co-authored-by: Rohan --- frontend/app/providers.tsx | 21 ++-- frontend/components/layout/Sidebar.tsx | 168 +++++++++++++++++-------- frontend/contexts/sidebarContext.tsx | 45 +++++++ 3 files changed, 175 insertions(+), 59 deletions(-) create mode 100644 frontend/contexts/sidebarContext.tsx diff --git a/frontend/app/providers.tsx b/frontend/app/providers.tsx index 4978c862..202a2832 100644 --- a/frontend/app/providers.tsx +++ b/frontend/app/providers.tsx @@ -5,6 +5,7 @@ import { SessionProvider } from 'next-auth/react' import { ApolloProvider } from '@apollo/client' import { graphQlClient } from '@/apollo/client' import { KeyringProvider } from '@/contexts/keyringContext' +import { SidebarProvider } from '@/contexts/sidebarContext' import { OrganisationProvider } from '@/contexts/organisationContext' import posthog from 'posthog-js' import { PostHogProvider } from 'posthog-js/react' @@ -21,15 +22,17 @@ export default function Providers({ children }: { children: React.ReactNode }) { return ( - - - - - {children} - - - - + + + + + + {children} + + + + + ) } diff --git a/frontend/components/layout/Sidebar.tsx b/frontend/components/layout/Sidebar.tsx index ec4c27f1..ef462518 100644 --- a/frontend/components/layout/Sidebar.tsx +++ b/frontend/components/layout/Sidebar.tsx @@ -4,19 +4,21 @@ import Link from 'next/link' import { usePathname } from 'next/navigation' import clsx from 'clsx' import { - FaChevronDown, FaCog, FaCubes, FaExchangeAlt, FaHome, - FaKey, FaPlus, FaUsersCog, FaProjectDiagram, + FaAngleDoubleLeft, + FaAngleDoubleRight, + FaChevronDown, } from 'react-icons/fa' import { organisationContext } from '@/contexts/organisationContext' +import { SidebarContext } from '@/contexts/sidebarContext' import { Fragment, useContext } from 'react' -import { OrganisationType } from '@/apollo/graphql' +import { ApiOrganisationPlanChoices, OrganisationType } from '@/apollo/graphql' import { Menu, Transition } from '@headlessui/react' import { Button } from '../common/Button' import { PlanLabel } from '../settings/organisation/PlanLabel' @@ -28,66 +30,121 @@ export type SidebarLinkT = { active: boolean } -const SidebarLink = (props: SidebarLinkT) => { - const { name, href, icon, active } = props - +const SidebarLink = ({ + name, + href, + icon, + active, + collapsed, +}: SidebarLinkT & { collapsed: boolean }) => { return ( - -
+
+
+
{icon}
+ + + {' '} + {name}{' '} + +
+ {collapsed && ( +
+ {name} +
)} - > -
{icon}
- {name}
) } const Sidebar = () => { + const { sidebarState, setSidebarState } = useContext(SidebarContext) + const collapsed = sidebarState === 'collapsed' const team = usePathname()?.split('/')[1] - const { organisations, activeOrganisation } = useContext(organisationContext) - const showOrgsMenu = organisations && organisations.length > 1 - const isOwner = organisations?.some((org) => org.role!.name!.toLowerCase() === 'owner') const OrgsMenu = () => { - const OrgLabel = () => ( -
-
-
- + const planStyle = () => { + if (activeOrganisation?.plan === ApiOrganisationPlanChoices.Fr) + return 'ring-neutral-500/40 bg-neutral-500/40 text-zinc-900 dark:bg-zinc-800 dark:text-neutral-500' + if (activeOrganisation?.plan === ApiOrganisationPlanChoices.Pr) + return 'ring-emerald-400/10 bg-emerald-400 text-zinc-900 dark:bg-emerald-400/10 dark:text-emerald-400' + if (activeOrganisation?.plan === ApiOrganisationPlanChoices.En) + return 'ring-amber-400/10 bg-amber-400 text-zinc-900 dark:bg-amber-400/10 dark:text-amber-400' + } + + const OrgLabel = ({ open }: { open?: boolean }) => ( +
+ {collapsed ? ( +
+ + {activeOrganisation?.name?.[0]?.toUpperCase()} + +
+ ) : ( +
+
+ +
+ + {activeOrganisation?.name} +
- + )} + {showOrgsMenu && !collapsed && ( + + )} + {collapsed && ( +
{activeOrganisation?.name} - -
+
+ )}
) if (!showOrgsMenu) return return ( - + {({ open }) => ( <> - -
- - -
+ + { leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - +
{organisations?.map((org: OrganisationType) => ( @@ -178,30 +235,41 @@ const Sidebar = () => { ] return ( -
+
diff --git a/frontend/contexts/sidebarContext.tsx b/frontend/contexts/sidebarContext.tsx new file mode 100644 index 00000000..9a519673 --- /dev/null +++ b/frontend/contexts/sidebarContext.tsx @@ -0,0 +1,45 @@ +import React, { createContext, useState, useEffect } from 'react' + +type SidebarState = 'expanded' | 'collapsed' + +interface SidebarContextValue { + sidebarState: SidebarState + setSidebarState: (state: SidebarState) => void +} + +const getInitialState = (): SidebarState => { + if (typeof window !== 'undefined' && window.localStorage) { + const storedState = window.localStorage.getItem('sidebar-state') + if (storedState === 'collapsed') { + return 'collapsed' + } + } + return 'expanded' // Default state is expanded +} + +export const SidebarContext = createContext({ + sidebarState: 'expanded', + setSidebarState: () => {}, +}) + +interface SidebarProviderProps { + children: React.ReactNode +} + +export const SidebarProvider: React.FC = ({ children }) => { + const [sidebarState, setSidebarState] = useState('expanded') + + useEffect(() => { + setSidebarState(getInitialState()) + }, []) + + useEffect(() => { + localStorage.setItem('sidebar-state', sidebarState) + }, [sidebarState]) + + return ( + + {children} + + ) +} From 94b15b697a6af504a4defdfcd3572f5f0df062c6 Mon Sep 17 00:00:00 2001 From: Rohan Chaturvedi Date: Sat, 23 Nov 2024 15:17:45 +0530 Subject: [PATCH 89/92] feat: update seat limits and usage (#393) * feat: consolidate seat quota logic * feat: misc updates to plan info display * refactor: org seat usage types * fix: misc copy and ui updates * feat: remove feature list on self-hosted --- backend/api/models.py | 13 +- .../graphene/mutations/service_accounts.py | 6 - backend/backend/graphene/types.py | 34 ++-- backend/backend/quotas.py | 50 ++--- frontend/apollo/gql.ts | 8 +- frontend/apollo/graphql.ts | 18 +- frontend/apollo/schema.graphql | 9 +- frontend/app/[team]/access/members/page.tsx | 2 +- .../CreateServiceAccountDialog.tsx | 27 ++- .../[team]/access/service-accounts/page.tsx | 1 - frontend/components/common/Accordion.tsx | 70 +++++++ .../settings/organisation/PlanInfo.tsx | 180 +++++++++++------- frontend/graphql/queries/getOrganisations.gql | 7 +- .../organisation/getOrganisationPlan.gql | 7 +- 14 files changed, 286 insertions(+), 146 deletions(-) create mode 100644 frontend/components/common/Accordion.tsx diff --git a/backend/api/models.py b/backend/api/models.py index 1b494048..193d3898 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -12,10 +12,10 @@ from api.services import Providers, ServiceConfig from api.tasks import trigger_sync_tasks from backend.quotas import ( + can_add_account, can_add_app, can_add_environment, can_add_service_token, - can_add_user, ) @@ -215,6 +215,14 @@ def delete(self, *args, **kwargs): self.save() +class ServiceAccountManager(models.Manager): + def create(self, *args, **kwargs): + organisation = kwargs.get("organisation") + if not can_add_account(organisation): + raise ValueError("Cannot add more accounts to this organisation's plan.") + return super().create(*args, **kwargs) + + class ServiceAccount(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) name = models.CharField(max_length=255) @@ -234,6 +242,7 @@ class ServiceAccount(models.Model): 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) + objects = ServiceAccountManager() class ServiceAccountHandler(models.Model): @@ -251,7 +260,7 @@ class ServiceAccountHandler(models.Model): class OrganisationMemberInviteManager(models.Manager): def create(self, *args, **kwargs): organisation = kwargs.get("organisation") - if not can_add_user(organisation): + if not can_add_account(organisation): raise ValueError("Cannot add more users to this organisation's plan.") return super().create(*args, **kwargs) diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index 834c7d71..b8768e95 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -1,4 +1,3 @@ -from backend.quotas import can_add_service_account import graphene from graphql import GraphQLError from api.models import ( @@ -55,11 +54,6 @@ def mutate( "You don't have the permissions required to create Service Accounts in this organisation" ) - if not can_add_service_account(org): - raise GraphQLError( - "You cannot add any more service accounts to this organisation" - ) - if handlers is None or len(handlers) == 0: raise GraphQLError("At least one service account handler must be provided") diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 0b310f1e..50989a20 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -39,13 +39,18 @@ from api.utils.access.roles import default_roles +class SeatsUsed(ObjectType): + users = graphene.Int() + service_accounts = graphene.Int() + total = graphene.Int() + + class OrganisationPlanType(ObjectType): name = graphene.String() max_users = graphene.Int() max_apps = graphene.Int() max_envs_per_app = graphene.Int() - user_count = graphene.Int() - service_account_count = graphene.Int() + seats_used = graphene.Field(SeatsUsed) app_count = graphene.Int() @@ -126,18 +131,23 @@ def resolve_plan_detail(self, info): plan = PLAN_CONFIG[self.plan] - plan["user_count"] = ( - OrganisationMember.objects.filter( + plan["seats_used"] = { + "users": ( + OrganisationMember.objects.filter( + organisation=self, deleted_at=None + ).count() + + OrganisationMemberInvite.objects.filter( + organisation=self, valid=True, expires_at__gte=timezone.now() + ).count() + ), + "service_accounts": ServiceAccount.objects.filter( organisation=self, deleted_at=None - ).count() - + OrganisationMemberInvite.objects.filter( - organisation=self, valid=True, expires_at__gte=timezone.now() - ).count() - ) + ).count(), + } - plan["service_account_count"] = ServiceAccount.objects.filter( - organisation=self, deleted_at=None - ).count() + plan["seats_used"]["total"] = ( + plan["seats_used"]["users"] + plan["seats_used"]["service_accounts"] + ) plan["app_count"] = App.objects.filter( organisation=self, deleted_at=None diff --git a/backend/backend/quotas.py b/backend/backend/quotas.py index 3642b4dc..bebb1593 100644 --- a/backend/backend/quotas.py +++ b/backend/backend/quotas.py @@ -10,8 +10,8 @@ PLAN_CONFIG = { "FR": { "name": "Free", - "max_users": 5 if CLOUD_HOSTED else 20, - "max_apps": 3 if CLOUD_HOSTED else 20, + "max_users": 5 if CLOUD_HOSTED else None, + "max_apps": 3 if CLOUD_HOSTED else None, "max_envs_per_app": 3, "max_tokens_per_app": 3, }, @@ -54,11 +54,12 @@ def can_add_app(organisation): return current_app_count < plan_limits["max_apps"] -def can_add_user(organisation): - """Check if a new user can be added to the organisation.""" +def can_add_account(organisation): + """Check if a new human or service account can be added to the organisation.""" OrganisationMember = apps.get_model("api", "OrganisationMember") OrganisationMemberInvite = apps.get_model("api", "OrganisationMemberInvite") + ServiceAccount = apps.get_model("api", "ServiceAccount") ActivatedPhaseLicense = apps.get_model("api", "ActivatedPhaseLicense") plan_limits = PLAN_CONFIG[organisation.plan] @@ -66,7 +67,8 @@ def can_add_user(organisation): organisation=organisation ).exists() - current_user_count = ( + # Calculate the current count of users and service accounts + current_human_user_count = ( OrganisationMember.objects.filter( organisation=organisation, deleted_at=None ).count() @@ -74,38 +76,12 @@ def can_add_user(organisation): organisation=organisation, valid=True, expires_at__gte=timezone.now() ).count() ) - - if license_exists: - license = ( - ActivatedPhaseLicense.objects.filter(organisation=organisation) - .order_by("-activated_at") - .first() - ) - user_limit = license.seats - - else: - user_limit = plan_limits["max_users"] - - if user_limit is None: - return True - return current_user_count < user_limit - - -def can_add_service_account(organisation): - """Check if a new service account can be added to the organisation.""" - - ServiceAccount = apps.get_model("api", "ServiceAccount") - ActivatedPhaseLicense = apps.get_model("api", "ActivatedPhaseLicense") - - plan_limits = PLAN_CONFIG[organisation.plan] - license_exists = ActivatedPhaseLicense.objects.filter( - organisation=organisation - ).exists() - - current_account_count = ServiceAccount.objects.filter( + current_service_account_count = ServiceAccount.objects.filter( organisation=organisation, deleted_at=None ).count() + total_account_count = current_human_user_count + current_service_account_count + # Determine the user limit if license_exists: license = ( ActivatedPhaseLicense.objects.filter(organisation=organisation) @@ -113,13 +89,15 @@ def can_add_service_account(organisation): .first() ) user_limit = license.seats - else: user_limit = plan_limits["max_users"] + # If there's no limit, allow unlimited additions if user_limit is None: return True - return current_account_count < user_limit + + # Check if the total account count is below the limit + return total_account_count < user_limit def can_add_environment(app): diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index b6365de6..aeba92a2 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -82,14 +82,14 @@ const documents = { "query GetAppKmsLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n kms {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n }\n kmsLogsCount(appId: $appId)\n}": types.GetAppKmsLogsDocument, "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n envType\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.GetAppsDocument, "query GetDashboard($organisationId: ID!) {\n apps(organisationId: $organisationId) {\n id\n sseEnabled\n }\n userTokens(organisationId: $organisationId) {\n id\n }\n organisationInvites(orgId: $organisationId) {\n id\n }\n organisationMembers(organisationId: $organisationId, role: null) {\n id\n }\n savedCredentials(orgId: $organisationId) {\n id\n }\n syncs(orgId: $organisationId) {\n id\n }\n}": types.GetDashboardDocument, - "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}": types.GetOrganisationsDocument, + "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}": types.GetOrganisationsDocument, "query CheckOrganisationNameAvailability($name: String!) {\n organisationNameAvailable(name: $name)\n}": types.CheckOrganisationNameAvailabilityDocument, "query GetGlobalAccessUsers($organisationId: ID!) {\n organisationGlobalAccessUsers(organisationId: $organisationId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetGlobalAccessUsersDocument, "query GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n fullName\n self\n }\n inviteeEmail\n }\n}": types.GetInvitesDocument, "query GetLicenseData {\n license {\n id\n customerName\n organisationName\n expiresAt\n plan\n seats\n isActivated\n organisationOwner {\n fullName\n email\n }\n }\n}": types.GetLicenseDataDocument, "query GetOrgLicense($organisationId: ID!) {\n organisationLicense(organisationId: $organisationId) {\n id\n customerName\n issuedAt\n expiresAt\n activatedAt\n plan\n seats\n tokens\n }\n}": types.GetOrgLicenseDocument, "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n id\n role {\n id\n name\n description\n permissions\n color\n }\n identityKey\n email\n fullName\n avatarUrl\n createdAt\n self\n }\n}": types.GetOrganisationMembersDocument, - "query GetOrganisationPlan($organisationId: ID!) {\n organisationPlan(organisationId: $organisationId) {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n}": types.GetOrganisationPlanDocument, + "query GetOrganisationPlan($organisationId: ID!) {\n organisationPlan(organisationId: $organisationId) {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n}": types.GetOrganisationPlanDocument, "query GetRoles($orgId: ID!) {\n roles(orgId: $orgId) {\n id\n name\n description\n color\n permissions\n isDefault\n }\n}": types.GetRolesDocument, "query VerifyInvite($inviteId: ID!) {\n validateInvite(inviteId: $inviteId) {\n id\n organisation {\n id\n name\n }\n inviteeEmail\n invitedBy {\n email\n }\n apps {\n id\n name\n }\n }\n}": types.VerifyInviteDocument, "query GetAppEnvironments($appId: ID!, $memberId: ID, $memberType: MemberType) {\n appEnvironments(\n appId: $appId\n environmentId: null\n memberId: $memberId\n memberType: $memberType\n ) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n app {\n name\n id\n }\n secretCount\n folderCount\n index\n members {\n email\n fullName\n avatarUrl\n }\n }\n sseEnabled(appId: $appId)\n serverPublicKey\n}": types.GetAppEnvironmentsDocument, @@ -413,7 +413,7 @@ export function graphql(source: "query GetDashboard($organisationId: ID!) {\n a /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"]; +export function graphql(source: "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -441,7 +441,7 @@ export function graphql(source: "query GetOrganisationMembers($organisationId: I /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetOrganisationPlan($organisationId: ID!) {\n organisationPlan(organisationId: $organisationId) {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n}"): (typeof documents)["query GetOrganisationPlan($organisationId: ID!) {\n organisationPlan(organisationId: $organisationId) {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n userCount\n serviceAccountCount\n appCount\n }\n}"]; +export function graphql(source: "query GetOrganisationPlan($organisationId: ID!) {\n organisationPlan(organisationId: $organisationId) {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n}"): (typeof documents)["query GetOrganisationPlan($organisationId: ID!) {\n organisationPlan(organisationId: $organisationId) {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\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 16ecf890..09a36cba 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1147,8 +1147,7 @@ export type OrganisationPlanType = { maxEnvsPerApp?: Maybe; maxUsers?: Maybe; name?: Maybe; - serviceAccountCount?: Maybe; - userCount?: Maybe; + seatsUsed?: Maybe; }; export type OrganisationType = { @@ -1553,6 +1552,13 @@ export type RotateAppKeysMutation = { app?: Maybe; }; +export type SeatsUsed = { + __typename?: 'SeatsUsed'; + serviceAccounts?: Maybe; + total?: Maybe; + users?: Maybe; +}; + export type SecretEventType = { __typename?: 'SecretEventType'; comment: Scalars['String']['output']; @@ -2422,7 +2428,7 @@ export type GetDashboardQuery = { __typename?: 'Query', apps?: Array<{ __typenam export type GetOrganisationsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetOrganisationsQuery = { __typename?: 'Query', organisations?: Array<{ __typename?: 'OrganisationType', id: string, name: string, identityKey: string, createdAt?: any | null, plan: ApiOrganisationPlanChoices, memberId?: string | null, keyring?: string | null, recovery?: string | null, planDetail?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, userCount?: number | null, serviceAccountCount?: number | null, appCount?: number | null } | null, role?: { __typename?: 'RoleType', name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null } | null> | null }; +export type GetOrganisationsQuery = { __typename?: 'Query', organisations?: Array<{ __typename?: 'OrganisationType', id: string, name: string, identityKey: string, createdAt?: any | null, plan: ApiOrganisationPlanChoices, memberId?: string | null, keyring?: string | null, recovery?: string | null, planDetail?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, appCount?: number | null, seatsUsed?: { __typename?: 'SeatsUsed', users?: number | null, serviceAccounts?: number | null, total?: number | null } | null } | null, role?: { __typename?: 'RoleType', name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null } | null> | null }; export type CheckOrganisationNameAvailabilityQueryVariables = Exact<{ name: Scalars['String']['input']; @@ -2470,7 +2476,7 @@ export type GetOrganisationPlanQueryVariables = Exact<{ }>; -export type GetOrganisationPlanQuery = { __typename?: 'Query', organisationPlan?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, userCount?: number | null, serviceAccountCount?: number | null, appCount?: number | null } | null }; +export type GetOrganisationPlanQuery = { __typename?: 'Query', organisationPlan?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, appCount?: number | null, seatsUsed?: { __typename?: 'SeatsUsed', users?: number | null, serviceAccounts?: number | null, total?: number | null } | null } | null }; export type GetRolesQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -2741,14 +2747,14 @@ export const GetAppDetailDocument = {"kind":"Document","definitions":[{"kind":"O export const GetAppKmsLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppKmsLogs"},"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":"start"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"end"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"start"},"value":{"kind":"Variable","name":{"kind":"Name","value":"start"}}},{"kind":"Argument","name":{"kind":"Name","value":"end"},"value":{"kind":"Variable","name":{"kind":"Name","value":"end"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"kms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"phaseNode"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"ipAddress"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"city"}},{"kind":"Field","name":{"kind":"Name","value":"phSize"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"kmsLogsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]}]}}]} as unknown as DocumentNode; export const GetAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetApps"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"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":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"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":"envType"}},{"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 GetDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"userTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationInvites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationMembers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"savedCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; -export const GetOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisations"},"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":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"planDetail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"userCount"}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccountCount"}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"memberId"}},{"kind":"Field","name":{"kind":"Name","value":"keyring"}},{"kind":"Field","name":{"kind":"Name","value":"recovery"}}]}}]}}]} as unknown as DocumentNode; +export const GetOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisations"},"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":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"planDetail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"seatsUsed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"users"}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"memberId"}},{"kind":"Field","name":{"kind":"Name","value":"keyring"}},{"kind":"Field","name":{"kind":"Name","value":"recovery"}}]}}]}}]} as unknown as DocumentNode; export const CheckOrganisationNameAvailabilityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheckOrganisationNameAvailability"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationNameAvailable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}]}]}}]} as unknown as DocumentNode; export const GetGlobalAccessUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetGlobalAccessUsers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationGlobalAccessUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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 GetInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetInvites"},"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":"organisationInvites"},"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}}]}}]}}]} as unknown as DocumentNode; export const GetLicenseDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetLicenseData"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"license"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"customerName"}},{"kind":"Field","name":{"kind":"Name","value":"organisationName"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"seats"}},{"kind":"Field","name":{"kind":"Name","value":"isActivated"}},{"kind":"Field","name":{"kind":"Name","value":"organisationOwner"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetOrgLicenseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrgLicense"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationLicense"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"customerName"}},{"kind":"Field","name":{"kind":"Name","value":"issuedAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"activatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"seats"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationMembersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationMembers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"role"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationMembers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"Variable","name":{"kind":"Name","value":"role"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}}]} as unknown as DocumentNode; -export const GetOrganisationPlanDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationPlan"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationPlan"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"userCount"}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccountCount"}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}}]}}]} as unknown as DocumentNode; +export const GetOrganisationPlanDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationPlan"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationPlan"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"seatsUsed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"users"}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}}]}}]} as unknown as DocumentNode; export const GetRolesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRoles"},"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":"roles"},"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}}]} as unknown as DocumentNode; export const VerifyInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"VerifyInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validateInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppEnvironments"},"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":"memberId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"memberType"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MemberType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"NullValue"}},{"kind":"Argument","name":{"kind":"Name","value":"memberId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberId"}}},{"kind":"Argument","name":{"kind":"Name","value":"memberType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"memberType"}}}],"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":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"secretCount"}},{"kind":"Field","name":{"kind":"Name","value":"folderCount"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}]},{"kind":"Field","name":{"kind":"Name","value":"serverPublicKey"}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index c27476c6..6b8a00db 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -100,11 +100,16 @@ type OrganisationPlanType { maxUsers: Int maxApps: Int maxEnvsPerApp: Int - userCount: Int - serviceAccountCount: Int + seatsUsed: SeatsUsed appCount: Int } +type SeatsUsed { + users: Int + serviceAccounts: Int + total: Int +} + type PhaseLicenseType { id: String customerName: String diff --git a/frontend/app/[team]/access/members/page.tsx b/frontend/app/[team]/access/members/page.tsx index d7639739..81d32ce7 100644 --- a/frontend/app/[team]/access/members/page.tsx +++ b/frontend/app/[team]/access/members/page.tsx @@ -316,7 +316,7 @@ const InviteDialog = (props: { organisationId: string }) => { const upsell = isCloudHosted() && activeOrganisation?.plan === ApiOrganisationPlanChoices.Fr && - data?.organisationPlan.userCount === data?.organisationPlan.maxUsers + data?.organisationPlan.seatsUsed.total === data?.organisationPlan.maxUsers const [createInvite, { error, loading: mutationLoading }] = useMutation(InviteMember) diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index b0878342..2b1f2d68 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -1,4 +1,4 @@ -import { OrganisationMemberType, RoleType } from '@/apollo/graphql' +import { ApiOrganisationPlanChoices, OrganisationMemberType, RoleType } from '@/apollo/graphql' import GenericDialog from '@/components/common/GenericDialog' import { Fragment, useContext, useEffect, useRef, useState } from 'react' import { FaChevronDown, FaPlus } from 'react-icons/fa' @@ -7,6 +7,7 @@ import { GetServiceAccountHandlers } from '@/graphql/queries/service-accounts/ge import { GetRoles } from '@/graphql/queries/organisation/getRoles.gql' import { GetServerKey } from '@/graphql/queries/syncing/getServerKey.gql' import { CreateServiceAccountOp } from '@/graphql/mutations/service-accounts/createServiceAccount.gql' +import { GetOrganisationPlan } from '@/graphql/queries/organisation/getOrganisationPlan.gql' import { organisationContext } from '@/contexts/organisationContext' import { useMutation, useQuery } from '@apollo/client' import { @@ -22,6 +23,8 @@ import clsx from 'clsx' import { ToggleSwitch } from '@/components/common/ToggleSwitch' import { Button } from '@/components/common/Button' import { toast } from 'react-toastify' +import { isCloudHosted } from '@/utils/appConfig' +import { UpsellDialog } from '@/components/settings/organisation/UpsellDialog' const bip39 = require('bip39') @@ -38,6 +41,12 @@ export const CreateServiceAccountDialog = () => { skip: !organisation, }) + const { data } = useQuery(GetOrganisationPlan, { + variables: { organisationId: organisation?.id }, + fetchPolicy: 'cache-and-network', + skip: !organisation, + }) + const [createServiceAccount] = useMutation(CreateServiceAccountOp) const { data: serverKeyData } = useQuery(GetServerKey) @@ -54,6 +63,11 @@ export const CreateServiceAccountDialog = () => { setThirdParty(false) } + const upsell = + isCloudHosted() && + organisation?.plan === ApiOrganisationPlanChoices.Fr && + data?.organisationPlan.seatsUsed.total === data?.organisationPlan.maxUsers + const roleOptions = roleData?.roles.filter( (option: RoleType) => option.name !== 'Owner' && option.name !== 'Admin' @@ -139,6 +153,17 @@ export const CreateServiceAccountDialog = () => { }) } + if (upsell) + return ( + + Create Service Account + + } + /> + ) + return ( React.ReactNode // Custom button content with open state + defaultOpen?: boolean + className?: string +} + +const Accordion: React.FC = ({ + title, + description, + children, + buttonContent, + defaultOpen = false, + className = '', +}) => { + return ( + + {({ open }) => ( + <> + + {buttonContent ? ( + buttonContent(open) // Render custom content with access to the `open` state + ) : ( +
+
+
{title}
+ {description &&
{description}
} +
+
+ ▼ +
+
+ )} +
+ + + {children} + + + )} +
+ ) +} + +export default Accordion diff --git a/frontend/components/settings/organisation/PlanInfo.tsx b/frontend/components/settings/organisation/PlanInfo.tsx index f3d51686..26184630 100644 --- a/frontend/components/settings/organisation/PlanInfo.tsx +++ b/frontend/components/settings/organisation/PlanInfo.tsx @@ -10,6 +10,7 @@ import { calculatePercentage } from '@/utils/dataUnits' import { Button } from '@/components/common/Button' import { FaCheckCircle, + FaChevronDown, FaCog, FaCube, FaCubes, @@ -28,15 +29,16 @@ import { useSearchParams } from 'next/navigation' import { PostCheckoutScreen } from '@/ee/billing/PostCheckoutScreen' import { UpsellDialog } from './UpsellDialog' import { userHasPermission } from '@/utils/access/permissions' +import Accordion from '@/components/common/Accordion' +import clsx from 'clsx' const plansInfo = { FR: { id: ApiOrganisationPlanChoices.Fr, name: 'Free', description: 'Try Phase without any commitments.', - seats: isCloudHosted() ? '5 Users' : 'Unlimited Users', + seats: isCloudHosted() ? '5 Users / Service Accounts' : 'Unlimited Users', apps: isCloudHosted() ? '3 Apps' : 'Unlimited Apps', - tokens: isCloudHosted() ? '3 Service Tokens per app' : 'Unlimited Service Tokens per app', featureSummary: [ 'End-to-end Encryption', 'Google/GitHub/Gitlab SSO', @@ -47,7 +49,6 @@ const plansInfo = { 'Community Support', ], notIncluded: [ - ...['SAML SSO', 'Priority Support'], ...(isCloudHosted() ? [ '90-day audit log retention', @@ -64,7 +65,6 @@ const plansInfo = { name: 'Pro', seats: 'Unlimited Users', apps: 'Unlimited Apps', - tokens: isCloudHosted() ? '10 Service Tokens per app' : 'Unlimited Service Tokens per app', featureSummary: [ 'End-to-end Encryption', 'Google/GitHub/Gitlab SSO', @@ -75,10 +75,7 @@ const plansInfo = { 'Priority Support', ], notIncluded: [ - ...['SAML SSO', 'Dedicated Support'], - ...(isCloudHosted() - ? ['Unlimited audit log retention', 'Unlimited Environments', 'Unlimited Service Tokens'] - : []), + ...(isCloudHosted() ? ['Unlimited audit log retention', 'Unlimited Environments'] : []), ], }, EN: { @@ -88,10 +85,9 @@ const plansInfo = { 'Secure existing data in your enterprise workload. Get full onboarding and priority technical support.', seats: 'Unlimited Users', apps: 'Unlimited Apps', - tokens: 'Unlimited Service Tokens per app', featureSummary: [ 'End-to-end Encryption', - 'Google/GitHub/Gitlab/SAML SSO', + 'Google/GitHub/Gitlab SSO', 'Role-based Access Control', 'Secret Versioning', 'Secret Referencing', @@ -108,7 +104,7 @@ const PlanFeatureItem = (props: { iconType: 'check' | 'cross' | 'user' | 'app' | 'env' | 'key' }) => { return ( -
+
{props.iconType === 'check' && } {props.iconType === 'cross' && } {props.iconType === 'user' && } @@ -145,20 +141,28 @@ export const PlanInfo = () => { const license = (): ActivatedPhaseLicenseType | null => licenseData?.organisationLicense || null + const seatsUsed = data ? data.organisationPlan.seatsUsed.total : 0 + + const seatLimit = data ? license()?.seats || data.organisationPlan.maxUsers : undefined + const appQuotaUsage = data ? calculatePercentage(data.organisationPlan.appCount, data.organisationPlan.maxApps) : 0 + const seatQuotaUsage = data + ? calculatePercentage(seatsUsed, license()?.seats || data.organisationPlan.maxUsers) + : 0 + const memberQuotaUsage = data ? calculatePercentage( - data.organisationPlan.userCount, + data.organisationPlan.seatsUsed.users, license()?.seats || data.organisationPlan.maxUsers ) : 0 const serviceAccountQuotaUsage = data ? calculatePercentage( - data.organisationPlan.serviceAccountCount, + data.organisationPlan.seatsUsed.serviceAccounts, license()?.seats || data.organisationPlan.maxUsers ) : 0 @@ -200,20 +204,16 @@ export const PlanInfo = () => {
- {planInfo && ( + {planInfo && isCloudHosted() && (
- {license()?.seats ? `${license()?.seats} Users` : planInfo.seats} + {license()?.seats ? `${license()?.seats} Users / Service Accounts` : planInfo.seats} {planInfo.apps} - - {license()?.tokens - ? `${license()?.tokens} Service Tokens per App` - : planInfo.tokens} - + {planInfo.featureSummary.map((feature) => ( {feature} @@ -236,21 +236,107 @@ export const PlanInfo = () => {
-
Usage
+
+
Usage
+
+ Details of seat and app quota usage for your Organisation plan +
+
+ + ( +
+
+
+
Seats
+ +
+
{`${seatsUsed} ${seatLimit ? `of ${seatLimit}` : ''} Seats used`}
+
+ {seatLimit && ( + + )} +
+ )} + > +
+
+
+
+
Members
+ + + +
+ +
{`${data.organisationPlan.seatsUsed.users} Seats used`}
+
+ {seatLimit && ( + + )} +
+ +
+
+
+
+ Service Accounts +
+ + + +
+ +
{`${data.organisationPlan.seatsUsed.serviceAccounts} Seats used`}
+
+ {seatLimit && ( + + )} +
+
+
Apps
{`${data.organisationPlan.appCount} ${data.organisationPlan.maxApps ? `of ${data.organisationPlan.maxApps}` : ''} Apps used`}
- {activeOrganisation.plan === ApiOrganisationPlanChoices.Fr && ( + {data.organisationPlan.maxApps && ( )}
@@ -261,54 +347,6 @@ export const PlanInfo = () => {
- -
-
-
Members
-
{`${data.organisationPlan.userCount} ${license()?.seats || data.organisationPlan.maxUsers ? `of ${license()?.seats || data.organisationPlan.maxUsers}` : ''} Seats used`}
-
- {(activeOrganisation.plan === ApiOrganisationPlanChoices.Fr || license()?.seats) && ( - - )} -
- - - -
-
- -
-
-
Service Accounts
-
{`${data.organisationPlan.serviceAccountCount} ${license()?.seats || data.organisationPlan.maxUsers ? `of ${license()?.seats || data.organisationPlan.maxUsers}` : ''} Seats used`}
-
- {(activeOrganisation.plan === ApiOrganisationPlanChoices.Fr || license()?.seats) && ( - - )} -
- - - -
-
{searchParams?.get('stripe_session_id') && ( diff --git a/frontend/graphql/queries/getOrganisations.gql b/frontend/graphql/queries/getOrganisations.gql index 7a168447..e443bca4 100644 --- a/frontend/graphql/queries/getOrganisations.gql +++ b/frontend/graphql/queries/getOrganisations.gql @@ -10,8 +10,11 @@ query GetOrganisations { maxUsers maxApps maxEnvsPerApp - userCount - serviceAccountCount + seatsUsed { + users + serviceAccounts + total + } appCount } role { diff --git a/frontend/graphql/queries/organisation/getOrganisationPlan.gql b/frontend/graphql/queries/organisation/getOrganisationPlan.gql index f8f7e1fb..bd5e7244 100644 --- a/frontend/graphql/queries/organisation/getOrganisationPlan.gql +++ b/frontend/graphql/queries/organisation/getOrganisationPlan.gql @@ -4,8 +4,11 @@ query GetOrganisationPlan($organisationId: ID!) { maxUsers maxApps maxEnvsPerApp - userCount - serviceAccountCount + seatsUsed { + users + serviceAccounts + total + } appCount } } From f58fa86a7186f3a94c2011af36483adf72d533e2 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Nov 2024 19:41:23 +0530 Subject: [PATCH 90/92] chore: bumped version --- backend/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/version.txt b/backend/version.txt index ee14c9d6..873e6cc6 100644 --- a/backend/version.txt +++ b/backend/version.txt @@ -1 +1 @@ -v2.33.0 +v2.34.0 From f866c8e0b1a3302e1df813a14cad79116c3c2d80 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Nov 2024 19:42:56 +0530 Subject: [PATCH 91/92] fix: token label --- .../[account]/_components/CreateServiceAccountTokenDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx index 3214f2e3..e8a943ee 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx @@ -202,7 +202,7 @@ export const CreateServiceAccountTokenDialog = ({
- user token + Service token
{cliSAToken && ( From 92c72a88a74628101072537586b95e66dc1a1ef5 Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 23 Nov 2024 19:45:23 +0530 Subject: [PATCH 92/92] fix: remove the inaccurate token access note --- .../[account]/_components/CreateServiceAccountTokenDialog.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx index e8a943ee..98349c65 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/CreateServiceAccountTokenDialog.tsx @@ -155,9 +155,6 @@ export const CreateServiceAccountTokenDialog = ({
{name}
{humanReadableExpiry(expiry)}
-
- This token has access to all apps and environments accessible by you. -