From 4baee31b991e889810250be29c489aebf7e09054 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 14:47:16 +0530 Subject: [PATCH 001/161] feat: add models and migrations --- ...t_secret_secrettag_secretevent_and_more.py | 106 ++++++++++++ ...t_token_environmentsecret_name_and_more.py | 60 +++++++ ..._remove_secretevent_collection_and_more.py | 27 ++++ ...0020_remove_organisation_owner_and_more.py | 56 +++++++ .../0021_remove_secretevent_timestamp.py | 17 ++ .../migrations/0022_secretevent_timestamp.py | 20 +++ backend/api/models.py | 152 +++++++++++++++++- 7 files changed, 434 insertions(+), 4 deletions(-) create mode 100644 backend/api/migrations/0017_environment_secret_secrettag_secretevent_and_more.py create mode 100644 backend/api/migrations/0018_rename_environment_token_environmentsecret_name_and_more.py create mode 100644 backend/api/migrations/0019_remove_secret_user_remove_secretevent_collection_and_more.py create mode 100644 backend/api/migrations/0020_remove_organisation_owner_and_more.py create mode 100644 backend/api/migrations/0021_remove_secretevent_timestamp.py create mode 100644 backend/api/migrations/0022_secretevent_timestamp.py diff --git a/backend/api/migrations/0017_environment_secret_secrettag_secretevent_and_more.py b/backend/api/migrations/0017_environment_secret_secrettag_secretevent_and_more.py new file mode 100644 index 000000000..f9a0c1149 --- /dev/null +++ b/backend/api/migrations/0017_environment_secret_secrettag_secretevent_and_more.py @@ -0,0 +1,106 @@ +# Generated by Django 4.2.3 on 2023-07-31 10:52 + +from django.conf import settings +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0016_organisation_plan'), + ] + + operations = [ + migrations.CreateModel( + name='Environment', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('env_type', models.CharField(choices=[('dev', 'Development'), ('staging', 'Staging'), ('prod', 'Production')], default='dev', max_length=7)), + ('wrapped_seed', models.CharField(max_length=208)), + ('wrapped_salt', models.CharField(max_length=208)), + ('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)), + ('is_deleted', models.BooleanField(default=False)), + ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.app')), + ], + ), + migrations.CreateModel( + name='Secret', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('collection', models.TextField(blank=True, null=True)), + ('key', models.TextField()), + ('key_digest', models.TextField()), + ('value', models.TextField()), + ('version', models.IntegerField(default=1)), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=64), size=10)), + ('comment', models.TextField()), + ('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)), + ('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='SecretTag', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisation')), + ], + ), + migrations.CreateModel( + name='SecretEvent', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('collection', models.TextField(blank=True, null=True)), + ('key', models.TextField()), + ('key_digest', models.TextField()), + ('value', models.TextField()), + ('version', models.IntegerField(default=1)), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=64), size=10)), + ('comment', models.TextField()), + ('event_type', models.CharField(choices=[('C', 'Create'), ('R', 'Read'), ('U', 'Update'), ('D', 'Delete')], default='C', max_length=1)), + ('timestamp', models.BigIntegerField()), + ('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')), + ('secret', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.secret')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='EnvironmentSecret', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('identity_key', models.CharField(max_length=256)), + ('environment_token', models.CharField(max_length=64)), + ('wrapped_key_share', models.CharField(max_length=406)), + ('token', models.CharField(max_length=64)), + ('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)), + ('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='EnvironmentKey', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('identity_key', models.CharField(max_length=256)), + ('environment_token', models.CharField(max_length=64)), + ('wrapped_seed', models.CharField(max_length=208)), + ('wrapped_salt', models.CharField(max_length=208)), + ('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)), + ('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/api/migrations/0018_rename_environment_token_environmentsecret_name_and_more.py b/backend/api/migrations/0018_rename_environment_token_environmentsecret_name_and_more.py new file mode 100644 index 000000000..1f098d870 --- /dev/null +++ b/backend/api/migrations/0018_rename_environment_token_environmentsecret_name_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.3 on 2023-08-01 07:57 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0017_environment_secret_secrettag_secretevent_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='environmentsecret', + old_name='environment_token', + new_name='name', + ), + migrations.RemoveField( + model_name='environmentkey', + name='environment_token', + ), + migrations.RemoveField( + model_name='secret', + name='collection', + ), + migrations.AddField( + model_name='secrettag', + name='created_at', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='secrettag', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='secrettag', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + migrations.CreateModel( + name='SecretFolder', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('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)), + ('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')), + ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.secretfolder')), + ], + ), + migrations.AddField( + model_name='secret', + name='folder', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.secretfolder'), + ), + ] diff --git a/backend/api/migrations/0019_remove_secret_user_remove_secretevent_collection_and_more.py b/backend/api/migrations/0019_remove_secret_user_remove_secretevent_collection_and_more.py new file mode 100644 index 000000000..cc4af5830 --- /dev/null +++ b/backend/api/migrations/0019_remove_secret_user_remove_secretevent_collection_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.3 on 2023-08-02 07:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0018_rename_environment_token_environmentsecret_name_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='secret', + name='user', + ), + migrations.RemoveField( + model_name='secretevent', + name='collection', + ), + migrations.AddField( + model_name='secretevent', + name='folder', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.secretfolder'), + ), + ] diff --git a/backend/api/migrations/0020_remove_organisation_owner_and_more.py b/backend/api/migrations/0020_remove_organisation_owner_and_more.py new file mode 100644 index 000000000..f0a16b88d --- /dev/null +++ b/backend/api/migrations/0020_remove_organisation_owner_and_more.py @@ -0,0 +1,56 @@ +# Generated by Django 4.2.3 on 2023-08-04 09:30 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + +def migrate_org_owners(apps, schema_editor): + OrgModel = apps.get_model('api', 'Organisation') + OrgMemberModel = apps.get_model('api', 'OrganisationMember') + + for org in OrgModel.objects.all(): + OrgMemberModel.objects.create(user=org.owner, organisation=org, role='owner', identity_key=org.identity_key, created_at=org.created_at) + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0019_remove_secret_user_remove_secretevent_collection_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='OrganisationMember', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('role', models.CharField(choices=[('owner', 'Owner'), ('admin', 'Admin'), ('dev', 'Developer')], default='dev', max_length=5)), + ('identity_key', models.CharField(blank=True, max_length=256, null=True)), + ('wrapped_keyring', models.TextField(blank=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('deleted_at', models.DateTimeField(auto_now=True)), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='users', to='api.organisation')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organisation', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AlterField( + model_name='environmentkey', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember'), + ), + migrations.AlterField( + model_name='environmentsecret', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember'), + ), + migrations.AlterField( + model_name='secretevent', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.organisationmember'), + ), + migrations.RunPython(migrate_org_owners), + migrations.RemoveField( + model_name='organisation', + name='owner', + ), + ] diff --git a/backend/api/migrations/0021_remove_secretevent_timestamp.py b/backend/api/migrations/0021_remove_secretevent_timestamp.py new file mode 100644 index 000000000..936f72816 --- /dev/null +++ b/backend/api/migrations/0021_remove_secretevent_timestamp.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.3 on 2023-08-04 09:42 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0020_remove_organisation_owner_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='secretevent', + name='timestamp', + ), + ] diff --git a/backend/api/migrations/0022_secretevent_timestamp.py b/backend/api/migrations/0022_secretevent_timestamp.py new file mode 100644 index 000000000..ca51b3695 --- /dev/null +++ b/backend/api/migrations/0022_secretevent_timestamp.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.3 on 2023-08-04 09:54 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0021_remove_secretevent_timestamp'), + ] + + operations = [ + migrations.AddField( + model_name='secretevent', + name='timestamp', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index c5f50c7d9..5bbab03cb 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.contrib.postgres.fields import ArrayField from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from uuid import uuid4 from backend.api.kv import write @@ -74,8 +75,6 @@ class Organisation(models.Model): ] id = models.TextField(default=uuid4, primary_key=True, editable=False) - owner = models.ForeignKey( - CustomUser, related_name='organisation', on_delete=models.CASCADE) name = models.CharField(max_length=64, unique=True) identity_key = models.CharField(max_length=256) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) @@ -85,16 +84,41 @@ class Organisation(models.Model): choices=PLAN_TIERS, default=FREE_PLAN, ) - list_display = ('owner', 'name', 'identity_key', 'id') + list_display = ('name', 'identity_key', 'id') def __str__(self): return self.name +class OrganisationMember(models.Model): + OWNER = 'owner' + ADMIN = 'admin' + DEVELOPER = 'dev' + + USER_ROLES = [ + (OWNER, 'Owner'), + (ADMIN, 'Admin'), + (DEVELOPER, 'Developer') + ] + + id = models.TextField(default=uuid4, primary_key=True, editable=False) + user = models.ForeignKey( + CustomUser, related_name='organisation', on_delete=models.CASCADE) + organisation = models.ForeignKey(Organisation, related_name='users', on_delete=models.CASCADE) + role = models.CharField( + max_length=5, + choices=USER_ROLES, + default=DEVELOPER, + ) + identity_key = models.CharField(max_length=256, null=True, blank=True) + wrapped_keyring = models.TextField(blank=True) + created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(auto_now=True) class App(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) - name = name = models.CharField(max_length=64) + name = models.CharField(max_length=64) identity_key = models.CharField(max_length=256) app_version = models.IntegerField(null=False, blank=False, default=1) app_token = models.CharField(max_length=64) @@ -122,3 +146,123 @@ def save(self, *args, **kwargs): def __str__(self): return self.name + +class Environment(models.Model): + + DEVELOPMENT = "dev" + STAGING = "staging" + PRODUCTION = "prod" + + ENV_TYPES = [ + (DEVELOPMENT, 'Development'), + (STAGING, 'Staging'), + (PRODUCTION, 'Production') + ] + + id = models.TextField(default=uuid4, primary_key=True, editable=False) + app = models.ForeignKey(App, on_delete=models.CASCADE) + name = models.CharField(max_length=64) + env_type = models.CharField( + max_length=7, + choices=ENV_TYPES, + default=DEVELOPMENT, + ) + wrapped_seed = models.CharField(max_length=208) + wrapped_salt = models.CharField(max_length=208) + 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) + is_deleted = models.BooleanField(default=False) + +class EnvironmentKey(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + environment = models.ForeignKey(Environment, on_delete=models.CASCADE) + user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) + identity_key = models.CharField(max_length=256) + wrapped_seed = models.CharField(max_length=208) + wrapped_salt = models.CharField(max_length=208) + 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) + +class EnvironmentSecret(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + environment = models.ForeignKey(Environment, on_delete=models.CASCADE) + user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) + 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, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(blank=True, null=True) + +class SecretFolder(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + environment = models.ForeignKey(Environment, on_delete=models.CASCADE) + parent = models.ForeignKey('self', on_delete=models.CASCADE) + name = models.CharField(max_length=64) + 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) + +class SecretTag(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) + name = models.CharField(max_length=64) + 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) + +class Secret(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + environment = models.ForeignKey(Environment, on_delete=models.CASCADE) + folder = models.ForeignKey(SecretFolder, on_delete=models.CASCADE, null=True) + key = models.TextField() + key_digest = models.TextField() + value = models.TextField() + version = models.IntegerField(default=1) + tags = ArrayField( + models.CharField(max_length=64), + size=10, + ) + comment = models.TextField() + 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) + + +class SecretEvent(models.Model): + + CREATE = "C" + READ = "R" + UPDATE = "U" + DELETE = "D" + + EVENT_TYPES = [ + (CREATE, 'Create'), + (READ, 'Read'), + (UPDATE, 'Update'), + (DELETE, 'Delete') + ] + + id = models.TextField(default=uuid4, primary_key=True, editable=False) + secret = models.ForeignKey(Secret, on_delete=models.CASCADE) + environment = models.ForeignKey(Environment, on_delete=models.CASCADE) + folder = models.ForeignKey(SecretFolder, on_delete=models.CASCADE, null=True) + user = models.ForeignKey(OrganisationMember, on_delete=models.SET_NULL, blank=True, null=True) + key = models.TextField() + key_digest = models.TextField() + value = models.TextField() + version = models.IntegerField(default=1) + tags = ArrayField( + models.CharField(max_length=64), + size=10, + ) + comment = models.TextField() + event_type = models.CharField( + max_length=1, + choices=EVENT_TYPES, + default=CREATE, + ) + timestamp = models.DateTimeField(auto_now_add=True) From 3229aa2b7cb3af175d239eae90d50b0990647d24 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 14:47:36 +0530 Subject: [PATCH 002/161] feat: add permission utils --- backend/backend/graphene/utils/permissions.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 backend/backend/graphene/utils/permissions.py diff --git a/backend/backend/graphene/utils/permissions.py b/backend/backend/graphene/utils/permissions.py new file mode 100644 index 000000000..31099cccb --- /dev/null +++ b/backend/backend/graphene/utils/permissions.py @@ -0,0 +1,18 @@ +from api.models import App, Environment, EnvironmentKey, Organisation, OrganisationMember +from graphql import GraphQLError + +def user_is_admin(user_id, org_id): + admin_roles = [OrganisationMember.OWNER, OrganisationMember.ADMIN] + member = OrganisationMember.objects.get(user__id=user_id, organisation__id=org_id) + return member.role in admin_roles + +def user_is_org_member(user_id, org_id): + return OrganisationMember.objects.filter(user__id=user_id, organisation__id=org_id).exists() + +def user_can_access_app(user_id, app_id): + org_memberships = OrganisationMember.objects.filter(user_id=user_id) + app = App.objects.get(id=app_id) + return app.organisation.id in [membership.organisation.id for membership in org_memberships] + +def user_can_access_environment(user_id, env_id): + return EnvironmentKey.objects.filter(user_id=user_id, environment_id=env_id).exists() From d51024c541706fb19629dea4d7f91fa03306c69d Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 14:48:26 +0530 Subject: [PATCH 003/161] refactor: split graphene types and resolvers --- backend/backend/graphene/mutations/app.py | 110 ++++++++ .../graphene/mutations/organisation.py | 73 ++++++ backend/backend/graphene/types.py | 102 ++++++++ backend/backend/schema.py | 240 +++--------------- 4 files changed, 325 insertions(+), 200 deletions(-) create mode 100644 backend/backend/graphene/mutations/app.py create mode 100644 backend/backend/graphene/mutations/organisation.py create mode 100644 backend/backend/graphene/types.py diff --git a/backend/backend/graphene/mutations/app.py b/backend/backend/graphene/mutations/app.py new file mode 100644 index 000000000..5e930f347 --- /dev/null +++ b/backend/backend/graphene/mutations/app.py @@ -0,0 +1,110 @@ +from backend.api.kv import delete, purge +from backend.graphene.utils.permissions import user_can_access_app, user_is_org_member +from ee.feature_flags import allow_new_app +import graphene +from django.utils import timezone +from graphql import GraphQLError +from api.models import App, Organisation, OrganisationMember +from backend.graphene.types import AppType +from django.conf import settings + +CLOUD_HOSTED = settings.APP_HOST == 'cloud' + +class CreateAppMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + organisation_id = graphene.ID(required=True) + name = graphene.String(required=True) + identity_key = graphene.String(required=True) + app_token = graphene.String(required=True) + app_seed = graphene.String(required=True) + wrapped_key_share = graphene.String(required=True) + app_version = graphene.Int(required=True) + + app = graphene.Field(AppType) + + @classmethod + def mutate(cls, root, info, id, organisation_id, name, identity_key, app_token, app_seed, wrapped_key_share, app_version): + user = info.context.user + org = Organisation.objects.get(id=organisation_id) + if not user_is_org_member(user.userId, organisation_id): + raise GraphQLError("You don't have access to this organisation") + + if allow_new_app(org) == False: + raise GraphQLError( + 'You have reached the App limit for your current plan. Please upgrade your account to add more.') + + if App.objects.filter(identity_key=identity_key).exists(): + raise GraphQLError("This app already exists") + + app = App.objects.create(id=id, organisation=org, name=name, identity_key=identity_key, + app_token=app_token, app_seed=app_seed, wrapped_key_share=wrapped_key_share, app_version=app_version) + + return CreateAppMutation(app=app) + + +class RotateAppKeysMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + app_token = graphene.String(required=True) + wrapped_key_share = graphene.String(required=True) + + app = graphene.Field(AppType) + + @classmethod + def mutate(cls, root, info, id, app_token, wrapped_key_share): + user = info.context.user + app = App.objects.get(id=id) + + if not user_can_access_app(user.userId, app.id): + raise GraphQLError("You don't have access to this app") + + if CLOUD_HOSTED: + # delete current keys from cloudflare KV + deleted = delete(app.app_token) + + # purge keys from cloudflare cache + purged = purge( + f"phApp:v{app.app_version}:{app.identity_key}/{app.app_token}") + + if not deleted or not purged: + raise GraphQLError("Failed to delete app keys. Please try again.") + + app.app_token = app_token + app.wrapped_key_share = wrapped_key_share + app.save() + + return RotateAppKeysMutation(app=app) + + +class DeleteAppMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + + app = graphene.Field(AppType) + + @classmethod + def mutate(cls, root, info, id): + user = info.context.user + app = App.objects.get(id=id) + + if not user_can_access_app(user.userId, app.id): + raise GraphQLError("You don't have access to this app") + + if CLOUD_HOSTED: + # delete current keys from cloudflare KV + deleted = delete(app.app_token) + + # purge keys from cloudflare cache + purged = purge( + f"phApp:v{app.app_version}:{app.identity_key}/{app.app_token}") + + if not deleted or not purged: + raise GraphQLError("Failed to delete app keys. Please try again.") + + app.wrapped_key_share = "" + app.is_deleted = True + app.deleted_at = timezone.now() + app.save() + + return DeleteAppMutation(app=app) diff --git a/backend/backend/graphene/mutations/organisation.py b/backend/backend/graphene/mutations/organisation.py new file mode 100644 index 000000000..d2c1465f1 --- /dev/null +++ b/backend/backend/graphene/mutations/organisation.py @@ -0,0 +1,73 @@ +from backend.graphene.utils.permissions import user_is_admin +import graphene +from graphql import GraphQLError +from api.models import Organisation, CustomUser, OrganisationMember +from backend.graphene.types import OrganisationMemberType, OrganisationType + +class CreateOrganisationMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + name = graphene.String(required=True) + identity_key = graphene.String(required=True) + + organisation = graphene.Field(OrganisationType) + + @classmethod + def mutate(cls, root, info, id, name, identity_key): + if Organisation.objects.filter(name__iexact=name).exists(): + raise GraphQLError('This organisation name is not available.') + if OrganisationMember.objects.filter(user_id=info.context.user.userId, role=OrganisationMember.OWNER).exists(): + raise GraphQLError( + 'Your current plan only supports one organisation.') + + owner = CustomUser.objects.get(userId=info.context.user.userId) + org = Organisation.objects.create( + id=id, name=name, identity_key=identity_key) + OrganisationMember.objects.create(user=owner, organisation=org, role=OrganisationMember.OWNER, identity_key=identity_key) + + return CreateOrganisationMutation(organisation=org) + +class CreateOrganisationMemberMutation(graphene.Mutation): + class Arguments: + org_id = graphene.ID(required=True) + user_id = graphene.ID(required=True) + role = graphene.String(required=True) + identity_key = graphene.String(required=False) + wrapped_keyring = graphene.String(required=False) + + org_member = graphene.Field(OrganisationMemberType) + + @classmethod + def mutate(cls, root, info, org_id, user_id, role, identity_key, wrapped_keyring): + if user_is_admin(info.context.user, org_id): + org = Organisation.objects.get(id=org_id) + + org_member = OrganisationMember.objects.create(user_id=user_id, organisation=org, role=role, identity_key=identity_key, wrapped_keyring=wrapped_keyring) + + return CreateOrganisationMemberMutation(org_member=org_member) + else: + raise GraphQLError("You don't have permission to perform this action") + +class UpdateOrganisationMemberRole(graphene.Mutation): + class Arguments: + org_id = graphene.ID(required=True) + user_id = graphene.ID(required=True) + role = graphene.String(required=True) + + org_member = graphene.Field(OrganisationMemberType) + + @classmethod + def mutate(cls, root, info, org_id, user_id, role): + if user_is_admin(info.context.user, org_id): + if role == OrganisationMember.OWNER: + raise GraphQLError('You cannot set this user as the organisation owner') + org_member = OrganisationMember.objects.get(organisation__id=org_id, user__id=user_id) + org_member.role = role + org_member.save() + + return UpdateOrganisationMemberRole(org_member=org_member) + else: + raise GraphQLError("You don't have permission to perform this action") + + + \ No newline at end of file diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py new file mode 100644 index 000000000..d37f9abb1 --- /dev/null +++ b/backend/backend/graphene/types.py @@ -0,0 +1,102 @@ +import graphene +from enum import Enum +from graphene import ObjectType, relay +from graphene_django import DjangoObjectType +from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentSecret, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag +from logs.dynamodb_models import KMSLog + +class OrganisationType(DjangoObjectType): + class Meta: + model = Organisation + fields = ('id', 'name', 'identity_key', 'created_at', 'plan') + +class OrganisationMemberType(DjangoObjectType): + class Meta: + model = OrganisationMember + fields = ('user', 'role', 'identity_key', 'wrapped_keyring', 'created_at', 'updated_at') + +class AppType(DjangoObjectType): + class Meta: + model = App + fields = ('id', 'name', 'identity_key', + 'wrapped_key_share', 'created_at', 'app_token', 'app_seed', 'app_version') + +class EnvironmentType(DjangoObjectType): + class Meta: + model = Environment + fields = ('id', 'name', 'env_type', 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') + +class EnvironmentKeyType(DjangoObjectType): + class Meta: + model = EnvironmentKey + fields = ('id','identity_key', 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') + +class EnvironmentSecretType(DjangoObjectType): + class Meta: + model = EnvironmentSecret + fields = ('id', 'name', 'identity_key', 'token', 'wrapped_key_share', 'created_at', 'updated_at') + +class SecretFolderType(DjangoObjectType): + class Meta: + model = SecretFolder + fields = ('id', 'environment_id', 'parent_folder_id', 'name', 'created_at', 'updated_at') + +class SecretTagType(DjangoObjectType): + class Meta: + model = SecretTag + fields = ('id', 'name') + +class SecretEventType(DjangoObjectType): + class Meta: + model = SecretEvent + fields = ('id', 'secret', 'collection', 'key', 'value', 'version', 'tags', 'comment', 'event_type', 'timestamp') + +class SecretType(ObjectType): + + history = graphene.List(SecretEventType) + + class Meta: + model = Secret + fields = ('id', 'key', 'value', 'folder', 'version', 'tags', 'comment', 'created_at', 'updated_at', 'history') + interfaces = (relay.Node, ) + + def resolve_history(self, info): + return SecretEvent.objects.filter(secret_id=self.id).order_by('version') + + +class KMSLogType(ObjectType): + class Meta: + model = KMSLog + fields = ('id', 'app_id', 'timestamp', 'phase_node', + 'event_type', 'ip_address', 'ph_size', 'edge_location', 'country', 'city', 'latitude', 'longitude') + interfaces = (relay.Node, ) + + id = graphene.ID(required=True) + timestamp = graphene.BigInt() + app_id = graphene.String() + phase_node = graphene.String() + event_type = graphene.String() + ip_address = graphene.String() + ph_size = graphene.Int() + asn = graphene.Int() + isp = graphene.String() + edge_location = graphene.String() + country = graphene.String() + city = graphene.String() + latitude = graphene.Float() + longitude = graphene.Float() + + +class ChartDataPointType(graphene.ObjectType): + index = graphene.Int() + date = graphene.BigInt() + data = graphene.Int() + + +class TimeRange(Enum): + HOUR = 'hour' + DAY = 'day' + WEEK = 'week' + MONTH = 'month' + YEAR = 'year' + ALL_TIME = 'allTime' diff --git a/backend/backend/schema.py b/backend/backend/schema.py index cc17a572d..51614f80e 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,13 +1,10 @@ -from enum import Enum +from .graphene.utils.permissions import user_can_access_app, user_can_access_environment +from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation +from .graphene.mutations.organisation import CreateOrganisationMutation +from .graphene.types import AppType, ChartDataPointType, KMSLogType, OrganisationType, TimeRange import graphene -from django.utils import timezone -from graphene import ObjectType, relay -from graphene_django import DjangoObjectType from graphql import GraphQLError -from api.models import CustomUser, Organisation, App -from backend.api.kv import delete, purge -from ee.feature_flags import allow_new_app -from logs.dynamodb_models import KMSLog +from api.models import Environment, EnvironmentKey, Organisation, App, OrganisationMember, Secret, SecretEvent from logs.queries import get_app_log_count, get_app_log_count_range, get_app_logs from datetime import datetime, timedelta from django.conf import settings @@ -15,182 +12,6 @@ CLOUD_HOSTED = settings.APP_HOST == 'cloud' -class OrganisationType(DjangoObjectType): - class Meta: - model = Organisation - fields = ('id', 'name', 'identity_key', 'created_at', 'plan') - - -class AppType(DjangoObjectType): - class Meta: - model = App - fields = ('id', 'name', 'identity_key', - 'wrapped_key_share', 'created_at', 'app_token', 'app_seed', 'app_version') - - -class KMSLogType(ObjectType): - class Meta: - model = KMSLog - fields = ('id', 'app_id', 'timestamp', 'phase_node', - 'event_type', 'ip_address', 'ph_size', 'edge_location', 'country', 'city', 'latitude', 'longitude') - interfaces = (relay.Node, ) - - id = graphene.ID(required=True) - timestamp = graphene.BigInt() - app_id = graphene.String() - phase_node = graphene.String() - event_type = graphene.String() - ip_address = graphene.String() - ph_size = graphene.Int() - asn = graphene.Int() - isp = graphene.String() - edge_location = graphene.String() - country = graphene.String() - city = graphene.String() - latitude = graphene.Float() - longitude = graphene.Float() - - -class ChartDataPointType(graphene.ObjectType): - index = graphene.Int() - date = graphene.BigInt() - data = graphene.Int() - - -class TimeRange(Enum): - HOUR = 'hour' - DAY = 'day' - WEEK = 'week' - MONTH = 'month' - YEAR = 'year' - ALL_TIME = 'allTime' - - -class CreateOrganisationMutation(graphene.Mutation): - class Arguments: - id = graphene.ID(required=True) - name = graphene.String(required=True) - identity_key = graphene.String(required=True) - - organisation = graphene.Field(OrganisationType) - - @classmethod - def mutate(cls, root, info, id, name, identity_key): - if Organisation.objects.filter(name__iexact=name).exists(): - raise GraphQLError('This organisation name is not available.') - if Organisation.objects.filter(owner__userId=info.context.user.userId).exists(): - raise GraphQLError( - 'Your current plan only supports one organisation.') - - owner = CustomUser.objects.get(userId=info.context.user.userId) - org = Organisation.objects.create( - id=id, name=name, identity_key=identity_key, owner=owner) - - return CreateOrganisationMutation(organisation=org) - - -class CreateAppMutation(graphene.Mutation): - class Arguments: - id = graphene.ID(required=True) - organisation_id = graphene.ID(required=True) - name = graphene.String(required=True) - identity_key = graphene.String(required=True) - app_token = graphene.String(required=True) - app_seed = graphene.String(required=True) - wrapped_key_share = graphene.String(required=True) - app_version = graphene.Int(required=True) - - app = graphene.Field(AppType) - - @classmethod - def mutate(cls, root, info, id, organisation_id, name, identity_key, app_token, app_seed, wrapped_key_share, app_version): - owner = info.context.user - org = Organisation.objects.get(id=organisation_id) - if not Organisation.objects.filter(id=organisation_id, owner__userId=owner.userId).exists(): - raise GraphQLError("You don't have access to this organisation") - - if allow_new_app(org) == False: - raise GraphQLError( - 'You have reached the App limit for your current plan. Please upgrade your account to add more.') - - if App.objects.filter(identity_key=identity_key).exists(): - raise GraphQLError("This app already exists") - - app = App.objects.create(id=id, organisation=org, name=name, identity_key=identity_key, - app_token=app_token, app_seed=app_seed, wrapped_key_share=wrapped_key_share, app_version=app_version) - - return CreateAppMutation(app=app) - - -class RotateAppKeysMutation(graphene.Mutation): - class Arguments: - id = graphene.ID(required=True) - app_token = graphene.String(required=True) - wrapped_key_share = graphene.String(required=True) - - app = graphene.Field(AppType) - - @classmethod - def mutate(cls, root, info, id, app_token, wrapped_key_share): - owner = info.context.user - org = Organisation.objects.filter( - owner__userId=owner.userId).first() - app = App.objects.get(id=id) - if not app.organisation.id == org.id: - raise GraphQLError("You don't have access to this app") - - if CLOUD_HOSTED: - # delete current keys from cloudflare KV - deleted = delete(app.app_token) - - # purge keys from cloudflare cache - purged = purge( - f"phApp:v{app.app_version}:{app.identity_key}/{app.app_token}") - - if not deleted or not purged: - raise GraphQLError("Failed to delete app keys. Please try again.") - - app.app_token = app_token - app.wrapped_key_share = wrapped_key_share - app.save() - - return RotateAppKeysMutation(app=app) - - -class DeleteAppMutation(graphene.Mutation): - class Arguments: - id = graphene.ID(required=True) - - app = graphene.Field(AppType) - - @classmethod - def mutate(cls, root, info, id): - owner = info.context.user - org = Organisation.objects.filter( - owner__userId=owner.userId).first() - app = App.objects.get(id=id) - if not app.organisation.id == org.id: - raise GraphQLError("You don't have access to this app") - - if CLOUD_HOSTED: - # delete current keys from cloudflare KV - deleted = delete(app.app_token) - - # purge keys from cloudflare cache - purged = purge( - f"phApp:v{app.app_version}:{app.identity_key}/{app.app_token}") - - if not deleted or not purged: - raise GraphQLError("Failed to delete app keys. Please try again.") - - app.wrapped_key_share = "" - app.is_deleted = True - app.deleted_at = timezone.now() - app.save() - - return DeleteAppMutation(app=app) - - class Query(graphene.ObjectType): organisations = graphene.List(OrganisationType) apps = graphene.List( @@ -205,7 +26,8 @@ class Query(graphene.ObjectType): ), period=graphene.Argument(graphene.Enum.from_enum(TimeRange))) def resolve_organisations(root, info): - return Organisation.objects.filter(owner__userId=info.context.user.userId) + memberships = OrganisationMember.objects.filter(user=info.context.user) + return [membership.organisation for membership in memberships] def resolve_apps(root, info, organisation_id, app_id): filter = { @@ -215,28 +37,49 @@ def resolve_apps(root, info, organisation_id, app_id): if app_id != '': filter['id'] = app_id return App.objects.filter(**filter) + + def resolve_app_environments(root, info, app_id): + if not user_can_access_app(info.context.user.userId, app_id): + raise GraphQLError("You don't have access to this app") + + app_environments = Environment.objects.filter(app_id=app_id) + return [app_env for app_env in app_environments if EnvironmentKey.objects.filter(user_id=info.context.user.userId, env_id=app_env.id).exists()] + + def resolve_environment_secrets(root, info, env_id): + if not user_can_access_environment(info.context.user.userId, env_id): + raise GraphQLError("You don't have access to this environment") + + return Secret.objects.filter(environment_id=env_id) + + def resolve_secret_history(root, info, secret_id): + secret = Secret.objects.get(id=secret_id) + if not user_can_access_environment(info.context.user.userId, secret.environment.id): + raise GraphQLError("You don't have access to this secret") + return SecretEvent.objects.filter(secret_id=secret_id) + def resolve_logs(root, info, app_id, start=0, end=0): - owner = info.context.user - org = Organisation.objects.filter( - owner__userId=owner.userId).first() - app = App.objects.get(id=app_id) - if not app.organisation.id == org.id: + if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") + + app = App.objects.get(id=app_id) + if end == 0: end = datetime.now().timestamp() * 1000 + if CLOUD_HOSTED: return get_app_logs(f"phApp:v{app.app_version}:{app.identity_key}", start, end, 25) + logs = KMSDBLog.objects.filter(app_id=f"phApp:v{app.app_version}:{app.identity_key}",timestamp__lte=end, timestamp__gte=start).order_by('-timestamp')[:25] + return list(logs.values()) def resolve_logs_count(root, info, app_id): - owner = info.context.user - org = Organisation.objects.filter( - owner__userId=owner.userId).first() - app = App.objects.get(id=app_id) - if not app.organisation.id == org.id: + if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") + + app = App.objects.get(id=app_id) + if CLOUD_HOSTED: return get_app_log_count(f"phApp:v{app.app_version}:{app.identity_key}") return KMSDBLog.objects.filter(app_id=f"phApp:v{app.app_version}:{app.identity_key}").count() @@ -255,11 +98,9 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): Returns: List[ChartDataPointType]: Time series decrypt count data """ - owner = info.context.user - org = Organisation.objects.filter( - owner__userId=owner.userId).first() + app = App.objects.get(id=app_id) - if not app.organisation.id == org.id: + if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") end_date = datetime.now() # current time @@ -327,7 +168,6 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): return time_series_logs - class Mutation(graphene.ObjectType): create_organisation = CreateOrganisationMutation.Field() create_app = CreateAppMutation.Field() From 9ca2d58848a22a6ca8c1d7745f95bd5bdb99e56f Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 14:48:38 +0530 Subject: [PATCH 004/161] feat: mutations for envs and secrets --- .../backend/graphene/mutations/environment.py | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 backend/backend/graphene/mutations/environment.py diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py new file mode 100644 index 000000000..1f9a0f3e1 --- /dev/null +++ b/backend/backend/graphene/mutations/environment.py @@ -0,0 +1,238 @@ +from django.utils import timezone +from backend.graphene.mutations.organisation import user_is_org_member +from backend.graphene.utils.permissions import user_can_access_app, user_can_access_environment +import graphene +from graphql import GraphQLError +from api.models import App, Environment, EnvironmentKey, EnvironmentSecret, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag +from backend.graphene.types import EnvironmentKeyType, EnvironmentSecretType, EnvironmentType, SecretFolderType, SecretTagType, SecretType + + +class CreateEnvironmentMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + app_id = graphene.ID(required=True) + name = graphene.String(required=True) + env_type = graphene.String(required=True) + wrapped_seed = graphene.String(required=True) + wrapped_salt = graphene.String(required=True) + identity_key = graphene.String(required=True) + + env = graphene.Field(EnvironmentType) + + @classmethod + def mutate(cls, root, info, id, app_id, name, env_type, wrapped_seed, wrapped_salt, identity_key): + user_id = info.context.user.userId + + if not user_can_access_app(user_id, app_id): + raise GraphQLError("You don't have access to this app") + + app = App.objects.get(id=app_id) + + env = Environment.objects.create(id=id, app=app, name=name, env_type=env_type, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + + org_member = OrganisationMember.objects.get(user_id=user_id, organisation=env.app.organisation) + + EnvironmentKey.objects.create(id=id, environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + + return CreateEnvironmentMutation(env=env) + +class CreateEnvironmentKeyMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + env_id = graphene.ID(required=True) + user_id =graphene.ID(required=True) + identity_key = graphene.String(required=True) + wrapped_seed = graphene.String(required=True) + wrapped_salt = graphene.String(required=True) + + env_key = graphene.Field(EnvironmentKeyType) + + @classmethod + def mutate(cls, root, info, id, env_id, user_id, identity_key, wrapped_seed, wrapped_salt): + + env = Environment.objects.get(id=env_id) + + if not user_can_access_app(info.context.user.userId, env.app.id): + raise GraphQLError("You don't have access to this app") + + if not user_can_access_app(user_id, env.app.id): + raise GraphQLError("This user doesn't have access to this app") + + org_member = OrganisationMember.objects.get(user_id=user_id, organisation=env.app.organisation) + + if EnvironmentKey.objects.filter(environment=env, user_id=org_member).exists() + + env_key = EnvironmentKey.objects.create(id=id, environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + + return CreateEnvironmentKeyMutation(env_key=env_key) + +class CreateEnvironmentSecretMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + env_id = graphene.ID(required=True) + name = graphene.String(required=True) + identity_key = graphene.String(required=True) + token = graphene.String(required=True) + wrapped_key_share = graphene.String(required=True) + + env_secret = graphene.Field(EnvironmentSecretType) + + @classmethod + def mutate(cls, root, info, id, env_id, name, identity_key, token, wrapped_key_share): + user = info.context.user + if user_can_access_environment(user.id, env_id): + + env_secret = EnvironmentSecret.objects.create(id=id, environment_id=env_id, user=user, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + + return CreateEnvironmentSecretMutation(env_secret=env_secret) + +class CreateSecretFolderMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + env_id = graphene.ID(required=True) + parent_folder_id = graphene.ID(required=False) + name = graphene.String(required=True) + + folder = graphene.Field(SecretFolderType) + + @classmethod + def mutate(cls, root, info, id, env_id, name, parent_folder_id=None): + user = info.context.user + if user_can_access_environment(user.id, env_id): + folder = SecretFolder.objects.create(id=id, environment_id=env_id, parent_id=parent_folder_id, name=name) + + return CreateSecretFolderMutation(folder=folder) + +class CreateSecretTagMutation(graphene.Mutataion): + class Arguments: + id = graphene.ID(required=True) + org_id = graphene.ID(required=True) + name = graphene.String(required=True) + + tag = graphene.Field(SecretTagType) + + @classmethod + def mutate(cls, root, info, id, org_id, name): + + if not user_is_org_member(info.context.user.userId, org_id): + raise GraphQLError("You don't have permission to perform this action") + + org = Organisation.objects.get(id=org_id) + + if SecretTag.objects.filter(organisation=org, name=name).exists(): + raise GraphQLError('This tag already exists!') + + tag = SecretTag.objects.create(id=id, organisation=org, name=name) + + return CreateSecretTagMutation(tag=tag) + +class CreateSecretMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + env_id = graphene.ID(required=True) + folder_id = graphene.ID(required=False) + key = graphene.String(required=True) + key_digest = graphene.String(required=True) + value = graphene.String(required=True) + tags = graphene.List(graphene.String(required=True)) + comment = graphene.String() + + secret = graphene.Field(SecretType) + + @classmethod + def mutate(cls, root, info, id, env_id, folder_id, key, key_digest, value, tags, comment): + env = Environment.objects.get(id=env_id) + org = env.app.organisation + if not user_is_org_member(info.context.user.userId, org.id): + raise GraphQLError("You don't have permission to perform this action") + + tag_names = SecretTag.objects.filter(id__in=tags).values('name') + + secret_data = { + 'environment_id': env.id, + 'folder_id': folder_id, + 'key': key, + 'key_digest': key_digest, + 'value': value, + 'version': 1, + 'tags': tag_names, + 'comment': comment + } + + secret = Secret.objects.create(**{**secret_data, **{'id': id}}) + + org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) + + SecretEvent.objects.create(**{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + + return CreateSecretMutation(secret=secret) + +class EditSecretMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + folder_id = graphene.ID(required=False) + key = graphene.String(required=True) + key_digest = graphene.String(required=True) + value = graphene.String(required=True) + tags = graphene.List(graphene.String(required=True)) + comment = graphene.String() + + secret = graphene.Field(SecretType) + + @classmethod + def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment): + secret = Secret.objects.get(id=id) + env = secret.environment + org = env.app.organisation + if not user_is_org_member(info.context.user.userId, org.id): + raise GraphQLError("You don't have permission to perform this action") + + tag_names = SecretTag.objects.filter(id__in=tags).values('name') + + secret_data = { + 'environment_id': env.id, + 'folder_id': folder_id, + 'key': key, + 'key_digest': key_digest, + 'value': value, + 'version': secret.version + 1, + 'tags': tag_names, + 'comment': comment + } + + for key, value in secret_data.items(): + setattr(secret, key, value) + + secret.updated_at = timezone.now() + + org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) + + SecretEvent.objects.create(**{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) + + return EditSecretMutation(secret=secret) + +class DeleteSecretMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + + secret = graphene.Field(SecretType) + + @classmethod + def mutate(cls, root, info, id): + secret = Secret.objects.get(id=id) + env = secret.environment + org = env.app.organisation + if not user_is_org_member(info.context.user.userId, org.id): + raise GraphQLError("You don't have permission to perform this action") + + secret.deleted_at = timezone.now() + + most_recent_event = SecretEvent.objects.filter(secret=secret).order_by('version').last() + + # settings the pk to None and then saving it creates a copy of the instance with updated fields + most_recent_event.id = None + most_recent_event.event_type=SecretEvent.DELETE + most_recent_event.save() + + return DeleteSecretMutation(secret=secret) + From 84a4427875e89f603b3ab9e3f54ceb001d5339bb Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 14:49:29 +0530 Subject: [PATCH 005/161] chore: update vscode settings --- frontend/.vscode/settings.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 frontend/.vscode/settings.json diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 000000000..d0679104b --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true +} \ No newline at end of file From e9adc3e13b5a167d5a52ddd744b13a0784d4a37f Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 16:46:32 +0530 Subject: [PATCH 006/161] fix: misc fixes to schema --- .../backend/graphene/mutations/environment.py | 14 ++++----- backend/backend/schema.py | 29 ++++++++++++++++--- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 1f9a0f3e1..1e023615c 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -1,6 +1,5 @@ from django.utils import timezone -from backend.graphene.mutations.organisation import user_is_org_member -from backend.graphene.utils.permissions import user_can_access_app, user_can_access_environment +from backend.graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member import graphene from graphql import GraphQLError from api.models import App, Environment, EnvironmentKey, EnvironmentSecret, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag @@ -32,7 +31,7 @@ def mutate(cls, root, info, id, app_id, name, env_type, wrapped_seed, wrapped_sa org_member = OrganisationMember.objects.get(user_id=user_id, organisation=env.app.organisation) - EnvironmentKey.objects.create(id=id, environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + EnvironmentKey.objects.create(id=id, environment=env, user=org_member, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) return CreateEnvironmentMutation(env=env) @@ -60,7 +59,8 @@ def mutate(cls, root, info, id, env_id, user_id, identity_key, wrapped_seed, wra org_member = OrganisationMember.objects.get(user_id=user_id, organisation=env.app.organisation) - if EnvironmentKey.objects.filter(environment=env, user_id=org_member).exists() + if EnvironmentKey.objects.filter(environment=env, user_id=org_member).exists(): + raise GraphQLError("This user already has access to this environment") env_key = EnvironmentKey.objects.create(id=id, environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) @@ -103,7 +103,7 @@ def mutate(cls, root, info, id, env_id, name, parent_folder_id=None): return CreateSecretFolderMutation(folder=folder) -class CreateSecretTagMutation(graphene.Mutataion): +class CreateSecretTagMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) org_id = graphene.ID(required=True) @@ -134,7 +134,7 @@ class Arguments: key = graphene.String(required=True) key_digest = graphene.String(required=True) value = graphene.String(required=True) - tags = graphene.List(graphene.String(required=True)) + tags = graphene.List(graphene.String) comment = graphene.String() secret = graphene.Field(SecretType) @@ -174,7 +174,7 @@ class Arguments: key = graphene.String(required=True) key_digest = graphene.String(required=True) value = graphene.String(required=True) - tags = graphene.List(graphene.String(required=True)) + tags = graphene.List(graphene.String) comment = graphene.String() secret = graphene.Field(SecretType) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 51614f80e..a87a2f0dd 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,10 +1,11 @@ -from .graphene.utils.permissions import user_can_access_app, user_can_access_environment +from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentSecretMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, DeleteSecretMutation, EditSecretMutation +from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation from .graphene.mutations.organisation import CreateOrganisationMutation -from .graphene.types import AppType, ChartDataPointType, KMSLogType, OrganisationType, TimeRange +from .graphene.types import AppType, ChartDataPointType, EnvironmentType, KMSLogType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange import graphene from graphql import GraphQLError -from api.models import Environment, EnvironmentKey, Organisation, App, OrganisationMember, Secret, SecretEvent +from api.models import Environment, EnvironmentKey, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag from logs.queries import get_app_log_count, get_app_log_count_range, get_app_logs from datetime import datetime, timedelta from django.conf import settings @@ -25,6 +26,12 @@ class Query(graphene.ObjectType): app_activity_chart = graphene.List(ChartDataPointType, app_id=graphene.ID( ), period=graphene.Argument(graphene.Enum.from_enum(TimeRange))) + app_environments = graphene.List(EnvironmentType, app_id=graphene.ID()) + secrets = graphene.List(SecretType, env_id=graphene.ID()) + secret_history = graphene.List(SecretEventType, secret_id=graphene.ID()) + secret_tags = graphene.List(SecretTagType, org_id=graphene.ID()) + + def resolve_organisations(root, info): memberships = OrganisationMember.objects.filter(user=info.context.user) return [membership.organisation for membership in memberships] @@ -45,7 +52,7 @@ def resolve_app_environments(root, info, app_id): app_environments = Environment.objects.filter(app_id=app_id) return [app_env for app_env in app_environments if EnvironmentKey.objects.filter(user_id=info.context.user.userId, env_id=app_env.id).exists()] - def resolve_environment_secrets(root, info, env_id): + def resolve_secrets(root, info, env_id): if not user_can_access_environment(info.context.user.userId, env_id): raise GraphQLError("You don't have access to this environment") @@ -56,6 +63,12 @@ def resolve_secret_history(root, info, secret_id): if not user_can_access_environment(info.context.user.userId, secret.environment.id): raise GraphQLError("You don't have access to this secret") return SecretEvent.objects.filter(secret_id=secret_id) + + def resolve_secret_tags(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") + + return SecretTag.objects.filter(org_id=org_id) def resolve_logs(root, info, app_id, start=0, end=0): @@ -173,6 +186,14 @@ class Mutation(graphene.ObjectType): create_app = CreateAppMutation.Field() rotate_app_keys = RotateAppKeysMutation.Field() delete_app = DeleteAppMutation.Field() + create_environment = CreateEnvironmentMutation.Field() + create_environment_key = CreateEnvironmentKeyMutation.Field() + create_environment_secret = CreateEnvironmentSecretMutation.Field() + create_secret_folder = CreateSecretFolderMutation.Field() + create_secret_tag = CreateSecretTagMutation.Field() + create_secret = CreateSecretMutation.Field() + edit_secret = EditSecretMutation.Field() + delete_secret = DeleteSecretMutation.Field() schema = graphene.Schema(query=Query, mutation=Mutation) From 1d51d61fa62f0323233debf59a4d6e5c1996cd76 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 16:48:15 +0530 Subject: [PATCH 007/161] feat: update graphql schema and types --- frontend/apollo/gql.ts | 13 +- frontend/apollo/graphql.ts | 463 +++++++++++++++++++++++++-------- frontend/apollo/schema.graphql | 129 +++++++++ 3 files changed, 487 insertions(+), 118 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index b04b67717..d1e4001f4 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -1,8 +1,8 @@ /* eslint-disable */ -import * as types from './graphql' -import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core' +import * as types from './graphql'; +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -const documents: never[] = [] +const documents: never[] = []; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * @@ -15,11 +15,10 @@ const documents: never[] = [] * The query argument is unknown! * Please regenerate the types. */ -export function graphql(source: string): unknown +export function graphql(source: string): unknown; export function graphql(source: string) { - return (documents as any)[source] ?? {} + return (documents as any)[source] ?? {}; } -export type DocumentType> = - TDocumentNode extends DocumentNode ? TType : never +export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; \ No newline at end of file diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index ac1822861..96d5692f1 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1,28 +1,38 @@ /* eslint-disable */ -export type Maybe = T | null -export type InputMaybe = Maybe -export type Exact = { [K in keyof T]: T[K] } -export type MakeOptional = Omit & { [SubKey in K]?: Maybe } -export type MakeMaybe = Omit & { [SubKey in K]: Maybe } +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: string - String: string - Boolean: boolean - Int: number - Float: number + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; /** * 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. */ - BigInt: any + BigInt: any; /** * The `DateTime` scalar type represents a DateTime * value as specified by * [iso8601](https://en.wikipedia.org/wiki/ISO_8601). */ - DateTime: any + DateTime: any; +}; + +/** An enumeration. */ +export enum ApiEnvironmentEnvTypeChoices { + /** Development */ + Dev = 'DEV', + /** Production */ + Prod = 'PROD', + /** Staging */ + Staging = 'STAGING' } /** An enumeration. */ @@ -32,145 +42,376 @@ export enum ApiOrganisationPlanChoices { /** Free */ Fr = 'FR', /** Pro */ - Pr = 'PR', + Pr = 'PR' } -export type AppType = { - __typename?: 'AppType' - appSeed: Scalars['String'] - appToken: Scalars['String'] - appVersion: Scalars['Int'] - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - wrappedKeyShare: Scalars['String'] +/** An enumeration. */ +export enum ApiSecretEventEventTypeChoices { + /** Create */ + C = 'C', + /** Delete */ + D = 'D', + /** Read */ + R = 'R', + /** Update */ + U = 'U' } +export type AppType = { + __typename?: 'AppType'; + appSeed: Scalars['String']; + appToken: Scalars['String']; + appVersion: Scalars['Int']; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}; + export type ChartDataPointType = { - __typename?: 'ChartDataPointType' - data?: Maybe - date?: Maybe - index?: Maybe -} + __typename?: 'ChartDataPointType'; + data?: Maybe; + date?: Maybe; + index?: Maybe; +}; export type CreateAppMutation = { - __typename?: 'CreateAppMutation' - app?: Maybe -} + __typename?: 'CreateAppMutation'; + app?: Maybe; +}; + +export type CreateEnvironmentKeyMutation = { + __typename?: 'CreateEnvironmentKeyMutation'; + envKey?: Maybe; +}; + +export type CreateEnvironmentMutation = { + __typename?: 'CreateEnvironmentMutation'; + env?: Maybe; +}; + +export type CreateEnvironmentSecretMutation = { + __typename?: 'CreateEnvironmentSecretMutation'; + envSecret?: Maybe; +}; export type CreateOrganisationMutation = { - __typename?: 'CreateOrganisationMutation' - organisation?: Maybe -} + __typename?: 'CreateOrganisationMutation'; + organisation?: Maybe; +}; + +export type CreateSecretFolderMutation = { + __typename?: 'CreateSecretFolderMutation'; + folder?: Maybe; +}; + +export type CreateSecretMutation = { + __typename?: 'CreateSecretMutation'; + secret?: Maybe; +}; + +export type CreateSecretTagMutation = { + __typename?: 'CreateSecretTagMutation'; + tag?: Maybe; +}; export type DeleteAppMutation = { - __typename?: 'DeleteAppMutation' - app?: Maybe -} + __typename?: 'DeleteAppMutation'; + app?: Maybe; +}; + +export type DeleteSecretMutation = { + __typename?: 'DeleteSecretMutation'; + secret?: Maybe; +}; + +export type EditSecretMutation = { + __typename?: 'EditSecretMutation'; + secret?: Maybe; +}; + +export type EnvironmentKeyType = { + __typename?: 'EnvironmentKeyType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; + +export type EnvironmentSecretType = { + __typename?: 'EnvironmentSecretType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + token: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedKeyShare: Scalars['String']; +}; + +export type EnvironmentType = { + __typename?: 'EnvironmentType'; + createdAt?: Maybe; + envType: ApiEnvironmentEnvTypeChoices; + id: Scalars['String']; + name: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; export type KmsLogType = Node & { - __typename?: 'KMSLogType' - appId?: Maybe - asn?: Maybe - city?: Maybe - country?: Maybe - edgeLocation?: Maybe - eventType?: Maybe - id: Scalars['ID'] - ipAddress?: Maybe - isp?: Maybe - latitude?: Maybe - longitude?: Maybe - phSize?: Maybe - phaseNode?: Maybe - timestamp?: Maybe -} + __typename?: 'KMSLogType'; + appId?: Maybe; + asn?: Maybe; + city?: Maybe; + country?: Maybe; + edgeLocation?: Maybe; + eventType?: Maybe; + id: Scalars['ID']; + ipAddress?: Maybe; + isp?: Maybe; + latitude?: Maybe; + longitude?: Maybe; + phSize?: Maybe; + phaseNode?: Maybe; + timestamp?: Maybe; +}; export type Mutation = { - __typename?: 'Mutation' - createApp?: Maybe - createOrganisation?: Maybe - deleteApp?: Maybe - rotateAppKeys?: Maybe -} + __typename?: 'Mutation'; + createApp?: Maybe; + createEnvironment?: Maybe; + createEnvironmentKey?: Maybe; + createEnvironmentSecret?: Maybe; + createOrganisation?: Maybe; + createSecret?: Maybe; + createSecretFolder?: Maybe; + createSecretTag?: Maybe; + deleteApp?: Maybe; + deleteSecret?: Maybe; + editSecret?: Maybe; + rotateAppKeys?: Maybe; +}; + export type MutationCreateAppArgs = { - appSeed: Scalars['String'] - appToken: Scalars['String'] - appVersion: Scalars['Int'] - id: Scalars['ID'] - identityKey: Scalars['String'] - name: Scalars['String'] - organisationId: Scalars['ID'] - wrappedKeyShare: Scalars['String'] -} + appSeed: Scalars['String']; + appToken: Scalars['String']; + appVersion: Scalars['Int']; + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + organisationId: Scalars['ID']; + wrappedKeyShare: Scalars['String']; +}; + + +export type MutationCreateEnvironmentArgs = { + appId: Scalars['ID']; + envType: Scalars['String']; + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; + + +export type MutationCreateEnvironmentKeyArgs = { + envId: Scalars['ID']; + id: Scalars['ID']; + identityKey: Scalars['String']; + userId: Scalars['ID']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; + + +export type MutationCreateEnvironmentSecretArgs = { + envId: Scalars['ID']; + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + token: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}; + export type MutationCreateOrganisationArgs = { - id: Scalars['ID'] - identityKey: Scalars['String'] - name: Scalars['String'] -} + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; +}; + + +export type MutationCreateSecretArgs = { + comment?: InputMaybe; + envId: Scalars['ID']; + folderId?: InputMaybe; + id: Scalars['ID']; + key: Scalars['String']; + keyDigest: Scalars['String']; + tags?: InputMaybe>>; + value: Scalars['String']; +}; + + +export type MutationCreateSecretFolderArgs = { + envId: Scalars['ID']; + id: Scalars['ID']; + name: Scalars['String']; + parentFolderId?: InputMaybe; +}; + + +export type MutationCreateSecretTagArgs = { + id: Scalars['ID']; + name: Scalars['String']; + orgId: Scalars['ID']; +}; + export type MutationDeleteAppArgs = { - id: Scalars['ID'] -} + id: Scalars['ID']; +}; + + +export type MutationDeleteSecretArgs = { + id: Scalars['ID']; +}; + + +export type MutationEditSecretArgs = { + comment?: InputMaybe; + folderId?: InputMaybe; + id: Scalars['ID']; + key: Scalars['String']; + keyDigest: Scalars['String']; + tags?: InputMaybe>>; + value: Scalars['String']; +}; + export type MutationRotateAppKeysArgs = { - appToken: Scalars['String'] - id: Scalars['ID'] - wrappedKeyShare: Scalars['String'] -} + appToken: Scalars['String']; + id: Scalars['ID']; + wrappedKeyShare: Scalars['String']; +}; /** An object with an ID */ export type Node = { /** The ID of the object */ - id: Scalars['ID'] -} + id: Scalars['ID']; +}; export type OrganisationType = { - __typename?: 'OrganisationType' - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - plan: ApiOrganisationPlanChoices -} + __typename?: 'OrganisationType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + plan: ApiOrganisationPlanChoices; +}; export type Query = { - __typename?: 'Query' - appActivityChart?: Maybe>> - apps?: Maybe>> - logs?: Maybe>> - logsCount?: Maybe - organisations?: Maybe>> -} + __typename?: 'Query'; + appActivityChart?: Maybe>>; + appEnvironments?: Maybe>>; + apps?: Maybe>>; + logs?: Maybe>>; + logsCount?: Maybe; + organisations?: Maybe>>; + secretHistory?: Maybe>>; + secretTags?: Maybe>>; + secrets?: Maybe>>; +}; + export type QueryAppActivityChartArgs = { - appId?: InputMaybe - period?: InputMaybe -} + appId?: InputMaybe; + period?: InputMaybe; +}; + + +export type QueryAppEnvironmentsArgs = { + appId?: InputMaybe; +}; + export type QueryAppsArgs = { - appId?: InputMaybe - organisationId?: InputMaybe -} + appId?: InputMaybe; + organisationId?: InputMaybe; +}; + export type QueryLogsArgs = { - appId?: InputMaybe - end?: InputMaybe - start?: InputMaybe -} + appId?: InputMaybe; + end?: InputMaybe; + start?: InputMaybe; +}; + export type QueryLogsCountArgs = { - appId?: InputMaybe - thisMonth?: InputMaybe -} + appId?: InputMaybe; + thisMonth?: InputMaybe; +}; + + +export type QuerySecretHistoryArgs = { + secretId?: InputMaybe; +}; + + +export type QuerySecretTagsArgs = { + orgId?: InputMaybe; +}; + + +export type QuerySecretsArgs = { + envId?: InputMaybe; +}; export type RotateAppKeysMutation = { - __typename?: 'RotateAppKeysMutation' - app?: Maybe -} + __typename?: 'RotateAppKeysMutation'; + app?: Maybe; +}; + +export type SecretEventType = { + __typename?: 'SecretEventType'; + comment: Scalars['String']; + eventType: ApiSecretEventEventTypeChoices; + id: Scalars['String']; + key: Scalars['String']; + tags: Array; + timestamp: Scalars['DateTime']; + value: Scalars['String']; + version: Scalars['Int']; +}; + +export type SecretFolderType = { + __typename?: 'SecretFolderType'; + createdAt?: Maybe; + id: Scalars['String']; + name: Scalars['String']; + updatedAt: Scalars['DateTime']; +}; + +export type SecretTagType = { + __typename?: 'SecretTagType'; + id: Scalars['String']; + name: Scalars['String']; +}; + +export type SecretType = Node & { + __typename?: 'SecretType'; + history?: Maybe>>; + /** The ID of the object */ + id: Scalars['ID']; +}; /** An enumeration. */ export enum TimeRange { @@ -179,5 +420,5 @@ export enum TimeRange { Hour = 'HOUR', Month = 'MONTH', Week = 'WEEK', - Year = 'YEAR', + Year = 'YEAR' } diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 7aaf4a12c..f31d5553b 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -4,6 +4,10 @@ type Query { logs(appId: ID, start: BigInt, end: BigInt): [KMSLogType] logsCount(appId: ID, thisMonth: Boolean): Int appActivityChart(appId: ID, period: TimeRange): [ChartDataPointType] + appEnvironments(appId: ID): [EnvironmentType] + secrets(envId: ID): [SecretType] + secretHistory(secretId: ID): [SecretEventType] + secretTags(orgId: ID): [SecretTagType] } type OrganisationType { @@ -90,11 +94,78 @@ enum TimeRange { ALL_TIME } +type EnvironmentType { + id: String! + name: String! + envType: ApiEnvironmentEnvTypeChoices! + wrappedSeed: String! + wrappedSalt: String! + createdAt: DateTime + updatedAt: DateTime! +} + +"""An enumeration.""" +enum ApiEnvironmentEnvTypeChoices { + """Development""" + DEV + + """Staging""" + STAGING + + """Production""" + PROD +} + +type SecretType implements Node { + """The ID of the object""" + id: ID! + history: [SecretEventType] +} + +type SecretEventType { + id: String! + key: String! + value: String! + version: Int! + tags: [String!]! + comment: String! + eventType: ApiSecretEventEventTypeChoices! + timestamp: DateTime! +} + +"""An enumeration.""" +enum ApiSecretEventEventTypeChoices { + """Create""" + C + + """Read""" + R + + """Update""" + U + + """Delete""" + D +} + +type SecretTagType { + id: String! + name: String! +} + type Mutation { createOrganisation(id: ID!, identityKey: String!, name: String!): CreateOrganisationMutation createApp(appSeed: String!, appToken: String!, appVersion: Int!, id: ID!, identityKey: String!, name: String!, organisationId: ID!, wrappedKeyShare: String!): CreateAppMutation rotateAppKeys(appToken: String!, id: ID!, wrappedKeyShare: String!): RotateAppKeysMutation deleteApp(id: ID!): DeleteAppMutation + createEnvironment(appId: ID!, envType: String!, id: ID!, identityKey: String!, name: String!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentMutation + createEnvironmentKey(envId: ID!, id: ID!, identityKey: String!, userId: ID!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation + createEnvironmentSecret(envId: ID!, id: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentSecretMutation + createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation + createSecretTag(id: ID!, name: String!, orgId: ID!): CreateSecretTagMutation + createSecret(comment: String, envId: ID!, folderId: ID, id: ID!, key: String!, keyDigest: String!, tags: [String], value: String!): CreateSecretMutation + editSecret(comment: String, folderId: ID, id: ID!, key: String!, keyDigest: String!, tags: [String], value: String!): EditSecretMutation + deleteSecret(id: ID!): DeleteSecretMutation } type CreateOrganisationMutation { @@ -111,4 +182,62 @@ type RotateAppKeysMutation { type DeleteAppMutation { app: AppType +} + +type CreateEnvironmentMutation { + env: EnvironmentType +} + +type CreateEnvironmentKeyMutation { + envKey: EnvironmentKeyType +} + +type EnvironmentKeyType { + id: String! + identityKey: String! + wrappedSeed: String! + wrappedSalt: String! + createdAt: DateTime + updatedAt: DateTime! +} + +type CreateEnvironmentSecretMutation { + envSecret: EnvironmentSecretType +} + +type EnvironmentSecretType { + id: String! + name: String! + identityKey: String! + token: String! + wrappedKeyShare: String! + createdAt: DateTime + updatedAt: DateTime! +} + +type CreateSecretFolderMutation { + folder: SecretFolderType +} + +type SecretFolderType { + id: String! + name: String! + createdAt: DateTime + updatedAt: DateTime! +} + +type CreateSecretTagMutation { + tag: SecretTagType +} + +type CreateSecretMutation { + secret: SecretType +} + +type EditSecretMutation { + secret: SecretType +} + +type DeleteSecretMutation { + secret: SecretType } \ No newline at end of file From 569983a8daaeaa045e484d2104c9850f52a7ad8e Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 7 Aug 2023 16:48:42 +0530 Subject: [PATCH 008/161] chore: update graphql schema export command --- backend/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/README.md b/backend/README.md index 24bfad395..99d9b9bec 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,9 +1,9 @@ # Phase Console - Backend -Python Django REST api + Postgres +Django + Graphene + DRF ### Generate graphql schema for frontend ```bash -./manage.py graphql_schema --schema backend.schema.schema --out ../dashboard/apollo/schema.graphql +./manage.py graphql_schema --schema backend.schema.schema --out ../frontend/apollo/schema.graphql ``` From aa69ae3c8e2956d4ddec78dba71eb1ff6c660b1a Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 12:59:25 +0530 Subject: [PATCH 009/161] fix: add identity key to env model --- .../0023_environment_identity_key.py | 19 +++++++++++++++++++ backend/api/models.py | 1 + 2 files changed, 20 insertions(+) create mode 100644 backend/api/migrations/0023_environment_identity_key.py diff --git a/backend/api/migrations/0023_environment_identity_key.py b/backend/api/migrations/0023_environment_identity_key.py new file mode 100644 index 000000000..9724ddf23 --- /dev/null +++ b/backend/api/migrations/0023_environment_identity_key.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.3 on 2023-08-09 13:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0022_secretevent_timestamp'), + ] + + operations = [ + migrations.AddField( + model_name='environment', + name='identity_key', + field=models.CharField(default='', max_length=256), + preserve_default=False, + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 5bbab03cb..2deb037b7 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -167,6 +167,7 @@ class Environment(models.Model): choices=ENV_TYPES, default=DEVELOPMENT, ) + identity_key = models.CharField(max_length=256) wrapped_seed = models.CharField(max_length=208) wrapped_salt = models.CharField(max_length=208) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) From 41f957361a2a33ab179016cdebd4b3fa5726a2ed Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 12:59:45 +0530 Subject: [PATCH 010/161] fix: misc fixes and updates to schema --- .../backend/graphene/mutations/environment.py | 57 +++++++++++-------- backend/backend/graphene/types.py | 9 +-- backend/backend/graphene/utils/permissions.py | 16 ++++-- backend/backend/schema.py | 57 +++++++++++++++++-- 4 files changed, 104 insertions(+), 35 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 1e023615c..2f03fed50 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -1,5 +1,5 @@ from django.utils import timezone -from backend.graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member +from backend.graphene.utils.permissions import member_can_access_org, user_can_access_app, user_can_access_environment, user_is_org_member import graphene from graphql import GraphQLError from api.models import App, Environment, EnvironmentKey, EnvironmentSecret, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag @@ -16,7 +16,7 @@ class Arguments: wrapped_salt = graphene.String(required=True) identity_key = graphene.String(required=True) - env = graphene.Field(EnvironmentType) + environment = graphene.Field(EnvironmentType) @classmethod def mutate(cls, root, info, id, app_id, name, env_type, wrapped_seed, wrapped_salt, identity_key): @@ -27,64 +27,68 @@ def mutate(cls, root, info, id, app_id, name, env_type, wrapped_seed, wrapped_sa app = App.objects.get(id=app_id) - env = Environment.objects.create(id=id, app=app, name=name, env_type=env_type, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + environment = Environment.objects.create(id=id, app=app, name=name, env_type=env_type, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) - org_member = OrganisationMember.objects.get(user_id=user_id, organisation=env.app.organisation) + org_owner = OrganisationMember.objects.get(organisation=environment.app.organisation, role=OrganisationMember.OWNER) - EnvironmentKey.objects.create(id=id, environment=env, user=org_member, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + EnvironmentKey.objects.create(id=id, environment=environment, user=org_owner, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) - return CreateEnvironmentMutation(env=env) + return CreateEnvironmentMutation(environment=environment) class CreateEnvironmentKeyMutation(graphene.Mutation): class Arguments: - id = graphene.ID(required=True) + #id = graphene.ID(required=True) env_id = graphene.ID(required=True) user_id =graphene.ID(required=True) identity_key = graphene.String(required=True) wrapped_seed = graphene.String(required=True) wrapped_salt = graphene.String(required=True) - env_key = graphene.Field(EnvironmentKeyType) + environment_key = graphene.Field(EnvironmentKeyType) @classmethod - def mutate(cls, root, info, id, env_id, user_id, identity_key, wrapped_seed, wrapped_salt): + def mutate(cls, root, info, env_id, user_id, identity_key, wrapped_seed, wrapped_salt): env = Environment.objects.get(id=env_id) + # check that the user attempting the mutation has access if not user_can_access_app(info.context.user.userId, env.app.id): raise GraphQLError("You don't have access to this app") - if not user_can_access_app(user_id, env.app.id): + # check that the user for whom we are add a key has access + if not member_can_access_org(user_id, env.app.organisation.id): raise GraphQLError("This user doesn't have access to this app") - org_member = OrganisationMember.objects.get(user_id=user_id, organisation=env.app.organisation) + org_member = OrganisationMember.objects.get(id=user_id) if EnvironmentKey.objects.filter(environment=env, user_id=org_member).exists(): raise GraphQLError("This user already has access to this environment") - env_key = EnvironmentKey.objects.create(id=id, environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + environment_key = EnvironmentKey.objects.create(environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) - return CreateEnvironmentKeyMutation(env_key=env_key) + return CreateEnvironmentKeyMutation(environment_key=environment_key) class CreateEnvironmentSecretMutation(graphene.Mutation): class Arguments: - id = graphene.ID(required=True) env_id = graphene.ID(required=True) name = graphene.String(required=True) identity_key = graphene.String(required=True) token = graphene.String(required=True) wrapped_key_share = graphene.String(required=True) - env_secret = graphene.Field(EnvironmentSecretType) + environment_secret = graphene.Field(EnvironmentSecretType) @classmethod - def mutate(cls, root, info, id, env_id, name, identity_key, token, wrapped_key_share): + def mutate(cls, root, info, env_id, name, identity_key, token, wrapped_key_share): user = info.context.user - if user_can_access_environment(user.id, env_id): + if user_can_access_environment(user.userId, env_id): - env_secret = EnvironmentSecret.objects.create(id=id, environment_id=env_id, user=user, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + env = Environment.objects.get(id=env_id) + org_member = OrganisationMember.objects.get(organisation=env.app.organisation, user_id=user.userId) - return CreateEnvironmentSecretMutation(env_secret=env_secret) + environment_secret = EnvironmentSecret.objects.create(environment_id=env_id, user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + + return CreateEnvironmentSecretMutation(environment_secret=environment_secret) class CreateSecretFolderMutation(graphene.Mutation): class Arguments: @@ -128,7 +132,6 @@ def mutate(cls, root, info, id, org_id, name): class CreateSecretMutation(graphene.Mutation): class Arguments: - id = graphene.ID(required=True) env_id = graphene.ID(required=True) folder_id = graphene.ID(required=False) key = graphene.String(required=True) @@ -140,13 +143,15 @@ class Arguments: secret = graphene.Field(SecretType) @classmethod - def mutate(cls, root, info, id, env_id, folder_id, key, key_digest, value, tags, comment): + def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags=[], comment=''): env = Environment.objects.get(id=env_id) org = env.app.organisation if not user_is_org_member(info.context.user.userId, org.id): raise GraphQLError("You don't have permission to perform this action") tag_names = SecretTag.objects.filter(id__in=tags).values('name') + + print("TAGS", tag_names) secret_data = { 'environment_id': env.id, @@ -155,16 +160,22 @@ def mutate(cls, root, info, id, env_id, folder_id, key, key_digest, value, tags, 'key_digest': key_digest, 'value': value, 'version': 1, - 'tags': tag_names, + 'tags': [], 'comment': comment } - secret = Secret.objects.create(**{**secret_data, **{'id': id}}) + secret = Secret.objects.create(**secret_data) + + print("Created secret", secret) org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) + print("member:", org_member) + SecretEvent.objects.create(**{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + print("Created event") + return CreateSecretMutation(secret=secret) class EditSecretMutation(graphene.Mutation): diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index d37f9abb1..2411863c6 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -5,6 +5,7 @@ from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentSecret, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag from logs.dynamodb_models import KMSLog + class OrganisationType(DjangoObjectType): class Meta: model = Organisation @@ -13,7 +14,7 @@ class Meta: class OrganisationMemberType(DjangoObjectType): class Meta: model = OrganisationMember - fields = ('user', 'role', 'identity_key', 'wrapped_keyring', 'created_at', 'updated_at') + fields = ('id', 'role', 'identity_key', 'wrapped_keyring', 'created_at', 'updated_at') class AppType(DjangoObjectType): class Meta: @@ -24,7 +25,7 @@ class Meta: class EnvironmentType(DjangoObjectType): class Meta: model = Environment - fields = ('id', 'name', 'env_type', 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') + fields = ('id', 'name', 'env_type', 'identity_key', 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') class EnvironmentKeyType(DjangoObjectType): class Meta: @@ -51,14 +52,14 @@ class Meta: model = SecretEvent fields = ('id', 'secret', 'collection', 'key', 'value', 'version', 'tags', 'comment', 'event_type', 'timestamp') -class SecretType(ObjectType): +class SecretType(DjangoObjectType): history = graphene.List(SecretEventType) class Meta: model = Secret fields = ('id', 'key', 'value', 'folder', 'version', 'tags', 'comment', 'created_at', 'updated_at', 'history') - interfaces = (relay.Node, ) + #interfaces = (relay.Node, ) def resolve_history(self, info): return SecretEvent.objects.filter(secret_id=self.id).order_by('version') diff --git a/backend/backend/graphene/utils/permissions.py b/backend/backend/graphene/utils/permissions.py index 31099cccb..431f2f927 100644 --- a/backend/backend/graphene/utils/permissions.py +++ b/backend/backend/graphene/utils/permissions.py @@ -1,13 +1,15 @@ from api.models import App, Environment, EnvironmentKey, Organisation, OrganisationMember from graphql import GraphQLError +admin_roles = [OrganisationMember.OWNER, OrganisationMember.ADMIN] + def user_is_admin(user_id, org_id): - admin_roles = [OrganisationMember.OWNER, OrganisationMember.ADMIN] - member = OrganisationMember.objects.get(user__id=user_id, organisation__id=org_id) + + member = OrganisationMember.objects.get(user_id=user_id, organisation_id=org_id) return member.role in admin_roles def user_is_org_member(user_id, org_id): - return OrganisationMember.objects.filter(user__id=user_id, organisation__id=org_id).exists() + return OrganisationMember.objects.filter(user_id=user_id, organisation_id=org_id).exists() def user_can_access_app(user_id, app_id): org_memberships = OrganisationMember.objects.filter(user_id=user_id) @@ -15,4 +17,10 @@ def user_can_access_app(user_id, app_id): return app.organisation.id in [membership.organisation.id for membership in org_memberships] def user_can_access_environment(user_id, env_id): - return EnvironmentKey.objects.filter(user_id=user_id, environment_id=env_id).exists() + env = Environment.objects.get(id=env_id) + org_member = OrganisationMember.objects.get(organisation=env.app.organisation, user_id=user_id) + return EnvironmentKey.objects.filter(user_id=org_member, 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).exists() + \ No newline at end of file diff --git a/backend/backend/schema.py b/backend/backend/schema.py index a87a2f0dd..0cd218e85 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -2,19 +2,22 @@ from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation from .graphene.mutations.organisation import CreateOrganisationMutation -from .graphene.types import AppType, ChartDataPointType, EnvironmentType, KMSLogType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange +from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentSecretType, EnvironmentType, KMSLogType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange import graphene from graphql import GraphQLError -from api.models import Environment, EnvironmentKey, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag +from api.models import Environment, EnvironmentKey, EnvironmentSecret, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag from logs.queries import get_app_log_count, get_app_log_count_range, get_app_logs from datetime import datetime, timedelta from django.conf import settings from logs.models import KMSDBLog +from itertools import chain CLOUD_HOSTED = settings.APP_HOST == 'cloud' class Query(graphene.ObjectType): organisations = graphene.List(OrganisationType) + organisation_members = graphene.List(OrganisationMemberType, organisation_id=graphene.ID(), user_id=graphene.ID(), role=graphene.List(graphene.String)) + organisation_admins_and_self = graphene.List(OrganisationMemberType, organisation_id=graphene.ID()) apps = graphene.List( AppType, organisation_id=graphene.ID(), app_id=graphene.ID()) logs = graphene.List(KMSLogType, app_id=graphene.ID(), @@ -30,11 +33,36 @@ class Query(graphene.ObjectType): secrets = graphene.List(SecretType, env_id=graphene.ID()) secret_history = graphene.List(SecretEventType, secret_id=graphene.ID()) secret_tags = graphene.List(SecretTagType, org_id=graphene.ID()) + environment_keys = graphene.List(EnvironmentKeyType, environment_id=graphene.ID()) + environment_secrets = graphene.List(EnvironmentSecretType, environment_id=graphene.ID()) def resolve_organisations(root, info): memberships = OrganisationMember.objects.filter(user=info.context.user) return [membership.organisation for membership in memberships] + + def resolve_organisation_members(root, info, organisation_id, role, user_id=None): + if not user_is_org_member(info.context.user.userId, organisation_id): + raise GraphQLError("You don't have access to this organisation") + + roles = [user_role.lower() for user_role in role] + + return OrganisationMember.objects.filter(organisation_id=organisation_id, role__in=roles) + + def resolve_organisation_admins_and_self(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") + + roles = ['owner', 'admin'] + + members = OrganisationMember.objects.filter(organisation_id=organisation_id, role__in=roles) + + 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) + members = list(chain(members, self_member)) + + return members + def resolve_apps(root, info, organisation_id, app_id): filter = { @@ -49,8 +77,12 @@ def resolve_app_environments(root, info, app_id): if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") + app = App.objects.get(id=app_id) + + org_member = OrganisationMember.objects.get(organisation=app.organisation, user_id=info.context.user.userId) + app_environments = Environment.objects.filter(app_id=app_id) - return [app_env for app_env in app_environments if EnvironmentKey.objects.filter(user_id=info.context.user.userId, env_id=app_env.id).exists()] + return [app_env for app_env in app_environments if EnvironmentKey.objects.filter(user=org_member, environment_id=app_env.id).exists()] def resolve_secrets(root, info, env_id): if not user_can_access_environment(info.context.user.userId, env_id): @@ -69,8 +101,25 @@ def resolve_secret_tags(root, info, org_id): raise GraphQLError("You don't have access to this Organisation") return SecretTag.objects.filter(org_id=org_id) + + def resolve_environment_keys(root, info, environment_id): + if not user_can_access_environment(info.context.user.userId, environment_id): + raise GraphQLError("You don't have access to this secret") + + env = Environment.objects.get(id=environment_id) + org_member = OrganisationMember.objects.get(user=info.context.user, organisation=env.app.organisation) + return EnvironmentKey.objects.filter(environment=env, user=org_member) + + def resolve_environment_secrets(root, info, environment_id): + if not user_can_access_environment(info.context.user.userId, environment_id): + raise GraphQLError("You don't have access to this secret") + + env = Environment.objects.get(id=environment_id) + org_member = OrganisationMember.objects.get(user=info.context.user, organisation=env.app.organisation) + return EnvironmentSecret.objects.filter(environment=env, user=org_member) + + - 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") From b0201d999536841fc27cd84317152688773999f7 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 13:00:23 +0530 Subject: [PATCH 011/161] chore: regenerate frontend schema and types --- frontend/apollo/graphql.ts | 70 +++++++++++++++++++--- frontend/apollo/schema.graphql | 104 ++++++++++++++++++++++----------- 2 files changed, 130 insertions(+), 44 deletions(-) diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 96d5692f1..079dfe42d 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -35,6 +35,16 @@ export enum ApiEnvironmentEnvTypeChoices { Staging = 'STAGING' } +/** An enumeration. */ +export enum ApiOrganisationMemberRoleChoices { + /** Admin */ + Admin = 'ADMIN', + /** Developer */ + Dev = 'DEV', + /** Owner */ + Owner = 'OWNER' +} + /** An enumeration. */ export enum ApiOrganisationPlanChoices { /** Enterprise */ @@ -83,17 +93,17 @@ export type CreateAppMutation = { export type CreateEnvironmentKeyMutation = { __typename?: 'CreateEnvironmentKeyMutation'; - envKey?: Maybe; + environmentKey?: Maybe; }; export type CreateEnvironmentMutation = { __typename?: 'CreateEnvironmentMutation'; - env?: Maybe; + environment?: Maybe; }; export type CreateEnvironmentSecretMutation = { __typename?: 'CreateEnvironmentSecretMutation'; - envSecret?: Maybe; + environmentSecret?: Maybe; }; export type CreateOrganisationMutation = { @@ -157,6 +167,7 @@ export type EnvironmentType = { createdAt?: Maybe; envType: ApiEnvironmentEnvTypeChoices; id: Scalars['String']; + identityKey: Scalars['String']; name: Scalars['String']; updatedAt: Scalars['DateTime']; wrappedSalt: Scalars['String']; @@ -223,7 +234,6 @@ export type MutationCreateEnvironmentArgs = { export type MutationCreateEnvironmentKeyArgs = { envId: Scalars['ID']; - id: Scalars['ID']; identityKey: Scalars['String']; userId: Scalars['ID']; wrappedSalt: Scalars['String']; @@ -233,7 +243,6 @@ export type MutationCreateEnvironmentKeyArgs = { export type MutationCreateEnvironmentSecretArgs = { envId: Scalars['ID']; - id: Scalars['ID']; identityKey: Scalars['String']; name: Scalars['String']; token: Scalars['String']; @@ -252,7 +261,6 @@ export type MutationCreateSecretArgs = { comment?: InputMaybe; envId: Scalars['ID']; folderId?: InputMaybe; - id: Scalars['ID']; key: Scalars['String']; keyDigest: Scalars['String']; tags?: InputMaybe>>; @@ -308,6 +316,16 @@ export type Node = { id: Scalars['ID']; }; +export type OrganisationMemberType = { + __typename?: 'OrganisationMemberType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey?: Maybe; + role: ApiOrganisationMemberRoleChoices; + updatedAt: Scalars['DateTime']; + wrappedKeyring: Scalars['String']; +}; + export type OrganisationType = { __typename?: 'OrganisationType'; createdAt?: Maybe; @@ -322,8 +340,12 @@ export type Query = { appActivityChart?: Maybe>>; appEnvironments?: Maybe>>; apps?: Maybe>>; + environmentKeys?: Maybe>>; + environmentSecrets?: Maybe>>; logs?: Maybe>>; logsCount?: Maybe; + organisationAdminsAndSelf?: Maybe>>; + organisationMembers?: Maybe>>; organisations?: Maybe>>; secretHistory?: Maybe>>; secretTags?: Maybe>>; @@ -348,6 +370,16 @@ export type QueryAppsArgs = { }; +export type QueryEnvironmentKeysArgs = { + environmentId?: InputMaybe; +}; + + +export type QueryEnvironmentSecretsArgs = { + environmentId?: InputMaybe; +}; + + export type QueryLogsArgs = { appId?: InputMaybe; end?: InputMaybe; @@ -361,6 +393,18 @@ export type QueryLogsCountArgs = { }; +export type QueryOrganisationAdminsAndSelfArgs = { + organisationId?: InputMaybe; +}; + + +export type QueryOrganisationMembersArgs = { + organisationId?: InputMaybe; + role?: InputMaybe>>; + userId?: InputMaybe; +}; + + export type QuerySecretHistoryArgs = { secretId?: InputMaybe; }; @@ -386,6 +430,7 @@ export type SecretEventType = { eventType: ApiSecretEventEventTypeChoices; id: Scalars['String']; key: Scalars['String']; + secret: SecretType; tags: Array; timestamp: Scalars['DateTime']; value: Scalars['String']; @@ -406,11 +451,18 @@ export type SecretTagType = { name: Scalars['String']; }; -export type SecretType = Node & { +export type SecretType = { __typename?: 'SecretType'; + comment: Scalars['String']; + createdAt?: Maybe; + folder?: Maybe; history?: Maybe>>; - /** The ID of the object */ - id: Scalars['ID']; + id: Scalars['String']; + key: Scalars['String']; + tags: Array; + updatedAt: Scalars['DateTime']; + value: Scalars['String']; + version: Scalars['Int']; }; /** An enumeration. */ diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index f31d5553b..f9f722e2b 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -1,5 +1,7 @@ type Query { organisations: [OrganisationType] + organisationMembers(organisationId: ID, userId: ID, role: [String]): [OrganisationMemberType] + organisationAdminsAndSelf(organisationId: ID): [OrganisationMemberType] apps(organisationId: ID, appId: ID): [AppType] logs(appId: ID, start: BigInt, end: BigInt): [KMSLogType] logsCount(appId: ID, thisMonth: Boolean): Int @@ -8,6 +10,8 @@ type Query { secrets(envId: ID): [SecretType] secretHistory(secretId: ID): [SecretEventType] secretTags(orgId: ID): [SecretTagType] + environmentKeys(environmentId: ID): [EnvironmentKeyType] + environmentSecrets(environmentId: ID): [EnvironmentSecretType] } type OrganisationType { @@ -37,6 +41,27 @@ enum ApiOrganisationPlanChoices { EN } +type OrganisationMemberType { + id: String! + role: ApiOrganisationMemberRoleChoices! + identityKey: String + wrappedKeyring: String! + createdAt: DateTime + updatedAt: DateTime! +} + +"""An enumeration.""" +enum ApiOrganisationMemberRoleChoices { + """Owner""" + OWNER + + """Admin""" + ADMIN + + """Developer""" + DEV +} + type AppType { id: String! name: String! @@ -98,6 +123,7 @@ type EnvironmentType { id: String! name: String! envType: ApiEnvironmentEnvTypeChoices! + identityKey: String! wrappedSeed: String! wrappedSalt: String! createdAt: DateTime @@ -116,14 +142,29 @@ enum ApiEnvironmentEnvTypeChoices { PROD } -type SecretType implements Node { - """The ID of the object""" - id: ID! +type SecretType { + id: String! + folder: SecretFolderType + key: String! + value: String! + version: Int! + tags: [String!]! + comment: String! + createdAt: DateTime + updatedAt: DateTime! history: [SecretEventType] } +type SecretFolderType { + id: String! + name: String! + createdAt: DateTime + updatedAt: DateTime! +} + type SecretEventType { id: String! + secret: SecretType! key: String! value: String! version: Int! @@ -153,17 +194,36 @@ type SecretTagType { name: String! } +type EnvironmentKeyType { + id: String! + identityKey: String! + wrappedSeed: String! + wrappedSalt: String! + createdAt: DateTime + updatedAt: DateTime! +} + +type EnvironmentSecretType { + id: String! + name: String! + identityKey: String! + token: String! + wrappedKeyShare: String! + createdAt: DateTime + updatedAt: DateTime! +} + type Mutation { createOrganisation(id: ID!, identityKey: String!, name: String!): CreateOrganisationMutation createApp(appSeed: String!, appToken: String!, appVersion: Int!, id: ID!, identityKey: String!, name: String!, organisationId: ID!, wrappedKeyShare: String!): CreateAppMutation rotateAppKeys(appToken: String!, id: ID!, wrappedKeyShare: String!): RotateAppKeysMutation deleteApp(id: ID!): DeleteAppMutation createEnvironment(appId: ID!, envType: String!, id: ID!, identityKey: String!, name: String!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentMutation - createEnvironmentKey(envId: ID!, id: ID!, identityKey: String!, userId: ID!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation - createEnvironmentSecret(envId: ID!, id: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentSecretMutation + createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation + createEnvironmentSecret(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentSecretMutation createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation createSecretTag(id: ID!, name: String!, orgId: ID!): CreateSecretTagMutation - createSecret(comment: String, envId: ID!, folderId: ID, id: ID!, key: String!, keyDigest: String!, tags: [String], value: String!): CreateSecretMutation + createSecret(comment: String, envId: ID!, folderId: ID, key: String!, keyDigest: String!, tags: [String], value: String!): CreateSecretMutation editSecret(comment: String, folderId: ID, id: ID!, key: String!, keyDigest: String!, tags: [String], value: String!): EditSecretMutation deleteSecret(id: ID!): DeleteSecretMutation } @@ -185,47 +245,21 @@ type DeleteAppMutation { } type CreateEnvironmentMutation { - env: EnvironmentType + environment: EnvironmentType } type CreateEnvironmentKeyMutation { - envKey: EnvironmentKeyType -} - -type EnvironmentKeyType { - id: String! - identityKey: String! - wrappedSeed: String! - wrappedSalt: String! - createdAt: DateTime - updatedAt: DateTime! + environmentKey: EnvironmentKeyType } type CreateEnvironmentSecretMutation { - envSecret: EnvironmentSecretType -} - -type EnvironmentSecretType { - id: String! - name: String! - identityKey: String! - token: String! - wrappedKeyShare: String! - createdAt: DateTime - updatedAt: DateTime! + environmentSecret: EnvironmentSecretType } type CreateSecretFolderMutation { folder: SecretFolderType } -type SecretFolderType { - id: String! - name: String! - createdAt: DateTime - updatedAt: DateTime! -} - type CreateSecretTagMutation { tag: SecretTagType } From 750989779529fef9c5290498ba4eb0d1108a11c7 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 13:00:52 +0530 Subject: [PATCH 012/161] feat: add frontend queries and mutations --- .../environments/createEnvironment.gql | 10 +++++++++ .../environments/createEnvironmentKey.gql | 8 +++++++ .../environments/createEnvironmentSecret.gql | 8 +++++++ .../mutations/environments/createSecret.gql | 10 +++++++++ .../getOrganisationAdminsAndSelf.gql | 7 +++++++ .../organisation/getOrganisationMembers.gql | 6 ++++++ .../queries/secrets/getAppEnvironments.gql | 11 ++++++++++ .../queries/secrets/getEnvironmentKey.gql | 8 +++++++ .../queries/secrets/getEnvironmentSecrets.gql | 8 +++++++ .../apollo/queries/secrets/getSecrets.gql | 21 +++++++++++++++++++ 10 files changed, 97 insertions(+) create mode 100644 frontend/apollo/mutations/environments/createEnvironment.gql create mode 100644 frontend/apollo/mutations/environments/createEnvironmentKey.gql create mode 100644 frontend/apollo/mutations/environments/createEnvironmentSecret.gql create mode 100644 frontend/apollo/mutations/environments/createSecret.gql create mode 100644 frontend/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql create mode 100644 frontend/apollo/queries/organisation/getOrganisationMembers.gql create mode 100644 frontend/apollo/queries/secrets/getAppEnvironments.gql create mode 100644 frontend/apollo/queries/secrets/getEnvironmentKey.gql create mode 100644 frontend/apollo/queries/secrets/getEnvironmentSecrets.gql create mode 100644 frontend/apollo/queries/secrets/getSecrets.gql diff --git a/frontend/apollo/mutations/environments/createEnvironment.gql b/frontend/apollo/mutations/environments/createEnvironment.gql new file mode 100644 index 000000000..e3d701e28 --- /dev/null +++ b/frontend/apollo/mutations/environments/createEnvironment.gql @@ -0,0 +1,10 @@ +mutation CreateEnvironment($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) { + createEnvironment(id: $id, appId: $appId, name: $name, envType: $envType, identityKey:$identityKey, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt) { + environment { + id + name + createdAt + identityKey + } + } +} diff --git a/frontend/apollo/mutations/environments/createEnvironmentKey.gql b/frontend/apollo/mutations/environments/createEnvironmentKey.gql new file mode 100644 index 000000000..bcb586dc1 --- /dev/null +++ b/frontend/apollo/mutations/environments/createEnvironmentKey.gql @@ -0,0 +1,8 @@ +mutation CreateEnvironmentKey($envId: ID!, $ownerId: ID!,$wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) { + createEnvironmentKey(envId: $envId, userId: $ownerId, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt, identityKey: $identityKey) { + environmentKey { + id + createdAt + } + } +} diff --git a/frontend/apollo/mutations/environments/createEnvironmentSecret.gql b/frontend/apollo/mutations/environments/createEnvironmentSecret.gql new file mode 100644 index 000000000..01fac4a65 --- /dev/null +++ b/frontend/apollo/mutations/environments/createEnvironmentSecret.gql @@ -0,0 +1,8 @@ +mutation CreateEnvironmentSecret($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { + createEnvironmentSecret(envId: $envId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { + environmentSecret { + id + createdAt + } + } +} diff --git a/frontend/apollo/mutations/environments/createSecret.gql b/frontend/apollo/mutations/environments/createSecret.gql new file mode 100644 index 000000000..5187e6a50 --- /dev/null +++ b/frontend/apollo/mutations/environments/createSecret.gql @@ -0,0 +1,10 @@ +mutation CreateSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) { + createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) { + secret { + id + key + value + createdAt + } + } +} diff --git a/frontend/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql b/frontend/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql new file mode 100644 index 000000000..1ca807269 --- /dev/null +++ b/frontend/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql @@ -0,0 +1,7 @@ +query GetOrganisationAdminsAndSelf($organisationId: ID!) { + organisationAdminsAndSelf(organisationId: $organisationId) { + id + role + identityKey + } +} diff --git a/frontend/apollo/queries/organisation/getOrganisationMembers.gql b/frontend/apollo/queries/organisation/getOrganisationMembers.gql new file mode 100644 index 000000000..c6145d3e6 --- /dev/null +++ b/frontend/apollo/queries/organisation/getOrganisationMembers.gql @@ -0,0 +1,6 @@ +query GetOrganisationMembers($organisationId: ID!, $role: [String]) { + organisationMembers(organisationId: $organisationId, role: $role) { + role + identityKey + } +} diff --git a/frontend/apollo/queries/secrets/getAppEnvironments.gql b/frontend/apollo/queries/secrets/getAppEnvironments.gql new file mode 100644 index 000000000..d6b03dc13 --- /dev/null +++ b/frontend/apollo/queries/secrets/getAppEnvironments.gql @@ -0,0 +1,11 @@ +query GetAppEnvironments($appId: ID!) { + appEnvironments(appId: $appId) { + id + name + envType + identityKey + wrappedSeed + wrappedSalt + createdAt + } +} diff --git a/frontend/apollo/queries/secrets/getEnvironmentKey.gql b/frontend/apollo/queries/secrets/getEnvironmentKey.gql new file mode 100644 index 000000000..62251a0b2 --- /dev/null +++ b/frontend/apollo/queries/secrets/getEnvironmentKey.gql @@ -0,0 +1,8 @@ +query GetEnvironmentKey($envId: ID!) { + environmentKeys(envId: $envId) { + id + identityKey + wrappedSeed + wrappedSalt + } +} diff --git a/frontend/apollo/queries/secrets/getEnvironmentSecrets.gql b/frontend/apollo/queries/secrets/getEnvironmentSecrets.gql new file mode 100644 index 000000000..37bf50e01 --- /dev/null +++ b/frontend/apollo/queries/secrets/getEnvironmentSecrets.gql @@ -0,0 +1,8 @@ +query GetEnvironmentSecrets($envId: ID!) { + environmentSecrets(environmentId: $envId) { + id + name + wrappedKeyShare + createdAt + } +} diff --git a/frontend/apollo/queries/secrets/getSecrets.gql b/frontend/apollo/queries/secrets/getSecrets.gql new file mode 100644 index 000000000..5c1af77c3 --- /dev/null +++ b/frontend/apollo/queries/secrets/getSecrets.gql @@ -0,0 +1,21 @@ +query GetSecrets($envId: ID!) { + secrets(envId: $envId) { + id + key + value + createdAt + history { + id + key + value + timestamp + eventType + } + } + environmentKeys(environmentId: $envId) { + id + identityKey + wrappedSeed + wrappedSalt + } +} From 10d9cdaa3ed809e8faa963c1502657e6cc5cabeb Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 13:01:35 +0530 Subject: [PATCH 013/161] feat: crypto utils for asymmetric encryption --- frontend/utils/crypto.ts | 157 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 frontend/utils/crypto.ts diff --git a/frontend/utils/crypto.ts b/frontend/utils/crypto.ts new file mode 100644 index 000000000..b186c053b --- /dev/null +++ b/frontend/utils/crypto.ts @@ -0,0 +1,157 @@ +import _sodium, { KeyPair } from 'libsodium-wrappers-sumo' + +import { cryptoUtils } from '@/utils/auth' + +/** + * Returns an random key exchange keypair + * + * @returns {KeyPair} + */ +export const randomKeyPair = async () => { + await _sodium.ready; + const sodium = _sodium; + const keypair = await sodium.crypto_kx_keypair(); + + return keypair; +}; + +/** + * Carries out diffie-hellman key exchange for client and returns a pair of symmetric encryption keys + * + * @param {KeyPair} ephemeralKeyPair + * @param {Uint8Array} recipientPubKey + * @returns + */ +export const clientSessionKeys = async ( + ephemeralKeyPair: KeyPair, + recipientPubKey: Uint8Array +) => { + await _sodium.ready; + const sodium = _sodium; + + const keys = await sodium.crypto_kx_client_session_keys( + ephemeralKeyPair.publicKey, + ephemeralKeyPair.privateKey, + recipientPubKey + ); + return keys; +}; + +/** + * Carries out diffie-hellman key exchange for server and returns a pair of symmetric encryption keys + * + * @param {KeyPair} ephemeralKeyPair + * @param {Uint8Array} recipientPubKey + * @returns + */ +export const serverSessionKeys = async ( + appKeyPair: { publicKey: Uint8Array; privateKey: Uint8Array }, + dataPubKey: Uint8Array +) => { + await _sodium.ready; + const sodium = _sodium; + const keys = await sodium.crypto_kx_server_session_keys( + appKeyPair.publicKey, + appKeyPair.privateKey, + dataPubKey + ); + return keys; +}; + +export const encryptAsymmetric = async ( + plaintext: string, + publicKey: string, +): Promise => { + await _sodium.ready; + const sodium = _sodium; + + return new Promise(async (resolve, reject) => { + try { + const oneTimeKeyPair = await randomKeyPair(); + + const symmetricKeys = await clientSessionKeys( + oneTimeKeyPair, + sodium.from_hex(publicKey) + ); + + const ciphertext = await cryptoUtils.encryptString( + plaintext, + symmetricKeys.sharedTx + ); + + // Use sodium.memzero to wipe the keys from memory + sodium.memzero(oneTimeKeyPair.privateKey); + sodium.memzero(symmetricKeys.sharedTx); + sodium.memzero(symmetricKeys.sharedRx); + + resolve( + `${sodium.to_hex( + oneTimeKeyPair.publicKey + )}:${ciphertext}` + ); + } catch (error) { + reject(`Something went wrong: ${error}`); + } + }); +}; + +export const decryptAsymmetric = async (ciphertextString: string, privateKey: string, publicKey: string): Promise => { + await _sodium.ready; + const sodium = _sodium; + + return new Promise(async (resolve, reject) => { + const ciphertextSegments = ciphertextString.split(":"); + if (ciphertextSegments.length !== 2) + reject("Invalid ciphertext"); + + const ciphertext = { + pubKey: ciphertextSegments[0], + data: ciphertextSegments[1], + }; + + try { + const sessionKeys = await serverSessionKeys( + { + publicKey: sodium.from_hex(publicKey) as Uint8Array, + privateKey: sodium.from_hex(privateKey) as Uint8Array, + }, + sodium.from_hex(ciphertext.pubKey) + ); + + const plaintext = await cryptoUtils.decryptString( + ciphertext.data, + sessionKeys.sharedRx + ); + + // Use sodium.memzero to wipe the keys from memory + sodium.memzero(sessionKeys.sharedRx); + sodium.memzero(sessionKeys.sharedTx); + + resolve(plaintext); + } catch (error) { + reject(`Something went wrong: ${error}`); + } + }); +}; + +export const getUserKxPublicKey = async (signingPublicKey: string) => { + await _sodium.ready; + const sodium = _sodium; + + return sodium.to_hex(sodium.crypto_sign_ed25519_pk_to_curve25519(sodium.from_hex(signingPublicKey))) +} + +export const getUserKxPrivateKey = async (signingPrivateKey: string) => { + await _sodium.ready; + const sodium = _sodium; + + return sodium.to_hex(sodium.crypto_sign_ed25519_sk_to_curve25519(sodium.from_hex(signingPrivateKey))) +} + +export const digest = async (input: string) => { + await _sodium.ready; + const sodium = _sodium; + + const hash = await sodium.crypto_generichash(32, input) + return sodium.to_base64(hash, sodium.base64_variants.ORIGINAL) +} \ No newline at end of file From be97b5db7bb65381053a3a8c3abac9529a1a6ac9 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 13:01:51 +0530 Subject: [PATCH 014/161] feat: env key utils --- frontend/utils/environments.ts | 150 +++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 frontend/utils/environments.ts diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts new file mode 100644 index 000000000..6ab5d371a --- /dev/null +++ b/frontend/utils/environments.ts @@ -0,0 +1,150 @@ +import _sodium from 'libsodium-wrappers-sumo' +import { cryptoUtils } from '@/utils/auth' +import { splitSecret } from './keyshares' +import { EnvironmentKeyType, EnvironmentType } from '@/apollo/graphql' +import { decryptAsymmetric } from './crypto' + +type EnvKeyring = { + publicKey: string + privateKey: string +} + +/** + * Create a random seed for a new env + * + * @returns {Promise} - hex encoded env seed + */ +export const newEnvSeed = async () => { + await _sodium.ready + const sodium = _sodium + + const seed = sodium.crypto_kdf_keygen() + return sodium.to_hex(seed) +} + +/** + * Create a random salt for a new env + * + * @returns {Promise} - hex encoded env salt + */ +export const newEnvSalt = async () => { + await _sodium.ready + const sodium = _sodium + + const seed = sodium.crypto_kdf_keygen() + return sodium.to_hex(seed) +} + +/** + * Create a random token for a new env + * + * @returns {Promise} - hex encoded env token + */ +export const newEnvToken = async () => { +await _sodium.ready +const sodium = _sodium + +const token = sodium.crypto_kdf_keygen() +return sodium.to_hex(token) +} + +/** + * Create a wrapping key for new env secret share + * + * @returns {Promise} - hex encoded wrapping key + */ +export const newEnvWrapKey = async () => { + await _sodium.ready + const sodium = _sodium + + const key = sodium.crypto_kdf_keygen() + return sodium.to_hex(key) +} + +/** + * Encrypts an env seed with the given key + * + * @param seed - Env seed as a hex string + * @param key - Encryption key as a hex string + * @returns {Promise} + */ +export const encryptedEnvSeed = async (seed: string, key: string) => { + await _sodium.ready + const sodium = _sodium + + const keyBytes = sodium.from_hex(key) + const encryptedSeed = await cryptoUtils.encryptRaw(seed, keyBytes) + return sodium.to_hex(encryptedSeed) +} + +/** + * Decrypts an env seed with the given key + * + * @param encryptedSeed - Encrytped env seed as a hex string + * @param key - Decryption key as a hex string + * @returns {Promise} - hex encoded plaintext app seed + */ +export const decryptedAppSeed = async (encryptedSeed: string, key: string) => { + await _sodium.ready + const sodium = _sodium + + const ciphertextBytes = sodium.from_hex(encryptedSeed) + const keyBytes = sodium.from_hex(key) + + const seedBytes = await cryptoUtils.decryptRaw(ciphertextBytes, keyBytes) + return sodium.to_string(seedBytes) +} + +/** + * Derives an env keyring from the given seed + * + * @param {string} envSeed - Env seed as a hex string + * @returns {Promise} + */ +export const envKeyring = async (envSeed: string): Promise => { + await _sodium.ready + const sodium = _sodium + + const seedBytes = sodium.from_hex(envSeed) + const envKeypair = sodium.crypto_kx_seed_keypair(seedBytes) + + const { publicKey, privateKey } = envKeypair + + return { publicKey: sodium.to_hex(publicKey), privateKey: sodium.to_hex(privateKey) } +} + +export const generateEnvironmentSecret = async (environment: EnvironmentType, key: EnvironmentKeyType, useKeyring: { publicKey: string, privateKey: string }) => { + const wrapKey = await newEnvWrapKey() + const token = await newEnvToken() + + const envSeed = await decryptAsymmetric( + key.wrappedSeed, + useKeyring.privateKey, + useKeyring.publicKey + ) + + const envKeys = await envKeyring(envSeed) + + const keyShares = await splitSecret(envKeys.privateKey) + const wrappedKeyShare = await cryptoUtils.wrappedKeyShare(keyShares[1], wrapKey) + + const envSalt = await decryptAsymmetric( + key.wrappedSalt, + useKeyring.privateKey, + useKeyring.publicKey + ) + + const pssEnv = `pss_env:v1:${envKeys.publicKey}:${token}:${keyShares[0]}:${wrapKey}:${envSalt}` + const mutationPayload = { + envId: environment.id, + name: 'testSecret', + identityKey: environment.identityKey, + token, + wrappedKeyShare + } + + return { + pssEnv, + mutationPayload + } +} \ No newline at end of file From 32151b8847d1e7edd2cd8543cd83a43da6725c2b Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 13:02:24 +0530 Subject: [PATCH 015/161] feat: add string encrypt and decrypt utils --- frontend/utils/auth.ts | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/frontend/utils/auth.ts b/frontend/utils/auth.ts index 3a107f2c0..f6bb21e8b 100644 --- a/frontend/utils/auth.ts +++ b/frontend/utils/auth.ts @@ -82,7 +82,7 @@ export namespace cryptoUtils { * @param {Uint8Array} key * @returns {Promise} - Ciphertext with appended nonce */ - const encryptRaw = async (plaintext: string, key: Uint8Array): Promise => { + export const encryptRaw = async (plaintext: string, key: Uint8Array): Promise => { await _sodium.ready const sodium = _sodium @@ -126,6 +126,42 @@ export namespace cryptoUtils { return plaintext } + /** + * Encrypts a single string with the given key. Returns the ciphertext as a base64 string + * + * @param {string} plaintext - Plaintext string to encrypt + * @param {Uint8Array} key - Symmetric encryption key + * @returns {string} + */ +export const encryptString = async (plaintext: string, key: Uint8Array) => { + await _sodium.ready; + const sodium = _sodium; + + return sodium.to_base64( + await encryptRaw(sodium.from_string(plaintext), key), + sodium.base64_variants.ORIGINAL + ); +}; + +/** + * Decrypts a single base64 ciphertext string with the given key. Returns the plaintext as a string + * + * @param cipherText - base64 string ciphertext with appended nonce + * @param key - Symmetric encryption key + * @returns {string} + */ +export const decryptString = async (cipherText: string, key: Uint8Array) => { + await _sodium.ready; + const sodium = _sodium; + + return sodium.to_string( + await decryptRaw( + sodium.from_base64(cipherText, sodium.base64_variants.ORIGINAL), + key + ) + ); +}; + /** * Computes the account recovery key from the mnemonic phrase and orgId. * Note: This can take between 15-20 seconds to resolve From a4626144f67bb6e46a7e65b872ab3fc12a8f4f43 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 13:02:52 +0530 Subject: [PATCH 016/161] feat: add secrets page to app layout --- frontend/app/[team]/apps/[app]/layout.tsx | 20 +- .../app/[team]/apps/[app]/secrets/page.tsx | 409 ++++++++++++++++++ 2 files changed, 422 insertions(+), 7 deletions(-) create mode 100644 frontend/app/[team]/apps/[app]/secrets/page.tsx diff --git a/frontend/app/[team]/apps/[app]/layout.tsx b/frontend/app/[team]/apps/[app]/layout.tsx index 74277d1bb..fb5f4d7bf 100644 --- a/frontend/app/[team]/apps/[app]/layout.tsx +++ b/frontend/app/[team]/apps/[app]/layout.tsx @@ -42,9 +42,10 @@ export default function AppLayout({ if (app) { const currentUrl = path?.split('/')[4] if (currentUrl === '') return 0 - if (currentUrl === 'logs') return 1 - if (currentUrl === 'keys') return 2 - if (currentUrl === 'settings') return 3 + if (currentUrl === 'keys') return 1 + if (currentUrl === 'secrets') return 2 + if (currentUrl === 'logs') return 3 + if (currentUrl === 'settings') return 4 } return 0 } @@ -56,14 +57,19 @@ export default function AppLayout({ name: 'Home', link: '', }, - { - name: 'Logs', - link: 'logs', - }, { name: 'Keys', link: 'keys', }, + { + name: 'Secrets', + link: 'secrets' + }, + { + name: 'Logs', + link: 'logs', + }, + { name: 'Settings', link: 'settings', diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx new file mode 100644 index 000000000..3be305b31 --- /dev/null +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -0,0 +1,409 @@ +'use client' + +import { GetAppEnvironments } from '@/apollo/queries/secrets/getAppEnvironments.gql' +import { GetSecrets } from '@/apollo/queries/secrets/getSecrets.gql' +import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' +import { GetOrganisationAdminsAndSelf } from '@/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql' +import { CreateEnvironment } from '@/apollo/mutations/environments/createEnvironment.gql' +import { CreateEnvironmentKey } from '@/apollo/mutations/environments/createEnvironmentKey.gql' +import { CreateEnvironmentSecret } from '@/apollo/mutations/environments/createEnvironmentSecret.gql' +import { GetEnvironmentSecrets } from '@/apollo/queries/secrets/getEnvironmentSecrets.gql' +import { CreateSecret } from '@/apollo/mutations/environments/createSecret.gql' +import { useLazyQuery, useMutation, useQuery } from '@apollo/client' +import { useEffect, useState } from 'react' +import { copyToClipBoard } from '@/utils/clipboard' +import { toast } from 'react-toastify' +import { useSession } from 'next-auth/react' +import { + envKeyring, + generateEnvironmentSecret, + newEnvSalt, + newEnvSeed, +} from '@/utils/environments' +import { Button } from '@/components/common/Button' +import { + ApiEnvironmentEnvTypeChoices, + ApiOrganisationMemberRoleChoices, + EnvironmentKeyType, + EnvironmentSecretType, + EnvironmentType, + OrganisationMemberType, + SecretType, +} from '@/apollo/graphql' +import { + decryptAsymmetric, + digest, + encryptAsymmetric, + getUserKxPrivateKey, + getUserKxPublicKey, +} from '@/utils/crypto' +import { cryptoUtils } from '@/utils/auth' +import { getLocalKeyring } from '@/utils/localStorage' +import _sodium, { KeyPair } from 'libsodium-wrappers-sumo' + +export default function Secrets({ params }: { params: { team: string; app: string } }) { + const { data } = useQuery(GetAppEnvironments, { + variables: { + appId: params.app, + }, + }) + const { data: orgsData } = useQuery(GetOrganisations) + + const [getOrgAdmins, { data: orgAdminsData }] = useLazyQuery(GetOrganisationAdminsAndSelf) + + const [createEnvironment, { loading, error }] = useMutation(CreateEnvironment) + const [createEnvironmentKey] = useMutation(CreateEnvironmentKey) + + const { data: session } = useSession() + + useEffect(() => { + if (orgsData) { + const organisationId = orgsData.organisations[0].id + getOrgAdmins({ + variables: { + organisationId, + }, + }) + } + }, [getOrgAdmins, orgsData, params.app]) + + const setupRequired = data?.appEnvironments.length === 0 ?? true + + const wrapEnvSecretsForUser = async ( + envSecrets: { seed: string; salt: string }, + user: OrganisationMemberType + ) => { + const userPubKey = await getUserKxPublicKey(user.identityKey!) + const wrappedSeed = await encryptAsymmetric(envSecrets.seed, userPubKey) + const wrappedSalt = await encryptAsymmetric(envSecrets.salt, userPubKey) + + console.log({ + wrappedSeed, + wrappedSalt, + }) + + return { + user, + wrappedSeed, + wrappedSalt, + } + } + + const createEnv = async () => { + const seed = await newEnvSeed() + const keys = await envKeyring(seed) + const salt = await newEnvSalt() + const name = 'testEnv' + const envType = ApiEnvironmentEnvTypeChoices.Dev + + const wrappedEnvSecrets = orgAdminsData.organisationAdminsAndSelf.map( + async (user: OrganisationMemberType) => { + const wrappedSeed = await encryptAsymmetric(seed, user.identityKey!) + const wrappedSalt = await encryptAsymmetric(salt, user.identityKey!) + + console.log({ + wrappedSeed, + wrappedSalt, + }) + + return { + user, + wrappedSeed, + wrappedSalt, + } + } + ) + + const owner = orgAdminsData.organisationAdminsAndSelf.find( + (user: OrganisationMemberType) => user.role === ApiOrganisationMemberRoleChoices.Owner + ) + + const ownerWrappedEnv = await wrapEnvSecretsForUser({ seed, salt }, owner) + + const envMutationPayload = { + id: crypto.randomUUID(), + appId: params.app, + name, + envType, + wrappedSeed: ownerWrappedEnv.wrappedSeed, + wrappedSalt: ownerWrappedEnv.wrappedSalt, + identityKey: keys.publicKey, + } + + const result = await createEnvironment({ + variables: envMutationPayload, + refetchQueries: [ + { + query: GetAppEnvironments, + variables: { + appId: params.app, + }, + }, + ], + }) + + console.log(result) + + // if (result.data.createEnvironment.environment) { + // const envKeyMutationPayload = { + // envId: result.data.createEnvironment.environment.id, + // ownerId: owner.id, + // wrappedSeed: ownerWrappedEnv.wrappedSeed, + // wrappedSalt: ownerWrappedEnv.wrappedSalt, + // identityKey: keys.publicKey, + // } + + // const keyResult = await createEnvironmentKey({ + // variables: envKeyMutationPayload, + // }) + + // console.log(keyResult) + // } + } + + const EnvironmentCard = (props: { environment: EnvironmentType }) => { + type EnvKeyring = { + privateKey: string + publicKey: string + } + + const { environment } = props + + const [key, setKey] = useState('') + const [value, setValue] = useState('') + const [envKeys, setEnvKeys] = useState(null) + const [envSecret, setEnvSecret] = useState('') + const [createSecret, { data, loading, error }] = useMutation(CreateSecret) + const [createEnvironmentSecret] = useMutation(CreateEnvironmentSecret) + const { data: secretsData } = useQuery(GetSecrets, { + variables: { + envId: environment.id, + }, + }) + const { data: envSecretsData } = useQuery(GetEnvironmentSecrets, { + variables: { + envId: environment.id, + }, + }) + const [secrets, setSecrets] = useState([]) + + useEffect(() => { + const initEnvKeys = async () => { + await _sodium.ready + const sodium = _sodium + + const sudoPass = 'testpassword1234' + const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) + const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) + if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' + const decryptedKeyring = await cryptoUtils.decryptAccountKeyring( + encryptedKeyring!, + deviceKey + ) + if (!decryptedKeyring) throw 'Failed to decrypt keys' + + const wrappedSeed = secretsData.environmentKeys[0].wrappedSeed + + const userKxKeys = { + publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), + privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), + } + const seed = await decryptAsymmetric( + wrappedSeed, + userKxKeys.privateKey, + userKxKeys.publicKey + ) + + const { publicKey, privateKey } = await envKeyring(seed) + + setEnvKeys({ + publicKey, + privateKey, + }) + } + + if (secretsData) initEnvKeys() + }, [secretsData]) + + useEffect(() => { + if (secretsData && envKeys) { + const decryptSecrets = async () => { + const decryptedSecrets = await Promise.all( + secretsData.secrets.map(async (secret: SecretType) => { + const decryptedSecret = structuredClone(secret) + decryptedSecret.key = await decryptAsymmetric( + secret.key, + envKeys?.privateKey, + envKeys?.publicKey + ) + decryptedSecret.value = await decryptAsymmetric( + secret.value, + envKeys.privateKey, + envKeys.publicKey + ) + return decryptedSecret + }) + ) + return decryptedSecrets + } + + decryptSecrets().then((decryptedSecrets) => { + setSecrets(decryptedSecrets) + }) + } + }, [envKeys, secretsData]) + + const decryptSecretField = async (encryptedField: string) => { + const decryptedField = await decryptAsymmetric( + encryptedField, + envKeys!.privateKey, + envKeys!.publicKey + ) + return decryptedField + } + + const handleCreateNewSecret = async () => { + const encryptedKey = await encryptAsymmetric(key, environment.identityKey) + const encryptedValue = await encryptAsymmetric(value, environment.identityKey) + const keyDigest = await digest(key) + + await createSecret({ + variables: { + envId: environment.id, + key: encryptedKey, + keyDigest, + value: encryptedValue, + }, + refetchQueries: [ + { + query: GetSecrets, + variables: { + envId: environment.id, + }, + }, + ], + }) + setKey('') + setValue('') + } + + const handleCreateNewEnvSecret = async () => { + const sudoPass = 'testpassword1234' + const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) + const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) + if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' + const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) + if (!decryptedKeyring) throw 'Failed to decrypt keys' + + const wrappedSeed = secretsData.environmentKeys[0].wrappedSeed + + const userKxKeys = { + publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), + privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), + } + + const { pssEnv, mutationPayload } = await generateEnvironmentSecret( + environment, + secretsData.environmentKeys[0], + userKxKeys + ) + + await createEnvironmentSecret({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetEnvironmentSecrets, + variables: { + envId: environment.id + } + } + ] + }) + + setEnvSecret(pssEnv) + } + + return ( +
+
+ {environment.name} + {environment.envType} +
+
+ {envKeys !== null && + secrets.map((secret: SecretType) => ( +
+
{secret.key}
+
{secret.value}
+
+ ))} +
+ + setKey(e.target.value)} + /> +
+
+ + setValue(e.target.value)} + /> +
+
+ +
+ +
+ {envSecretsData?.environmentSecrets.map((envSecret: EnvironmentSecretType) => ( +
+ {envSecret.name} | {envSecret.createdAt} +
+ ))} + {envSecret} +
+ +
+
+
+
+ ) + } + + return ( +
+
+ {setupRequired ? ( +
+

+ {"You don't have any environments for this app yet"} +

+ +
+ ) : ( +
+ {data?.appEnvironments.map((env: EnvironmentType) => ( + + ))} +
+ +
+
+ )} +
+
+ ) +} From b10f9c0ebf08eec98b7c8c4c92629924f7d74ea5 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 14:02:01 +0530 Subject: [PATCH 017/161] fix: env secret format --- frontend/utils/environments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index 6ab5d371a..cbe4f3b93 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -134,7 +134,7 @@ export const generateEnvironmentSecret = async (environment: EnvironmentType, ke useKeyring.publicKey ) - const pssEnv = `pss_env:v1:${envKeys.publicKey}:${token}:${keyShares[0]}:${wrapKey}:${envSalt}` + const pssEnv = `pss_env:v1:${envKeys.publicKey}:${keyShares[0]}:${token}:${wrapKey}:${envSalt}` const mutationPayload = { envId: environment.id, name: 'testSecret', From 194ef50a06f5ec4cf10d7162e6d27954e53afd44 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 15:44:09 +0530 Subject: [PATCH 018/161] fix: pss_env format --- frontend/utils/environments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index cbe4f3b93..e945a01a8 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -134,7 +134,7 @@ export const generateEnvironmentSecret = async (environment: EnvironmentType, ke useKeyring.publicKey ) - const pssEnv = `pss_env:v1:${envKeys.publicKey}:${keyShares[0]}:${token}:${wrapKey}:${envSalt}` + const pssEnv = `pss_env:v1:${token}:${envKeys.publicKey}:${envSalt}:${keyShares[0]}:${wrapKey}` const mutationPayload = { envId: environment.id, name: 'testSecret', From 86916c838c3fa562c8266c7521aa0080670f5c09 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 15:44:39 +0530 Subject: [PATCH 019/161] feat: add secrets serializer --- backend/api/serializers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 365174f96..b2d36507d 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework.serializers import ModelSerializer -from .models import CustomUser, Organisation +from .models import CustomUser, Organisation, Secret class CustomUserSerializer(ModelSerializer): @@ -29,3 +29,8 @@ class Meta: def create(self, validated_data): return Organisation(**validated_data) + +class SecretSerializer(ModelSerializer): + class Meta: + model = Secret + fields = '__all__' \ No newline at end of file From 1fd6550444d9ff3fb66f226a7d20cd8370e688b8 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 15:44:48 +0530 Subject: [PATCH 020/161] feat: add util to infer env from token --- backend/api/utils.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/backend/api/utils.py b/backend/api/utils.py index 29a2fa520..54f7c18e2 100644 --- a/backend/api/utils.py +++ b/backend/api/utils.py @@ -1,7 +1,23 @@ +from api.models import EnvironmentSecret + def get_client_ip(request): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[0] else: ip = request.META.get('REMOTE_ADDR') - return ip \ No newline at end of file + return ip + +def get_env_from_token(auth_token): + env_token = auth_token.split("Bearer ")[1] + + if not env_token: + return False + + try: + env_secret = EnvironmentSecret.objects.get(token=env_token) + return env_secret.environment, env_secret.user + except Exception as ex: + return False + + \ No newline at end of file From 2f10bd3c36c30ce360ed8a36bd6fe170992d6507 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 15:45:10 +0530 Subject: [PATCH 021/161] feat: add rest api for secrets --- backend/api/views.py | 129 +++++++++++++++++++++++++++++++++++++++- backend/backend/urls.py | 3 +- 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 3389bfd7e..ddb81c890 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -1,5 +1,7 @@ from datetime import datetime +import json +from api.serializers import SecretSerializer from dj_rest_auth.registration.views import SocialLoginView from django.contrib.auth.mixins import LoginRequiredMixin from graphene_django.views import GraphQLView @@ -8,9 +10,9 @@ from rest_framework.permissions import AllowAny from rest_framework.response import Response from django.http import JsonResponse, HttpResponse -from api.utils import get_client_ip +from api.utils import get_client_ip, get_env_from_token from logs.models import KMSDBLog -from .models import App +from .models import App, EnvironmentSecret, Secret, SecretEvent, SecretTag import jwt import requests from django.contrib.auth import logout @@ -23,6 +25,10 @@ from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter from allauth.socialaccount.providers.oauth2.views import OAuth2Adapter +from rest_framework.views import APIView +from rest_framework import status +from django.views.decorators.csrf import csrf_exempt +from django.utils import timezone CLOUD_HOSTED = settings.APP_HOST == 'cloud' @@ -207,3 +213,122 @@ def kms(request, app_id): class PrivateGraphQLView(LoginRequiredMixin, GraphQLView): raise_exception = True pass + +class SecretsView(APIView): + permission_classes = [AllowAny, ] + + @csrf_exempt + def dispatch(self, request, *args): + return super(SecretsView, self).dispatch(request, *args) + + def get(self, request): + auth_token = request.headers['authorization'] + env, user = get_env_from_token(auth_token) + + if not env.id: + return HttpResponse(status=404) + + secrets_filter = { + 'environment': env + } + + try: + key_digest = request.headers['keydigest'] + if key_digest: + secrets_filter['key_digest'] = key_digest + except: + pass + + secrets = Secret.objects.filter(**secrets_filter) + + serializer = SecretSerializer(secrets, many=True) + + return Response(serializer.data, status=status.HTTP_200_OK) + + def post(self, request): + auth_token = request.headers['authorization'] + env, user = get_env_from_token(auth_token) + if not env: + return HttpResponse(status=404) + + request_body = json.loads(request.body) + + for secret in request_body['secrets']: + + tag_names = SecretTag.objects.filter(id__in=secret['tags']).values('name') + + secret_data = { + 'environment': env, + 'key': secret['key'], + 'key_digest':secret['keyDigest'], + 'value': secret['value'], + 'folder_id': secret['folderId'], + 'version': 1, + 'tags': [], + 'comment': secret['comment'], + } + + Secret.objects.create(**secret_data) + SecretEvent.objects.create(**{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + + return Response(status=status.HTTP_200_OK) + + def put(self, request): + auth_token = request.headers['authorization'] + env, user = get_env_from_token(auth_token) + if not env: + return HttpResponse(status=404) + + request_body = json.loads(request.body) + + for secret in request_body['secrets']: + secret_obj = Secret.objects.get(id=secret['id']) + + tag_names = SecretTag.objects.filter(id__in=secret['tags']).values('name') + + secret_data = { + 'environment': env, + 'key': secret['key'], + 'key_digest':secret['keyDigest'], + 'value': secret['value'], + 'folder_id': secret['folderId'], + 'version': secret_obj.version + 1, + 'tags': [], + 'comment': secret['comment'], + } + + for key, value in secret_data.items(): + setattr(secret_obj, key, value) + + secret_obj.updated_at = timezone.now() + secret_obj.save() + SecretEvent.objects.create(**{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) + + return Response(status=status.HTTP_200_OK) + + def delete(self, request): + auth_token = request.headers['authorization'] + env, user = get_env_from_token(auth_token) + if not env: + return HttpResponse(status=404) + + request_body = json.loads(request.body) + + for secret in request_body['secrets']: + secret_obj = Secret.objects.get(id=secret['id']) + secret_obj.updated_at = timezone.now() + secret_obj.deleted_at = timezone.now() + secret_obj.save() + + most_recent_event = SecretEvent.objects.filter(secret=secret).order_by('version').last() + + # setting the pk to None and then saving it creates a copy of the instance with updated fields + most_recent_event.id = None + most_recent_event.event_type=SecretEvent.DELETE + most_recent_event.save() + + return Response(status=status.HTTP_200_OK) + + + + diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 872d7ed04..2d9e465a2 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -3,7 +3,7 @@ from django.conf import settings from graphene_django.views import GraphQLView from django.views.decorators.csrf import csrf_exempt -from api.views import PrivateGraphQLView, logout_view, health_check, kms +from api.views import PrivateGraphQLView, logout_view, health_check, kms, SecretsView CLOUD_HOSTED = settings.APP_HOST == 'cloud' @@ -14,6 +14,7 @@ path('logout/', csrf_exempt(logout_view)), path('graphql/', csrf_exempt(PrivateGraphQLView.as_view(graphiql=True))), path('493c5048-99f9-4eac-ad0d-98c3740b491f/health', health_check), + path('secrets/', SecretsView.as_view()), ] if not CLOUD_HOSTED: From 8a14c61590593a244eeeae5ed402ee95ce115bd1 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 15:45:19 +0530 Subject: [PATCH 022/161] fix: misc cleanup and fixes --- backend/backend/graphene/mutations/environment.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 2f03fed50..d257fd187 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -151,8 +151,6 @@ def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags tag_names = SecretTag.objects.filter(id__in=tags).values('name') - print("TAGS", tag_names) - secret_data = { 'environment_id': env.id, 'folder_id': folder_id, @@ -166,16 +164,10 @@ def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags secret = Secret.objects.create(**secret_data) - print("Created secret", secret) - org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) - print("member:", org_member) - SecretEvent.objects.create(**{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) - - print("Created event") - + return CreateSecretMutation(secret=secret) class EditSecretMutation(graphene.Mutation): @@ -215,6 +207,7 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment setattr(secret, key, value) secret.updated_at = timezone.now() + secret.save() org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) @@ -236,11 +229,13 @@ def mutate(cls, root, info, id): if not user_is_org_member(info.context.user.userId, org.id): raise GraphQLError("You don't have permission to perform this action") + secret.updated_at = timezone.now() secret.deleted_at = timezone.now() + secret.save() most_recent_event = SecretEvent.objects.filter(secret=secret).order_by('version').last() - # settings the pk to None and then saving it creates a copy of the instance with updated fields + # setting the pk to None and then saving it creates a copy of the instance with updated fields most_recent_event.id = None most_recent_event.event_type=SecretEvent.DELETE most_recent_event.save() From 31cf60b2ed54c1d4ef7838e79d44e159af8e2e6c Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 11 Aug 2023 15:45:49 +0530 Subject: [PATCH 023/161] feat: pass requests on /secrets to backend --- nginx/default.conf | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/nginx/default.conf b/nginx/default.conf index 461d45ba0..af66f2cdf 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -35,6 +35,21 @@ server { proxy_cookie_path / "/; HttpOnly; SameSite=strict"; } + location /secrets/ { + rewrite ^/secrets/(.*) /secrets/$1 break; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://backend:8000; + proxy_redirect off; + + proxy_cookie_path / "/; HttpOnly; SameSite=strict"; + } + location / { include /etc/nginx/mime.types; From 0f2a537162ad5bb72faf1fc5461b3cb5f23cd4ef Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 14:51:56 +0530 Subject: [PATCH 024/161] fix: prefix and version ciphertexts --- frontend/utils/crypto.ts | 147 ++++++++++++++++----------------- frontend/utils/environments.ts | 70 ++++++++-------- 2 files changed, 107 insertions(+), 110 deletions(-) diff --git a/frontend/utils/crypto.ts b/frontend/utils/crypto.ts index b186c053b..a74a6b544 100644 --- a/frontend/utils/crypto.ts +++ b/frontend/utils/crypto.ts @@ -2,18 +2,20 @@ import _sodium, { KeyPair } from 'libsodium-wrappers-sumo' import { cryptoUtils } from '@/utils/auth' +const VERSION = 1 + /** * Returns an random key exchange keypair * * @returns {KeyPair} */ export const randomKeyPair = async () => { - await _sodium.ready; - const sodium = _sodium; - const keypair = await sodium.crypto_kx_keypair(); + await _sodium.ready + const sodium = _sodium + const keypair = await sodium.crypto_kx_keypair() - return keypair; -}; + return keypair +} /** * Carries out diffie-hellman key exchange for client and returns a pair of symmetric encryption keys @@ -22,20 +24,17 @@ export const randomKeyPair = async () => { * @param {Uint8Array} recipientPubKey * @returns */ -export const clientSessionKeys = async ( - ephemeralKeyPair: KeyPair, - recipientPubKey: Uint8Array -) => { - await _sodium.ready; - const sodium = _sodium; +export const clientSessionKeys = async (ephemeralKeyPair: KeyPair, recipientPubKey: Uint8Array) => { + await _sodium.ready + const sodium = _sodium const keys = await sodium.crypto_kx_client_session_keys( ephemeralKeyPair.publicKey, ephemeralKeyPair.privateKey, recipientPubKey - ); - return keys; -}; + ) + return keys +} /** * Carries out diffie-hellman key exchange for server and returns a pair of symmetric encryption keys @@ -48,66 +47,59 @@ export const serverSessionKeys = async ( appKeyPair: { publicKey: Uint8Array; privateKey: Uint8Array }, dataPubKey: Uint8Array ) => { - await _sodium.ready; - const sodium = _sodium; + await _sodium.ready + const sodium = _sodium const keys = await sodium.crypto_kx_server_session_keys( appKeyPair.publicKey, appKeyPair.privateKey, dataPubKey - ); - return keys; -}; + ) + return keys +} -export const encryptAsymmetric = async ( - plaintext: string, - publicKey: string, -): Promise => { - await _sodium.ready; - const sodium = _sodium; +export const encryptAsymmetric = async (plaintext: string, publicKey: string): Promise => { + await _sodium.ready + const sodium = _sodium return new Promise(async (resolve, reject) => { try { - const oneTimeKeyPair = await randomKeyPair(); + const oneTimeKeyPair = await randomKeyPair() - const symmetricKeys = await clientSessionKeys( - oneTimeKeyPair, - sodium.from_hex(publicKey) - ); + const symmetricKeys = await clientSessionKeys(oneTimeKeyPair, sodium.from_hex(publicKey)) - const ciphertext = await cryptoUtils.encryptString( - plaintext, - symmetricKeys.sharedTx - ); + const ciphertext = await cryptoUtils.encryptString(plaintext, symmetricKeys.sharedTx) // Use sodium.memzero to wipe the keys from memory - sodium.memzero(oneTimeKeyPair.privateKey); - sodium.memzero(symmetricKeys.sharedTx); - sodium.memzero(symmetricKeys.sharedRx); - - resolve( - `${sodium.to_hex( - oneTimeKeyPair.publicKey - )}:${ciphertext}` - ); + sodium.memzero(oneTimeKeyPair.privateKey) + sodium.memzero(symmetricKeys.sharedTx) + sodium.memzero(symmetricKeys.sharedRx) + + resolve(`ph:v${VERSION}:${sodium.to_hex(oneTimeKeyPair.publicKey)}:${ciphertext}`) } catch (error) { - reject(`Something went wrong: ${error}`); + reject(`Something went wrong: ${error}`) } - }); -}; + }) +} -export const decryptAsymmetric = async (ciphertextString: string, privateKey: string, publicKey: string): Promise => { - await _sodium.ready; - const sodium = _sodium; +export const decryptAsymmetric = async ( + ciphertextString: string, + privateKey: string, + publicKey: string +): Promise => { + await _sodium.ready + const sodium = _sodium return new Promise(async (resolve, reject) => { - const ciphertextSegments = ciphertextString.split(":"); - if (ciphertextSegments.length !== 2) - reject("Invalid ciphertext"); + const ciphertextSegments = ciphertextString.split(':') + + if (ciphertextSegments.length !== 4) reject('Invalid ciphertext') const ciphertext = { - pubKey: ciphertextSegments[0], - data: ciphertextSegments[1], - }; + prefix: ciphertextSegments[0], + version: ciphertextSegments[1], + pubKey: ciphertextSegments[2], + data: ciphertextSegments[3], + } try { const sessionKeys = await serverSessionKeys( @@ -116,42 +108,43 @@ export const decryptAsymmetric = async (ciphertextString: string, privateKey: st privateKey: sodium.from_hex(privateKey) as Uint8Array, }, sodium.from_hex(ciphertext.pubKey) - ); + ) - const plaintext = await cryptoUtils.decryptString( - ciphertext.data, - sessionKeys.sharedRx - ); + const plaintext = await cryptoUtils.decryptString(ciphertext.data, sessionKeys.sharedRx) // Use sodium.memzero to wipe the keys from memory - sodium.memzero(sessionKeys.sharedRx); - sodium.memzero(sessionKeys.sharedTx); + sodium.memzero(sessionKeys.sharedRx) + sodium.memzero(sessionKeys.sharedTx) - resolve(plaintext); + resolve(plaintext) } catch (error) { - reject(`Something went wrong: ${error}`); + reject(`Something went wrong: ${error}`) } - }); -}; + }) +} export const getUserKxPublicKey = async (signingPublicKey: string) => { - await _sodium.ready; - const sodium = _sodium; - - return sodium.to_hex(sodium.crypto_sign_ed25519_pk_to_curve25519(sodium.from_hex(signingPublicKey))) + await _sodium.ready + const sodium = _sodium + + return sodium.to_hex( + sodium.crypto_sign_ed25519_pk_to_curve25519(sodium.from_hex(signingPublicKey)) + ) } export const getUserKxPrivateKey = async (signingPrivateKey: string) => { - await _sodium.ready; - const sodium = _sodium; - - return sodium.to_hex(sodium.crypto_sign_ed25519_sk_to_curve25519(sodium.from_hex(signingPrivateKey))) + await _sodium.ready + const sodium = _sodium + + return sodium.to_hex( + sodium.crypto_sign_ed25519_sk_to_curve25519(sodium.from_hex(signingPrivateKey)) + ) } export const digest = async (input: string) => { - await _sodium.ready; - const sodium = _sodium; + await _sodium.ready + const sodium = _sodium const hash = await sodium.crypto_generichash(32, input) return sodium.to_base64(hash, sodium.base64_variants.ORIGINAL) -} \ No newline at end of file +} diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index e945a01a8..8ab2fb56f 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -10,10 +10,10 @@ type EnvKeyring = { } /** - * Create a random seed for a new env - * - * @returns {Promise} - hex encoded env seed - */ + * Create a random seed for a new env + * + * @returns {Promise} - hex encoded env seed + */ export const newEnvSeed = async () => { await _sodium.ready const sodium = _sodium @@ -23,10 +23,10 @@ export const newEnvSeed = async () => { } /** - * Create a random salt for a new env - * - * @returns {Promise} - hex encoded env salt - */ + * Create a random salt for a new env + * + * @returns {Promise} - hex encoded env salt + */ export const newEnvSalt = async () => { await _sodium.ready const sodium = _sodium @@ -41,11 +41,11 @@ export const newEnvSalt = async () => { * @returns {Promise} - hex encoded env token */ export const newEnvToken = async () => { -await _sodium.ready -const sodium = _sodium + await _sodium.ready + const sodium = _sodium -const token = sodium.crypto_kdf_keygen() -return sodium.to_hex(token) + const token = sodium.crypto_kdf_keygen() + return sodium.to_hex(token) } /** @@ -62,12 +62,12 @@ export const newEnvWrapKey = async () => { } /** - * Encrypts an env seed with the given key - * - * @param seed - Env seed as a hex string - * @param key - Encryption key as a hex string - * @returns {Promise} - */ + * Encrypts an env seed with the given key + * + * @param seed - Env seed as a hex string + * @param key - Encryption key as a hex string + * @returns {Promise} + */ export const encryptedEnvSeed = async (seed: string, key: string) => { await _sodium.ready const sodium = _sodium @@ -78,12 +78,12 @@ export const encryptedEnvSeed = async (seed: string, key: string) => { } /** - * Decrypts an env seed with the given key - * - * @param encryptedSeed - Encrytped env seed as a hex string - * @param key - Decryption key as a hex string - * @returns {Promise} - hex encoded plaintext app seed - */ + * Decrypts an env seed with the given key + * + * @param encryptedSeed - Encrytped env seed as a hex string + * @param key - Decryption key as a hex string + * @returns {Promise} - hex encoded plaintext app seed + */ export const decryptedAppSeed = async (encryptedSeed: string, key: string) => { await _sodium.ready const sodium = _sodium @@ -96,11 +96,11 @@ export const decryptedAppSeed = async (encryptedSeed: string, key: string) => { } /** - * Derives an env keyring from the given seed - * - * @param {string} envSeed - Env seed as a hex string - * @returns {Promise} - */ + * Derives an env keyring from the given seed + * + * @param {string} envSeed - Env seed as a hex string + * @returns {Promise} + */ export const envKeyring = async (envSeed: string): Promise => { await _sodium.ready const sodium = _sodium @@ -113,7 +113,11 @@ export const envKeyring = async (envSeed: string): Promise => { return { publicKey: sodium.to_hex(publicKey), privateKey: sodium.to_hex(privateKey) } } -export const generateEnvironmentSecret = async (environment: EnvironmentType, key: EnvironmentKeyType, useKeyring: { publicKey: string, privateKey: string }) => { +export const generateEnvironmentSecret = async ( + environment: EnvironmentType, + key: EnvironmentKeyType, + useKeyring: { publicKey: string; privateKey: string } +) => { const wrapKey = await newEnvWrapKey() const token = await newEnvToken() @@ -140,11 +144,11 @@ export const generateEnvironmentSecret = async (environment: EnvironmentType, ke name: 'testSecret', identityKey: environment.identityKey, token, - wrappedKeyShare + wrappedKeyShare, } return { pssEnv, - mutationPayload + mutationPayload, } -} \ No newline at end of file +} From c92e9d32e052c4a148f069551f7ebe1cd1f37b01 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 14:54:48 +0530 Subject: [PATCH 025/161] fix: update wrapped value max length --- ...alter_environment_wrapped_salt_and_more.py | 33 +++++++++++ backend/api/models.py | 57 ++++++++++++------- 2 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 backend/api/migrations/0024_alter_environment_wrapped_salt_and_more.py diff --git a/backend/api/migrations/0024_alter_environment_wrapped_salt_and_more.py b/backend/api/migrations/0024_alter_environment_wrapped_salt_and_more.py new file mode 100644 index 000000000..346581de7 --- /dev/null +++ b/backend/api/migrations/0024_alter_environment_wrapped_salt_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.3 on 2023-08-12 09:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0023_environment_identity_key'), + ] + + operations = [ + migrations.AlterField( + model_name='environment', + name='wrapped_salt', + field=models.CharField(max_length=256), + ), + migrations.AlterField( + model_name='environment', + name='wrapped_seed', + field=models.CharField(max_length=256), + ), + migrations.AlterField( + model_name='environmentkey', + name='wrapped_salt', + field=models.CharField(max_length=256), + ), + migrations.AlterField( + model_name='environmentkey', + name='wrapped_seed', + field=models.CharField(max_length=256), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 2deb037b7..8536cd814 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -9,6 +9,7 @@ CLOUD_HOSTED = settings.APP_HOST == 'cloud' + class CustomUserManager(BaseUserManager): def create_user(self, username, email, password=None): """ @@ -73,7 +74,7 @@ class Organisation(models.Model): (PRO_PLAN, 'Pro'), (ENTERPRISE_PLAN, 'Enterprise') ] - + id = models.TextField(default=uuid4, primary_key=True, editable=False) name = models.CharField(max_length=64, unique=True) identity_key = models.CharField(max_length=256) @@ -89,6 +90,7 @@ class Organisation(models.Model): def __str__(self): return self.name + class OrganisationMember(models.Model): OWNER = 'owner' ADMIN = 'admin' @@ -103,7 +105,8 @@ class OrganisationMember(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) user = models.ForeignKey( CustomUser, related_name='organisation', on_delete=models.CASCADE) - organisation = models.ForeignKey(Organisation, related_name='users', on_delete=models.CASCADE) + organisation = models.ForeignKey( + Organisation, related_name='users', on_delete=models.CASCADE) role = models.CharField( max_length=5, choices=USER_ROLES, @@ -115,6 +118,7 @@ class OrganisationMember(models.Model): updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(auto_now=True) + class App(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) @@ -147,8 +151,9 @@ def save(self, *args, **kwargs): def __str__(self): return self.name + class Environment(models.Model): - + DEVELOPMENT = "dev" STAGING = "staging" PRODUCTION = "prod" @@ -158,7 +163,7 @@ class Environment(models.Model): (STAGING, 'Staging'), (PRODUCTION, 'Production') ] - + id = models.TextField(default=uuid4, primary_key=True, editable=False) app = models.ForeignKey(App, on_delete=models.CASCADE) name = models.CharField(max_length=64) @@ -168,28 +173,32 @@ class Environment(models.Model): default=DEVELOPMENT, ) identity_key = models.CharField(max_length=256) - wrapped_seed = models.CharField(max_length=208) - wrapped_salt = models.CharField(max_length=208) + wrapped_seed = models.CharField(max_length=256) + wrapped_salt = models.CharField(max_length=256) 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) is_deleted = models.BooleanField(default=False) + class EnvironmentKey(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) - user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) + user = models.ForeignKey( + OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) identity_key = models.CharField(max_length=256) - wrapped_seed = models.CharField(max_length=208) - wrapped_salt = models.CharField(max_length=208) + wrapped_seed = models.CharField(max_length=256) + wrapped_salt = models.CharField(max_length=256) 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) + class EnvironmentSecret(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) - user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) + user = models.ForeignKey( + OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) name = models.CharField(max_length=64) identity_key = models.CharField(max_length=256) token = models.CharField(max_length=64) @@ -198,6 +207,7 @@ class EnvironmentSecret(models.Model): updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(blank=True, null=True) + class SecretFolder(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) @@ -207,6 +217,7 @@ class SecretFolder(models.Model): updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(blank=True, null=True) + class SecretTag(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) @@ -215,18 +226,20 @@ class SecretTag(models.Model): updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(blank=True, null=True) + class Secret(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) - folder = models.ForeignKey(SecretFolder, on_delete=models.CASCADE, null=True) + folder = models.ForeignKey( + SecretFolder, on_delete=models.CASCADE, null=True) key = models.TextField() key_digest = models.TextField() value = models.TextField() version = models.IntegerField(default=1) tags = ArrayField( - models.CharField(max_length=64), - size=10, - ) + models.CharField(max_length=64), + size=10, + ) comment = models.TextField() created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) updated_at = models.DateTimeField(auto_now=True) @@ -234,7 +247,7 @@ class Secret(models.Model): class SecretEvent(models.Model): - + CREATE = "C" READ = "R" UPDATE = "U" @@ -246,20 +259,22 @@ class SecretEvent(models.Model): (UPDATE, 'Update'), (DELETE, 'Delete') ] - + id = models.TextField(default=uuid4, primary_key=True, editable=False) secret = models.ForeignKey(Secret, on_delete=models.CASCADE) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) - folder = models.ForeignKey(SecretFolder, on_delete=models.CASCADE, null=True) - user = models.ForeignKey(OrganisationMember, on_delete=models.SET_NULL, blank=True, null=True) + folder = models.ForeignKey( + SecretFolder, on_delete=models.CASCADE, null=True) + user = models.ForeignKey( + OrganisationMember, on_delete=models.SET_NULL, blank=True, null=True) key = models.TextField() key_digest = models.TextField() value = models.TextField() version = models.IntegerField(default=1) tags = ArrayField( - models.CharField(max_length=64), - size=10, - ) + models.CharField(max_length=64), + size=10, + ) comment = models.TextField() event_type = models.CharField( max_length=1, From 8ac2ded97b7fdd60217a0a4016c913d1863192f2 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 14:55:39 +0530 Subject: [PATCH 026/161] chore: format on save --- .vscode/settings.json | 8 ++- backend/api/views.py | 101 ++++++++++++++++++++------------------ backend/backend/schema.py | 89 ++++++++++++++++++--------------- 3 files changed, 108 insertions(+), 90 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 1a8213362..765df6263 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,7 @@ "package-lock.json": true }, "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "editor.formatOnSave": false, + "editor.formatOnSave": true, "editor.codeActionsOnSave": [ "source.addMissingImports", "source.fixAll.eslint" @@ -21,5 +21,9 @@ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode" }, - "prettier.ignorePath": ".gitignore" // Don't run prettier for files listed in .gitignore + "prettier.ignorePath": ".gitignore", + "[python]": { + "editor.defaultFormatter": "ms-python.autopep8", + "editor.formatOnSave": true + } // Don't run prettier for files listed in .gitignore } diff --git a/backend/api/views.py b/backend/api/views.py index ddb81c890..294615175 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -33,6 +33,8 @@ CLOUD_HOSTED = settings.APP_HOST == 'cloud' # for custom gitlab adapter class + + def _check_errors(response): # 403 error's are presented as user-facing errors if response.status_code == 403: @@ -178,8 +180,8 @@ def logout_view(request): @api_view(['GET']) @permission_classes([AllowAny]) -def health_check(request): - return JsonResponse({ +def health_check(request): + return JsonResponse({ 'status': 'alive' }) @@ -200,7 +202,8 @@ def kms(request, app_id): app = App.objects.get(app_token=app_token) try: timestamp = datetime.now().timestamp() * 1000 - KMSDBLog.objects.create(app_id=app_id, event_type=event_type, phase_node=phase_node, ph_size=float(ph_size), ip_address=ip_address, timestamp=timestamp) + KMSDBLog.objects.create(app_id=app_id, event_type=event_type, phase_node=phase_node, ph_size=float( + ph_size), ip_address=ip_address, timestamp=timestamp) except: pass return JsonResponse({ @@ -214,104 +217,109 @@ class PrivateGraphQLView(LoginRequiredMixin, GraphQLView): raise_exception = True pass + class SecretsView(APIView): permission_classes = [AllowAny, ] @csrf_exempt def dispatch(self, request, *args): return super(SecretsView, self).dispatch(request, *args) - + def get(self, request): auth_token = request.headers['authorization'] env, user = get_env_from_token(auth_token) - + if not env.id: return HttpResponse(status=404) - + secrets_filter = { 'environment': env } try: - key_digest = request.headers['keydigest'] - if key_digest: - secrets_filter['key_digest'] = key_digest + key_digest = request.headers['keydigest'] + if key_digest: + secrets_filter['key_digest'] = key_digest except: pass secrets = Secret.objects.filter(**secrets_filter) serializer = SecretSerializer(secrets, many=True) - + return Response(serializer.data, status=status.HTTP_200_OK) - + def post(self, request): auth_token = request.headers['authorization'] env, user = get_env_from_token(auth_token) if not env: return HttpResponse(status=404) - + request_body = json.loads(request.body) for secret in request_body['secrets']: - - tag_names = SecretTag.objects.filter(id__in=secret['tags']).values('name') - + + tag_names = SecretTag.objects.filter( + id__in=secret['tags']).values('name') + secret_data = { - 'environment': env, - 'key': secret['key'], - 'key_digest':secret['keyDigest'], - 'value': secret['value'], - 'folder_id': secret['folderId'], - 'version': 1, - 'tags': [], - 'comment': secret['comment'], + 'environment': env, + 'key': secret['key'], + 'key_digest': secret['keyDigest'], + 'value': secret['value'], + 'folder_id': secret['folderId'], + 'version': 1, + 'tags': [], + 'comment': secret['comment'], } Secret.objects.create(**secret_data) - SecretEvent.objects.create(**{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + SecretEvent.objects.create( + **{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.CREATE}}) return Response(status=status.HTTP_200_OK) - + def put(self, request): auth_token = request.headers['authorization'] env, user = get_env_from_token(auth_token) if not env: return HttpResponse(status=404) - + request_body = json.loads(request.body) for secret in request_body['secrets']: secret_obj = Secret.objects.get(id=secret['id']) - - tag_names = SecretTag.objects.filter(id__in=secret['tags']).values('name') - + + tag_names = SecretTag.objects.filter( + id__in=secret['tags']).values('name') + secret_data = { - 'environment': env, - 'key': secret['key'], - 'key_digest':secret['keyDigest'], - 'value': secret['value'], - 'folder_id': secret['folderId'], - 'version': secret_obj.version + 1, - 'tags': [], - 'comment': secret['comment'], + 'environment': env, + 'key': secret['key'], + 'key_digest': secret['keyDigest'], + 'value': secret['value'], + 'folder_id': secret['folderId'], + 'version': secret_obj.version + 1, + 'tags': [], + 'comment': secret['comment'], } for key, value in secret_data.items(): - setattr(secret_obj, key, value) - + setattr(secret_obj, key, value) + secret_obj.updated_at = timezone.now() secret_obj.save() - SecretEvent.objects.create(**{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) + SecretEvent.objects.create( + **{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) return Response(status=status.HTTP_200_OK) - + def delete(self, request): auth_token = request.headers['authorization'] env, user = get_env_from_token(auth_token) if not env: return HttpResponse(status=404) - + request_body = json.loads(request.body) for secret in request_body['secrets']: @@ -320,15 +328,12 @@ def delete(self, request): secret_obj.deleted_at = timezone.now() secret_obj.save() - most_recent_event = SecretEvent.objects.filter(secret=secret).order_by('version').last() + most_recent_event = SecretEvent.objects.filter( + secret=secret).order_by('version').last() # setting the pk to None and then saving it creates a copy of the instance with updated fields most_recent_event.id = None - most_recent_event.event_type=SecretEvent.DELETE + most_recent_event.event_type = SecretEvent.DELETE most_recent_event.save() return Response(status=status.HTTP_200_OK) - - - - diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 0cd218e85..e54684240 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -14,10 +14,13 @@ CLOUD_HOSTED = settings.APP_HOST == 'cloud' + class Query(graphene.ObjectType): organisations = graphene.List(OrganisationType) - organisation_members = graphene.List(OrganisationMemberType, organisation_id=graphene.ID(), user_id=graphene.ID(), role=graphene.List(graphene.String)) - organisation_admins_and_self = graphene.List(OrganisationMemberType, organisation_id=graphene.ID()) + organisation_members = graphene.List(OrganisationMemberType, organisation_id=graphene.ID( + ), user_id=graphene.ID(), role=graphene.List(graphene.String)) + organisation_admins_and_self = graphene.List( + OrganisationMemberType, organisation_id=graphene.ID()) apps = graphene.List( AppType, organisation_id=graphene.ID(), app_id=graphene.ID()) logs = graphene.List(KMSLogType, app_id=graphene.ID(), @@ -33,37 +36,39 @@ class Query(graphene.ObjectType): secrets = graphene.List(SecretType, env_id=graphene.ID()) secret_history = graphene.List(SecretEventType, secret_id=graphene.ID()) secret_tags = graphene.List(SecretTagType, org_id=graphene.ID()) - environment_keys = graphene.List(EnvironmentKeyType, environment_id=graphene.ID()) - environment_secrets = graphene.List(EnvironmentSecretType, environment_id=graphene.ID()) - + environment_keys = graphene.List( + EnvironmentKeyType, environment_id=graphene.ID()) + environment_secrets = graphene.List( + EnvironmentSecretType, environment_id=graphene.ID()) def resolve_organisations(root, info): memberships = OrganisationMember.objects.filter(user=info.context.user) return [membership.organisation for membership in memberships] - + def resolve_organisation_members(root, info, organisation_id, role, user_id=None): if not user_is_org_member(info.context.user.userId, organisation_id): raise GraphQLError("You don't have access to this organisation") - + roles = [user_role.lower() for user_role in role] return OrganisationMember.objects.filter(organisation_id=organisation_id, role__in=roles) - + def resolve_organisation_admins_and_self(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") - + roles = ['owner', 'admin'] - members = OrganisationMember.objects.filter(organisation_id=organisation_id, role__in=roles) - + members = OrganisationMember.objects.filter( + organisation_id=organisation_id, role__in=roles) + 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) - members = list(chain(members, self_member)) + self_member = OrganisationMember.objects.filter( + organisation_id=organisation_id, user_id=info.context.user.userId) + members = list(chain(members, self_member)) return members - def resolve_apps(root, info, organisation_id, app_id): filter = { 'organisation_id': organisation_id, @@ -72,74 +77,76 @@ def resolve_apps(root, info, organisation_id, app_id): if app_id != '': filter['id'] = app_id return App.objects.filter(**filter) - + def resolve_app_environments(root, info, app_id): if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") - + app = App.objects.get(id=app_id) - org_member = OrganisationMember.objects.get(organisation=app.organisation, user_id=info.context.user.userId) - + org_member = OrganisationMember.objects.get( + organisation=app.organisation, user_id=info.context.user.userId) + app_environments = Environment.objects.filter(app_id=app_id) return [app_env for app_env in app_environments if EnvironmentKey.objects.filter(user=org_member, environment_id=app_env.id).exists()] def resolve_secrets(root, info, env_id): if not user_can_access_environment(info.context.user.userId, env_id): raise GraphQLError("You don't have access to this environment") - + return Secret.objects.filter(environment_id=env_id) - + def resolve_secret_history(root, info, secret_id): secret = Secret.objects.get(id=secret_id) if not user_can_access_environment(info.context.user.userId, secret.environment.id): raise GraphQLError("You don't have access to this secret") return SecretEvent.objects.filter(secret_id=secret_id) - + def resolve_secret_tags(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") - + return SecretTag.objects.filter(org_id=org_id) - + def resolve_environment_keys(root, info, environment_id): if not user_can_access_environment(info.context.user.userId, environment_id): raise GraphQLError("You don't have access to this secret") - + env = Environment.objects.get(id=environment_id) - org_member = OrganisationMember.objects.get(user=info.context.user, organisation=env.app.organisation) + org_member = OrganisationMember.objects.get( + user=info.context.user, organisation=env.app.organisation) return EnvironmentKey.objects.filter(environment=env, user=org_member) - + def resolve_environment_secrets(root, info, environment_id): if not user_can_access_environment(info.context.user.userId, environment_id): raise GraphQLError("You don't have access to this secret") - + env = Environment.objects.get(id=environment_id) - org_member = OrganisationMember.objects.get(user=info.context.user, organisation=env.app.organisation) + org_member = OrganisationMember.objects.get( + user=info.context.user, organisation=env.app.organisation) return EnvironmentSecret.objects.filter(environment=env, user=org_member) - - - + 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") - + app = App.objects.get(id=app_id) - + if end == 0: end = datetime.now().timestamp() * 1000 - + if CLOUD_HOSTED: return get_app_logs(f"phApp:v{app.app_version}:{app.identity_key}", start, end, 25) - - logs = KMSDBLog.objects.filter(app_id=f"phApp:v{app.app_version}:{app.identity_key}",timestamp__lte=end, timestamp__gte=start).order_by('-timestamp')[:25] - + + logs = KMSDBLog.objects.filter( + app_id=f"phApp:v{app.app_version}:{app.identity_key}", timestamp__lte=end, timestamp__gte=start).order_by('-timestamp')[:25] + return list(logs.values()) def resolve_logs_count(root, info, app_id): if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") - + app = App.objects.get(id=app_id) if CLOUD_HOSTED: @@ -160,7 +167,7 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): Returns: List[ChartDataPointType]: Time series decrypt count data """ - + app = App.objects.get(id=app_id) if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") @@ -219,7 +226,8 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): decrypts = get_app_log_count_range( f"phApp:v{app.app_version}:{app.identity_key}", start_unix, end_unix) else: - decrypts = KMSDBLog.objects.filter(app_id=f"phApp:v{app.app_version}:{app.identity_key}",timestamp__lte=end_unix, timestamp__gte=start_unix).count() + decrypts = KMSDBLog.objects.filter( + app_id=f"phApp:v{app.app_version}:{app.identity_key}", timestamp__lte=end_unix, timestamp__gte=start_unix).count() time_series_logs.append(ChartDataPointType( index=str(index), date=end_unix, data=decrypts)) @@ -230,6 +238,7 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): return time_series_logs + class Mutation(graphene.ObjectType): create_organisation = CreateOrganisationMutation.Field() create_app = CreateAppMutation.Field() From dd260eb4a7714176b6b2defa0a46407630337ace Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:09:30 +0530 Subject: [PATCH 027/161] fix: rename env secret model to env token, add user token model --- ...onmentsecret_environmenttoken_usertoken.py | 33 +++++++++++++++++++ backend/api/models.py | 15 ++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 backend/api/migrations/0025_rename_environmentsecret_environmenttoken_usertoken.py diff --git a/backend/api/migrations/0025_rename_environmentsecret_environmenttoken_usertoken.py b/backend/api/migrations/0025_rename_environmentsecret_environmenttoken_usertoken.py new file mode 100644 index 000000000..7b70a0e3b --- /dev/null +++ b/backend/api/migrations/0025_rename_environmentsecret_environmenttoken_usertoken.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.3 on 2023-08-12 10:28 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0024_alter_environment_wrapped_salt_and_more'), + ] + + operations = [ + migrations.RenameModel( + old_name='EnvironmentSecret', + new_name='EnvironmentToken', + ), + migrations.CreateModel( + name='UserToken', + 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)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember')), + ], + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 8536cd814..3f4b2b5bb 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -194,7 +194,7 @@ class EnvironmentKey(models.Model): deleted_at = models.DateTimeField(blank=True, null=True) -class EnvironmentSecret(models.Model): +class EnvironmentToken(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) user = models.ForeignKey( @@ -208,6 +208,19 @@ class EnvironmentSecret(models.Model): deleted_at = models.DateTimeField(blank=True, null=True) +class UserToken(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + user = models.ForeignKey( + OrganisationMember, on_delete=models.CASCADE, blank=True, null=True) + 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, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(blank=True, null=True) + + class SecretFolder(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) environment = models.ForeignKey(Environment, on_delete=models.CASCADE) From 30f513f3bb2fa4455fc08e599c16ee522a6e2e16 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:10:12 +0530 Subject: [PATCH 028/161] chore: update types and schema to use environmen token --- backend/api/utils.py | 12 +- backend/api/views.py | 2 +- .../backend/graphene/mutations/environment.py | 139 ++++++++++-------- backend/backend/graphene/types.py | 44 ++++-- backend/backend/schema.py | 16 +- 5 files changed, 126 insertions(+), 87 deletions(-) diff --git a/backend/api/utils.py b/backend/api/utils.py index 54f7c18e2..dfc99f4e6 100644 --- a/backend/api/utils.py +++ b/backend/api/utils.py @@ -1,4 +1,5 @@ -from api.models import EnvironmentSecret +from api.models import EnvironmentToken + def get_client_ip(request): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') @@ -8,16 +9,15 @@ def get_client_ip(request): ip = request.META.get('REMOTE_ADDR') return ip + def get_env_from_token(auth_token): env_token = auth_token.split("Bearer ")[1] if not env_token: return False - + try: - env_secret = EnvironmentSecret.objects.get(token=env_token) - return env_secret.environment, env_secret.user + env_secret = EnvironmentToken.objects.get(token=env_token) + return env_secret.environment, env_secret.user except Exception as ex: return False - - \ No newline at end of file diff --git a/backend/api/views.py b/backend/api/views.py index 294615175..ea374453f 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -12,7 +12,7 @@ from django.http import JsonResponse, HttpResponse from api.utils import get_client_ip, get_env_from_token from logs.models import KMSDBLog -from .models import App, EnvironmentSecret, Secret, SecretEvent, SecretTag +from .models import App, EnvironmentToken, Secret, SecretEvent, SecretTag import jwt import requests from django.contrib.auth import logout diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index d257fd187..91466405b 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -2,8 +2,8 @@ from backend.graphene.utils.permissions import member_can_access_org, user_can_access_app, user_can_access_environment, user_is_org_member import graphene from graphql import GraphQLError -from api.models import App, Environment, EnvironmentKey, EnvironmentSecret, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag -from backend.graphene.types import EnvironmentKeyType, EnvironmentSecretType, EnvironmentType, SecretFolderType, SecretTagType, SecretType +from api.models import App, Environment, EnvironmentKey, EnvironmentToken, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag +from backend.graphene.types import EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, SecretFolderType, SecretTagType, SecretType class CreateEnvironmentMutation(graphene.Mutation): @@ -21,25 +21,29 @@ class Arguments: @classmethod def mutate(cls, root, info, id, app_id, name, env_type, wrapped_seed, wrapped_salt, identity_key): user_id = info.context.user.userId - + if not user_can_access_app(user_id, app_id): - raise GraphQLError("You don't have access to this app") - + raise GraphQLError("You don't have access to this app") + app = App.objects.get(id=app_id) - environment = Environment.objects.create(id=id, app=app, name=name, env_type=env_type, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + environment = Environment.objects.create( + id=id, app=app, name=name, env_type=env_type, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + + org_owner = OrganisationMember.objects.get( + organisation=environment.app.organisation, role=OrganisationMember.OWNER) - org_owner = OrganisationMember.objects.get(organisation=environment.app.organisation, role=OrganisationMember.OWNER) + EnvironmentKey.objects.create(id=id, environment=environment, user=org_owner, + identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) - EnvironmentKey.objects.create(id=id, environment=environment, user=org_owner, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) - return CreateEnvironmentMutation(environment=environment) - + + class CreateEnvironmentKeyMutation(graphene.Mutation): class Arguments: - #id = graphene.ID(required=True) + # id = graphene.ID(required=True) env_id = graphene.ID(required=True) - user_id =graphene.ID(required=True) + user_id = graphene.ID(required=True) identity_key = graphene.String(required=True) wrapped_seed = graphene.String(required=True) wrapped_salt = graphene.String(required=True) @@ -48,27 +52,30 @@ class Arguments: @classmethod def mutate(cls, root, info, env_id, user_id, identity_key, wrapped_seed, wrapped_salt): - + env = Environment.objects.get(id=env_id) - + # check that the user attempting the mutation has access if not user_can_access_app(info.context.user.userId, env.app.id): - raise GraphQLError("You don't have access to this app") - + raise GraphQLError("You don't have access to this app") + # check that the user for whom we are add a key has access if not member_can_access_org(user_id, env.app.organisation.id): - raise GraphQLError("This user doesn't have access to this app") + raise GraphQLError("This user doesn't have access to this app") org_member = OrganisationMember.objects.get(id=user_id) if EnvironmentKey.objects.filter(environment=env, user_id=org_member).exists(): - raise GraphQLError("This user already has access to this environment") - - environment_key = EnvironmentKey.objects.create(environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) - + raise GraphQLError( + "This user already has access to this environment") + + environment_key = EnvironmentKey.objects.create( + environment=env, user_id=user_id, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + return CreateEnvironmentKeyMutation(environment_key=environment_key) - -class CreateEnvironmentSecretMutation(graphene.Mutation): + + +class CreateEnvironmentTokenMutation(graphene.Mutation): class Arguments: env_id = graphene.ID(required=True) name = graphene.String(required=True) @@ -76,37 +83,42 @@ class Arguments: token = graphene.String(required=True) wrapped_key_share = graphene.String(required=True) - environment_secret = graphene.Field(EnvironmentSecretType) + environment_token = graphene.Field(EnvironmentTokenType) @classmethod def mutate(cls, root, info, env_id, name, identity_key, token, wrapped_key_share): user = info.context.user if user_can_access_environment(user.userId, env_id): - - env = Environment.objects.get(id=env_id) - org_member = OrganisationMember.objects.get(organisation=env.app.organisation, user_id=user.userId) - environment_secret = EnvironmentSecret.objects.create(environment_id=env_id, user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + env = Environment.objects.get(id=env_id) + org_member = OrganisationMember.objects.get( + organisation=env.app.organisation, user_id=user.userId) + + environment_token = EnvironmentToken.objects.create( + environment_id=env_id, user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + + return CreateEnvironmentTokenMutation(environment_token=environment_token) + - return CreateEnvironmentSecretMutation(environment_secret=environment_secret) - class CreateSecretFolderMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) env_id = graphene.ID(required=True) parent_folder_id = graphene.ID(required=False) name = graphene.String(required=True) - + folder = graphene.Field(SecretFolderType) @classmethod def mutate(cls, root, info, id, env_id, name, parent_folder_id=None): user = info.context.user if user_can_access_environment(user.id, env_id): - folder = SecretFolder.objects.create(id=id, environment_id=env_id, parent_id=parent_folder_id, name=name) + folder = SecretFolder.objects.create( + id=id, environment_id=env_id, parent_id=parent_folder_id, name=name) return CreateSecretFolderMutation(folder=folder) + class CreateSecretTagMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) @@ -119,17 +131,19 @@ class Arguments: def mutate(cls, root, info, id, org_id, name): if not user_is_org_member(info.context.user.userId, org_id): - raise GraphQLError("You don't have permission to perform this action") - + raise GraphQLError( + "You don't have permission to perform this action") + org = Organisation.objects.get(id=org_id) - + if SecretTag.objects.filter(organisation=org, name=name).exists(): raise GraphQLError('This tag already exists!') - + tag = SecretTag.objects.create(id=id, organisation=org, name=name) return CreateSecretTagMutation(tag=tag) - + + class CreateSecretMutation(graphene.Mutation): class Arguments: env_id = graphene.ID(required=True) @@ -147,8 +161,9 @@ def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags env = Environment.objects.get(id=env_id) org = env.app.organisation if not user_is_org_member(info.context.user.userId, org.id): - raise GraphQLError("You don't have permission to perform this action") - + raise GraphQLError( + "You don't have permission to perform this action") + tag_names = SecretTag.objects.filter(id__in=tags).values('name') secret_data = { @@ -161,15 +176,18 @@ def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags 'tags': [], 'comment': comment } - + secret = Secret.objects.create(**secret_data) - org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) + org_member = OrganisationMember.objects.get( + user=info.context.user, organisation=org) - SecretEvent.objects.create(**{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + SecretEvent.objects.create( + **{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) return CreateSecretMutation(secret=secret) - + + class EditSecretMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) @@ -188,10 +206,11 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment env = secret.environment org = env.app.organisation if not user_is_org_member(info.context.user.userId, org.id): - raise GraphQLError("You don't have permission to perform this action") - + raise GraphQLError( + "You don't have permission to perform this action") + tag_names = SecretTag.objects.filter(id__in=tags).values('name') - + secret_data = { 'environment_id': env.id, 'folder_id': folder_id, @@ -204,17 +223,20 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment } for key, value in secret_data.items(): - setattr(secret, key, value) - + setattr(secret, key, value) + secret.updated_at = timezone.now() secret.save() - org_member = OrganisationMember.objects.get(user=info.context.user, organisation=org) + org_member = OrganisationMember.objects.get( + user=info.context.user, organisation=org) + + SecretEvent.objects.create( + **{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) - SecretEvent.objects.create(**{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) - return EditSecretMutation(secret=secret) - + + class DeleteSecretMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) @@ -227,18 +249,19 @@ def mutate(cls, root, info, id): env = secret.environment org = env.app.organisation if not user_is_org_member(info.context.user.userId, org.id): - raise GraphQLError("You don't have permission to perform this action") - + raise GraphQLError( + "You don't have permission to perform this action") + secret.updated_at = timezone.now() secret.deleted_at = timezone.now() secret.save() - most_recent_event = SecretEvent.objects.filter(secret=secret).order_by('version').last() - + most_recent_event = SecretEvent.objects.filter( + secret=secret).order_by('version').last() + # setting the pk to None and then saving it creates a copy of the instance with updated fields most_recent_event.id = None - most_recent_event.event_type=SecretEvent.DELETE + most_recent_event.event_type = SecretEvent.DELETE most_recent_event.save() return DeleteSecretMutation(secret=secret) - diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 2411863c6..54a275e70 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -2,7 +2,7 @@ from enum import Enum from graphene import ObjectType, relay from graphene_django import DjangoObjectType -from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentSecret, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag +from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag from logs.dynamodb_models import KMSLog @@ -11,10 +11,13 @@ class Meta: model = Organisation fields = ('id', 'name', 'identity_key', 'created_at', 'plan') + class OrganisationMemberType(DjangoObjectType): class Meta: model = OrganisationMember - fields = ('id', 'role', 'identity_key', 'wrapped_keyring', 'created_at', 'updated_at') + fields = ('id', 'role', 'identity_key', + 'wrapped_keyring', 'created_at', 'updated_at') + class AppType(DjangoObjectType): class Meta: @@ -22,48 +25,61 @@ class Meta: fields = ('id', 'name', 'identity_key', 'wrapped_key_share', 'created_at', 'app_token', 'app_seed', 'app_version') + class EnvironmentType(DjangoObjectType): class Meta: model = Environment - fields = ('id', 'name', 'env_type', 'identity_key', 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') + fields = ('id', 'name', 'env_type', 'identity_key', + 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') + class EnvironmentKeyType(DjangoObjectType): class Meta: model = EnvironmentKey - fields = ('id','identity_key', 'wrapped_seed', 'wrapped_salt', 'created_at', 'updated_at') + fields = ('id', 'identity_key', 'wrapped_seed', + 'wrapped_salt', 'created_at', 'updated_at') + -class EnvironmentSecretType(DjangoObjectType): +class EnvironmentTokenType(DjangoObjectType): class Meta: - model = EnvironmentSecret - fields = ('id', 'name', 'identity_key', 'token', 'wrapped_key_share', 'created_at', 'updated_at') + model = EnvironmentToken + fields = ('id', 'name', 'identity_key', 'token', + 'wrapped_key_share', 'created_at', 'updated_at') + class SecretFolderType(DjangoObjectType): class Meta: model = SecretFolder - fields = ('id', 'environment_id', 'parent_folder_id', 'name', 'created_at', 'updated_at') + fields = ('id', 'environment_id', 'parent_folder_id', + 'name', 'created_at', 'updated_at') + class SecretTagType(DjangoObjectType): class Meta: model = SecretTag fields = ('id', 'name') + class SecretEventType(DjangoObjectType): class Meta: model = SecretEvent - fields = ('id', 'secret', 'collection', 'key', 'value', 'version', 'tags', 'comment', 'event_type', 'timestamp') + fields = ('id', 'secret', 'collection', 'key', 'value', + 'version', 'tags', 'comment', 'event_type', 'timestamp') + class SecretType(DjangoObjectType): - + history = graphene.List(SecretEventType) - + class Meta: model = Secret - fields = ('id', 'key', 'value', 'folder', 'version', 'tags', 'comment', 'created_at', 'updated_at', 'history') - #interfaces = (relay.Node, ) + fields = ('id', 'key', 'value', 'folder', 'version', 'tags', + 'comment', 'created_at', 'updated_at', 'history') + # interfaces = (relay.Node, ) def resolve_history(self, info): return SecretEvent.objects.filter(secret_id=self.id).order_by('version') - + class KMSLogType(ObjectType): class Meta: diff --git a/backend/backend/schema.py b/backend/backend/schema.py index e54684240..8058be724 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,11 +1,11 @@ -from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentSecretMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, DeleteSecretMutation, EditSecretMutation +from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, DeleteSecretMutation, EditSecretMutation from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation from .graphene.mutations.organisation import CreateOrganisationMutation -from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentSecretType, EnvironmentType, KMSLogType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange +from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, KMSLogType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange import graphene from graphql import GraphQLError -from api.models import Environment, EnvironmentKey, EnvironmentSecret, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag +from api.models import Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag from logs.queries import get_app_log_count, get_app_log_count_range, get_app_logs from datetime import datetime, timedelta from django.conf import settings @@ -38,8 +38,8 @@ class Query(graphene.ObjectType): secret_tags = graphene.List(SecretTagType, org_id=graphene.ID()) environment_keys = graphene.List( EnvironmentKeyType, environment_id=graphene.ID()) - environment_secrets = graphene.List( - EnvironmentSecretType, environment_id=graphene.ID()) + environment_tokens = graphene.List( + EnvironmentTokenType, environment_id=graphene.ID()) def resolve_organisations(root, info): memberships = OrganisationMember.objects.filter(user=info.context.user) @@ -117,14 +117,14 @@ def resolve_environment_keys(root, info, environment_id): user=info.context.user, organisation=env.app.organisation) return EnvironmentKey.objects.filter(environment=env, user=org_member) - def resolve_environment_secrets(root, info, environment_id): + def resolve_environment_tokens(root, info, environment_id): if not user_can_access_environment(info.context.user.userId, environment_id): raise GraphQLError("You don't have access to this secret") env = Environment.objects.get(id=environment_id) org_member = OrganisationMember.objects.get( user=info.context.user, organisation=env.app.organisation) - return EnvironmentSecret.objects.filter(environment=env, user=org_member) + return EnvironmentToken.objects.filter(environment=env, user=org_member) def resolve_logs(root, info, app_id, start=0, end=0): if not user_can_access_app(info.context.user.userId, app_id): @@ -246,7 +246,7 @@ class Mutation(graphene.ObjectType): delete_app = DeleteAppMutation.Field() create_environment = CreateEnvironmentMutation.Field() create_environment_key = CreateEnvironmentKeyMutation.Field() - create_environment_secret = CreateEnvironmentSecretMutation.Field() + create_environment_token = CreateEnvironmentTokenMutation.Field() create_secret_folder = CreateSecretFolderMutation.Field() create_secret_tag = CreateSecretTagMutation.Field() create_secret = CreateSecretMutation.Field() From 76fd0506946837e252430b21c425699422f7078e Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:10:26 +0530 Subject: [PATCH 029/161] chore: regenerate types and schema --- frontend/apollo/gql.ts | 2 +- frontend/apollo/graphql.ts | 610 ++++++++++++++++----------------- frontend/apollo/schema.graphql | 10 +- 3 files changed, 299 insertions(+), 323 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index d1e4001f4..da2e7513c 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -2,7 +2,7 @@ import * as types from './graphql'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -const documents: never[] = []; +const documents = []; /** * 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 079dfe42d..6d40b925a 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1,29 +1,29 @@ /* eslint-disable */ -export type Maybe = T | null; -export type InputMaybe = Maybe; -export type Exact = { [K in keyof T]: T[K] }; -export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; -export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type Maybe = T | null +export type InputMaybe = Maybe +export type Exact = { [K in keyof T]: T[K] } +export type MakeOptional = Omit & { [SubKey in K]?: Maybe } +export type MakeMaybe = Omit & { [SubKey in K]: Maybe } /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: string; - String: string; - Boolean: boolean; - Int: number; - Float: number; + ID: string + String: string + Boolean: boolean + Int: number + Float: number /** * 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. */ - BigInt: any; + BigInt: any /** * The `DateTime` scalar type represents a DateTime * value as specified by * [iso8601](https://en.wikipedia.org/wiki/ISO_8601). */ - DateTime: any; -}; + DateTime: any +} /** An enumeration. */ export enum ApiEnvironmentEnvTypeChoices { @@ -32,7 +32,7 @@ export enum ApiEnvironmentEnvTypeChoices { /** Production */ Prod = 'PROD', /** Staging */ - Staging = 'STAGING' + Staging = 'STAGING', } /** An enumeration. */ @@ -42,7 +42,7 @@ export enum ApiOrganisationMemberRoleChoices { /** Developer */ Dev = 'DEV', /** Owner */ - Owner = 'OWNER' + Owner = 'OWNER', } /** An enumeration. */ @@ -52,7 +52,7 @@ export enum ApiOrganisationPlanChoices { /** Free */ Fr = 'FR', /** Pro */ - Pr = 'PR' + Pr = 'PR', } /** An enumeration. */ @@ -64,406 +64,382 @@ export enum ApiSecretEventEventTypeChoices { /** Read */ R = 'R', /** Update */ - U = 'U' + U = 'U', } export type AppType = { - __typename?: 'AppType'; - appSeed: Scalars['String']; - appToken: Scalars['String']; - appVersion: Scalars['Int']; - createdAt?: Maybe; - id: Scalars['String']; - identityKey: Scalars['String']; - name: Scalars['String']; - wrappedKeyShare: Scalars['String']; -}; + __typename?: 'AppType' + appSeed: Scalars['String'] + appToken: Scalars['String'] + appVersion: Scalars['Int'] + createdAt?: Maybe + id: Scalars['String'] + identityKey: Scalars['String'] + name: Scalars['String'] + wrappedKeyShare: Scalars['String'] +} export type ChartDataPointType = { - __typename?: 'ChartDataPointType'; - data?: Maybe; - date?: Maybe; - index?: Maybe; -}; + __typename?: 'ChartDataPointType' + data?: Maybe + date?: Maybe + index?: Maybe +} export type CreateAppMutation = { - __typename?: 'CreateAppMutation'; - app?: Maybe; -}; + __typename?: 'CreateAppMutation' + app?: Maybe +} export type CreateEnvironmentKeyMutation = { - __typename?: 'CreateEnvironmentKeyMutation'; - environmentKey?: Maybe; -}; + __typename?: 'CreateEnvironmentKeyMutation' + environmentKey?: Maybe +} export type CreateEnvironmentMutation = { - __typename?: 'CreateEnvironmentMutation'; - environment?: Maybe; -}; + __typename?: 'CreateEnvironmentMutation' + environment?: Maybe +} -export type CreateEnvironmentSecretMutation = { - __typename?: 'CreateEnvironmentSecretMutation'; - environmentSecret?: Maybe; -}; +export type CreateEnvironmentTokenMutation = { + __typename?: 'CreateEnvironmentTokenMutation' + environmentToken?: Maybe +} export type CreateOrganisationMutation = { - __typename?: 'CreateOrganisationMutation'; - organisation?: Maybe; -}; + __typename?: 'CreateOrganisationMutation' + organisation?: Maybe +} export type CreateSecretFolderMutation = { - __typename?: 'CreateSecretFolderMutation'; - folder?: Maybe; -}; + __typename?: 'CreateSecretFolderMutation' + folder?: Maybe +} export type CreateSecretMutation = { - __typename?: 'CreateSecretMutation'; - secret?: Maybe; -}; + __typename?: 'CreateSecretMutation' + secret?: Maybe +} export type CreateSecretTagMutation = { - __typename?: 'CreateSecretTagMutation'; - tag?: Maybe; -}; + __typename?: 'CreateSecretTagMutation' + tag?: Maybe +} export type DeleteAppMutation = { - __typename?: 'DeleteAppMutation'; - app?: Maybe; -}; + __typename?: 'DeleteAppMutation' + app?: Maybe +} export type DeleteSecretMutation = { - __typename?: 'DeleteSecretMutation'; - secret?: Maybe; -}; + __typename?: 'DeleteSecretMutation' + secret?: Maybe +} export type EditSecretMutation = { - __typename?: 'EditSecretMutation'; - secret?: Maybe; -}; + __typename?: 'EditSecretMutation' + secret?: Maybe +} export type EnvironmentKeyType = { - __typename?: 'EnvironmentKeyType'; - createdAt?: Maybe; - id: Scalars['String']; - identityKey: Scalars['String']; - updatedAt: Scalars['DateTime']; - wrappedSalt: Scalars['String']; - wrappedSeed: Scalars['String']; -}; - -export type EnvironmentSecretType = { - __typename?: 'EnvironmentSecretType'; - createdAt?: Maybe; - id: Scalars['String']; - identityKey: Scalars['String']; - name: Scalars['String']; - token: Scalars['String']; - updatedAt: Scalars['DateTime']; - wrappedKeyShare: Scalars['String']; -}; + __typename?: 'EnvironmentKeyType' + createdAt?: Maybe + id: Scalars['String'] + identityKey: Scalars['String'] + updatedAt: Scalars['DateTime'] + wrappedSalt: Scalars['String'] + wrappedSeed: Scalars['String'] +} + +export type EnvironmentTokenType = { + __typename?: 'EnvironmentTokenType' + createdAt?: Maybe + id: Scalars['String'] + identityKey: Scalars['String'] + name: Scalars['String'] + token: Scalars['String'] + updatedAt: Scalars['DateTime'] + wrappedKeyShare: Scalars['String'] +} export type EnvironmentType = { - __typename?: 'EnvironmentType'; - createdAt?: Maybe; - envType: ApiEnvironmentEnvTypeChoices; - id: Scalars['String']; - identityKey: Scalars['String']; - name: Scalars['String']; - updatedAt: Scalars['DateTime']; - wrappedSalt: Scalars['String']; - wrappedSeed: Scalars['String']; -}; + __typename?: 'EnvironmentType' + createdAt?: Maybe + envType: ApiEnvironmentEnvTypeChoices + id: Scalars['String'] + identityKey: Scalars['String'] + name: Scalars['String'] + updatedAt: Scalars['DateTime'] + wrappedSalt: Scalars['String'] + wrappedSeed: Scalars['String'] +} export type KmsLogType = Node & { - __typename?: 'KMSLogType'; - appId?: Maybe; - asn?: Maybe; - city?: Maybe; - country?: Maybe; - edgeLocation?: Maybe; - eventType?: Maybe; - id: Scalars['ID']; - ipAddress?: Maybe; - isp?: Maybe; - latitude?: Maybe; - longitude?: Maybe; - phSize?: Maybe; - phaseNode?: Maybe; - timestamp?: Maybe; -}; + __typename?: 'KMSLogType' + appId?: Maybe + asn?: Maybe + city?: Maybe + country?: Maybe + edgeLocation?: Maybe + eventType?: Maybe + id: Scalars['ID'] + ipAddress?: Maybe + isp?: Maybe + latitude?: Maybe + longitude?: Maybe + phSize?: Maybe + phaseNode?: Maybe + timestamp?: Maybe +} export type Mutation = { - __typename?: 'Mutation'; - createApp?: Maybe; - createEnvironment?: Maybe; - createEnvironmentKey?: Maybe; - createEnvironmentSecret?: Maybe; - createOrganisation?: Maybe; - createSecret?: Maybe; - createSecretFolder?: Maybe; - createSecretTag?: Maybe; - deleteApp?: Maybe; - deleteSecret?: Maybe; - editSecret?: Maybe; - rotateAppKeys?: Maybe; -}; - + __typename?: 'Mutation' + createApp?: Maybe + createEnvironment?: Maybe + createEnvironmentKey?: Maybe + createEnvironmentToken?: Maybe + createOrganisation?: Maybe + createSecret?: Maybe + createSecretFolder?: Maybe + createSecretTag?: Maybe + deleteApp?: Maybe + deleteSecret?: Maybe + editSecret?: Maybe + rotateAppKeys?: Maybe +} export type MutationCreateAppArgs = { - appSeed: Scalars['String']; - appToken: Scalars['String']; - appVersion: Scalars['Int']; - id: Scalars['ID']; - identityKey: Scalars['String']; - name: Scalars['String']; - organisationId: Scalars['ID']; - wrappedKeyShare: Scalars['String']; -}; - + appSeed: Scalars['String'] + appToken: Scalars['String'] + appVersion: Scalars['Int'] + id: Scalars['ID'] + identityKey: Scalars['String'] + name: Scalars['String'] + organisationId: Scalars['ID'] + wrappedKeyShare: Scalars['String'] +} export type MutationCreateEnvironmentArgs = { - appId: Scalars['ID']; - envType: Scalars['String']; - id: Scalars['ID']; - identityKey: Scalars['String']; - name: Scalars['String']; - wrappedSalt: Scalars['String']; - wrappedSeed: Scalars['String']; -}; - + appId: Scalars['ID'] + envType: Scalars['String'] + id: Scalars['ID'] + identityKey: Scalars['String'] + name: Scalars['String'] + wrappedSalt: Scalars['String'] + wrappedSeed: Scalars['String'] +} export type MutationCreateEnvironmentKeyArgs = { - envId: Scalars['ID']; - identityKey: Scalars['String']; - userId: Scalars['ID']; - wrappedSalt: Scalars['String']; - wrappedSeed: Scalars['String']; -}; - - -export type MutationCreateEnvironmentSecretArgs = { - envId: Scalars['ID']; - identityKey: Scalars['String']; - name: Scalars['String']; - token: Scalars['String']; - wrappedKeyShare: Scalars['String']; -}; + envId: Scalars['ID'] + identityKey: Scalars['String'] + userId: Scalars['ID'] + wrappedSalt: Scalars['String'] + wrappedSeed: Scalars['String'] +} +export type MutationCreateEnvironmentTokenArgs = { + envId: Scalars['ID'] + identityKey: Scalars['String'] + name: Scalars['String'] + token: Scalars['String'] + wrappedKeyShare: Scalars['String'] +} export type MutationCreateOrganisationArgs = { - id: Scalars['ID']; - identityKey: Scalars['String']; - name: Scalars['String']; -}; - + id: Scalars['ID'] + identityKey: Scalars['String'] + name: Scalars['String'] +} export type MutationCreateSecretArgs = { - comment?: InputMaybe; - envId: Scalars['ID']; - folderId?: InputMaybe; - key: Scalars['String']; - keyDigest: Scalars['String']; - tags?: InputMaybe>>; - value: Scalars['String']; -}; - + comment?: InputMaybe + envId: Scalars['ID'] + folderId?: InputMaybe + key: Scalars['String'] + keyDigest: Scalars['String'] + tags?: InputMaybe>> + value: Scalars['String'] +} export type MutationCreateSecretFolderArgs = { - envId: Scalars['ID']; - id: Scalars['ID']; - name: Scalars['String']; - parentFolderId?: InputMaybe; -}; - + envId: Scalars['ID'] + id: Scalars['ID'] + name: Scalars['String'] + parentFolderId?: InputMaybe +} export type MutationCreateSecretTagArgs = { - id: Scalars['ID']; - name: Scalars['String']; - orgId: Scalars['ID']; -}; - + id: Scalars['ID'] + name: Scalars['String'] + orgId: Scalars['ID'] +} export type MutationDeleteAppArgs = { - id: Scalars['ID']; -}; - + id: Scalars['ID'] +} export type MutationDeleteSecretArgs = { - id: Scalars['ID']; -}; - + id: Scalars['ID'] +} export type MutationEditSecretArgs = { - comment?: InputMaybe; - folderId?: InputMaybe; - id: Scalars['ID']; - key: Scalars['String']; - keyDigest: Scalars['String']; - tags?: InputMaybe>>; - value: Scalars['String']; -}; - + comment?: InputMaybe + folderId?: InputMaybe + id: Scalars['ID'] + key: Scalars['String'] + keyDigest: Scalars['String'] + tags?: InputMaybe>> + value: Scalars['String'] +} export type MutationRotateAppKeysArgs = { - appToken: Scalars['String']; - id: Scalars['ID']; - wrappedKeyShare: Scalars['String']; -}; + appToken: Scalars['String'] + id: Scalars['ID'] + wrappedKeyShare: Scalars['String'] +} /** An object with an ID */ export type Node = { /** The ID of the object */ - id: Scalars['ID']; -}; + id: Scalars['ID'] +} export type OrganisationMemberType = { - __typename?: 'OrganisationMemberType'; - createdAt?: Maybe; - id: Scalars['String']; - identityKey?: Maybe; - role: ApiOrganisationMemberRoleChoices; - updatedAt: Scalars['DateTime']; - wrappedKeyring: Scalars['String']; -}; + __typename?: 'OrganisationMemberType' + createdAt?: Maybe + id: Scalars['String'] + identityKey?: Maybe + role: ApiOrganisationMemberRoleChoices + updatedAt: Scalars['DateTime'] + wrappedKeyring: Scalars['String'] +} export type OrganisationType = { - __typename?: 'OrganisationType'; - createdAt?: Maybe; - id: Scalars['String']; - identityKey: Scalars['String']; - name: Scalars['String']; - plan: ApiOrganisationPlanChoices; -}; + __typename?: 'OrganisationType' + createdAt?: Maybe + id: Scalars['String'] + identityKey: Scalars['String'] + name: Scalars['String'] + plan: ApiOrganisationPlanChoices +} export type Query = { - __typename?: 'Query'; - appActivityChart?: Maybe>>; - appEnvironments?: Maybe>>; - apps?: Maybe>>; - environmentKeys?: Maybe>>; - environmentSecrets?: Maybe>>; - logs?: Maybe>>; - logsCount?: Maybe; - organisationAdminsAndSelf?: Maybe>>; - organisationMembers?: Maybe>>; - organisations?: Maybe>>; - secretHistory?: Maybe>>; - secretTags?: Maybe>>; - secrets?: Maybe>>; -}; - + __typename?: 'Query' + appActivityChart?: Maybe>> + appEnvironments?: Maybe>> + apps?: Maybe>> + environmentKeys?: Maybe>> + environmentTokens?: Maybe>> + logs?: Maybe>> + logsCount?: Maybe + organisationAdminsAndSelf?: Maybe>> + organisationMembers?: Maybe>> + organisations?: Maybe>> + secretHistory?: Maybe>> + secretTags?: Maybe>> + secrets?: Maybe>> +} export type QueryAppActivityChartArgs = { - appId?: InputMaybe; - period?: InputMaybe; -}; - + appId?: InputMaybe + period?: InputMaybe +} export type QueryAppEnvironmentsArgs = { - appId?: InputMaybe; -}; - + appId?: InputMaybe +} export type QueryAppsArgs = { - appId?: InputMaybe; - organisationId?: InputMaybe; -}; - + appId?: InputMaybe + organisationId?: InputMaybe +} export type QueryEnvironmentKeysArgs = { - environmentId?: InputMaybe; -}; - - -export type QueryEnvironmentSecretsArgs = { - environmentId?: InputMaybe; -}; + environmentId?: InputMaybe +} +export type QueryEnvironmentTokensArgs = { + environmentId?: InputMaybe +} export type QueryLogsArgs = { - appId?: InputMaybe; - end?: InputMaybe; - start?: InputMaybe; -}; - + appId?: InputMaybe + end?: InputMaybe + start?: InputMaybe +} export type QueryLogsCountArgs = { - appId?: InputMaybe; - thisMonth?: InputMaybe; -}; - + appId?: InputMaybe + thisMonth?: InputMaybe +} export type QueryOrganisationAdminsAndSelfArgs = { - organisationId?: InputMaybe; -}; - + organisationId?: InputMaybe +} export type QueryOrganisationMembersArgs = { - organisationId?: InputMaybe; - role?: InputMaybe>>; - userId?: InputMaybe; -}; - + organisationId?: InputMaybe + role?: InputMaybe>> + userId?: InputMaybe +} export type QuerySecretHistoryArgs = { - secretId?: InputMaybe; -}; - + secretId?: InputMaybe +} export type QuerySecretTagsArgs = { - orgId?: InputMaybe; -}; - + orgId?: InputMaybe +} export type QuerySecretsArgs = { - envId?: InputMaybe; -}; + envId?: InputMaybe +} export type RotateAppKeysMutation = { - __typename?: 'RotateAppKeysMutation'; - app?: Maybe; -}; + __typename?: 'RotateAppKeysMutation' + app?: Maybe +} export type SecretEventType = { - __typename?: 'SecretEventType'; - comment: Scalars['String']; - eventType: ApiSecretEventEventTypeChoices; - id: Scalars['String']; - key: Scalars['String']; - secret: SecretType; - tags: Array; - timestamp: Scalars['DateTime']; - value: Scalars['String']; - version: Scalars['Int']; -}; + __typename?: 'SecretEventType' + comment: Scalars['String'] + eventType: ApiSecretEventEventTypeChoices + id: Scalars['String'] + key: Scalars['String'] + secret: SecretType + tags: Array + timestamp: Scalars['DateTime'] + value: Scalars['String'] + version: Scalars['Int'] +} export type SecretFolderType = { - __typename?: 'SecretFolderType'; - createdAt?: Maybe; - id: Scalars['String']; - name: Scalars['String']; - updatedAt: Scalars['DateTime']; -}; + __typename?: 'SecretFolderType' + createdAt?: Maybe + id: Scalars['String'] + name: Scalars['String'] + updatedAt: Scalars['DateTime'] +} export type SecretTagType = { - __typename?: 'SecretTagType'; - id: Scalars['String']; - name: Scalars['String']; -}; + __typename?: 'SecretTagType' + id: Scalars['String'] + name: Scalars['String'] +} export type SecretType = { - __typename?: 'SecretType'; - comment: Scalars['String']; - createdAt?: Maybe; - folder?: Maybe; - history?: Maybe>>; - id: Scalars['String']; - key: Scalars['String']; - tags: Array; - updatedAt: Scalars['DateTime']; - value: Scalars['String']; - version: Scalars['Int']; -}; + __typename?: 'SecretType' + comment: Scalars['String'] + createdAt?: Maybe + folder?: Maybe + history?: Maybe>> + id: Scalars['String'] + key: Scalars['String'] + tags: Array + updatedAt: Scalars['DateTime'] + value: Scalars['String'] + version: Scalars['Int'] +} /** An enumeration. */ export enum TimeRange { @@ -472,5 +448,5 @@ export enum TimeRange { Hour = 'HOUR', Month = 'MONTH', Week = 'WEEK', - Year = 'YEAR' + Year = 'YEAR', } diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index f9f722e2b..33617e6ac 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -11,7 +11,7 @@ type Query { secretHistory(secretId: ID): [SecretEventType] secretTags(orgId: ID): [SecretTagType] environmentKeys(environmentId: ID): [EnvironmentKeyType] - environmentSecrets(environmentId: ID): [EnvironmentSecretType] + environmentTokens(environmentId: ID): [EnvironmentTokenType] } type OrganisationType { @@ -203,7 +203,7 @@ type EnvironmentKeyType { updatedAt: DateTime! } -type EnvironmentSecretType { +type EnvironmentTokenType { id: String! name: String! identityKey: String! @@ -220,7 +220,7 @@ type Mutation { deleteApp(id: ID!): DeleteAppMutation createEnvironment(appId: ID!, envType: String!, id: ID!, identityKey: String!, name: String!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentMutation createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation - createEnvironmentSecret(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentSecretMutation + createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation createSecretTag(id: ID!, name: String!, orgId: ID!): CreateSecretTagMutation createSecret(comment: String, envId: ID!, folderId: ID, key: String!, keyDigest: String!, tags: [String], value: String!): CreateSecretMutation @@ -252,8 +252,8 @@ type CreateEnvironmentKeyMutation { environmentKey: EnvironmentKeyType } -type CreateEnvironmentSecretMutation { - environmentSecret: EnvironmentSecretType +type CreateEnvironmentTokenMutation { + environmentToken: EnvironmentTokenType } type CreateSecretFolderMutation { From 4e875a26d3c2308b5461e6c73b90c0537c0593fa Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:11:03 +0530 Subject: [PATCH 030/161] fix: update queries, mutations and utils for environment tokens --- .../environments/createEnvironmentSecret.gql | 8 ---- .../environments/createEnvironmentToken.gql | 8 ++++ .../queries/secrets/getEnvironmentSecrets.gql | 8 ---- .../queries/secrets/getEnvironmentTokens.gql | 8 ++++ .../app/[team]/apps/[app]/secrets/page.tsx | 37 ++++++++----------- frontend/utils/environments.ts | 2 +- 6 files changed, 33 insertions(+), 38 deletions(-) delete mode 100644 frontend/apollo/mutations/environments/createEnvironmentSecret.gql create mode 100644 frontend/apollo/mutations/environments/createEnvironmentToken.gql delete mode 100644 frontend/apollo/queries/secrets/getEnvironmentSecrets.gql create mode 100644 frontend/apollo/queries/secrets/getEnvironmentTokens.gql diff --git a/frontend/apollo/mutations/environments/createEnvironmentSecret.gql b/frontend/apollo/mutations/environments/createEnvironmentSecret.gql deleted file mode 100644 index 01fac4a65..000000000 --- a/frontend/apollo/mutations/environments/createEnvironmentSecret.gql +++ /dev/null @@ -1,8 +0,0 @@ -mutation CreateEnvironmentSecret($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { - createEnvironmentSecret(envId: $envId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { - environmentSecret { - id - createdAt - } - } -} diff --git a/frontend/apollo/mutations/environments/createEnvironmentToken.gql b/frontend/apollo/mutations/environments/createEnvironmentToken.gql new file mode 100644 index 000000000..2e242fd56 --- /dev/null +++ b/frontend/apollo/mutations/environments/createEnvironmentToken.gql @@ -0,0 +1,8 @@ +mutation CreateEnvironmentToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { + createEnvironmentToken(envId: $envId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { + environmentToken { + id + createdAt + } + } +} diff --git a/frontend/apollo/queries/secrets/getEnvironmentSecrets.gql b/frontend/apollo/queries/secrets/getEnvironmentSecrets.gql deleted file mode 100644 index 37bf50e01..000000000 --- a/frontend/apollo/queries/secrets/getEnvironmentSecrets.gql +++ /dev/null @@ -1,8 +0,0 @@ -query GetEnvironmentSecrets($envId: ID!) { - environmentSecrets(environmentId: $envId) { - id - name - wrappedKeyShare - createdAt - } -} diff --git a/frontend/apollo/queries/secrets/getEnvironmentTokens.gql b/frontend/apollo/queries/secrets/getEnvironmentTokens.gql new file mode 100644 index 000000000..44c31e5f3 --- /dev/null +++ b/frontend/apollo/queries/secrets/getEnvironmentTokens.gql @@ -0,0 +1,8 @@ +query GetEnvironmentTokens($envId: ID!) { + environmentTokens(environmentId: $envId) { + id + name + wrappedKeyShare + createdAt + } +} diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index 3be305b31..308617c45 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -6,26 +6,21 @@ import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' import { GetOrganisationAdminsAndSelf } from '@/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql' import { CreateEnvironment } from '@/apollo/mutations/environments/createEnvironment.gql' import { CreateEnvironmentKey } from '@/apollo/mutations/environments/createEnvironmentKey.gql' -import { CreateEnvironmentSecret } from '@/apollo/mutations/environments/createEnvironmentSecret.gql' -import { GetEnvironmentSecrets } from '@/apollo/queries/secrets/getEnvironmentSecrets.gql' +import { CreateEnvironmentToken } from '@/apollo/mutations/environments/createEnvironmentToken.gql' +import { GetEnvironmentTokens } from '@/apollo/queries/secrets/getEnvironmentTokens.gql' import { CreateSecret } from '@/apollo/mutations/environments/createSecret.gql' import { useLazyQuery, useMutation, useQuery } from '@apollo/client' import { useEffect, useState } from 'react' import { copyToClipBoard } from '@/utils/clipboard' import { toast } from 'react-toastify' import { useSession } from 'next-auth/react' -import { - envKeyring, - generateEnvironmentSecret, - newEnvSalt, - newEnvSeed, -} from '@/utils/environments' +import { envKeyring, generateEnvironmentToken, newEnvSalt, newEnvSeed } from '@/utils/environments' import { Button } from '@/components/common/Button' import { ApiEnvironmentEnvTypeChoices, ApiOrganisationMemberRoleChoices, EnvironmentKeyType, - EnvironmentSecretType, + EnvironmentTokenType, EnvironmentType, OrganisationMemberType, SecretType, @@ -174,13 +169,13 @@ export default function Secrets({ params }: { params: { team: string; app: strin const [envKeys, setEnvKeys] = useState(null) const [envSecret, setEnvSecret] = useState('') const [createSecret, { data, loading, error }] = useMutation(CreateSecret) - const [createEnvironmentSecret] = useMutation(CreateEnvironmentSecret) + const [createEnvironmentToken] = useMutation(CreateEnvironmentToken) const { data: secretsData } = useQuery(GetSecrets, { variables: { envId: environment.id, }, }) - const { data: envSecretsData } = useQuery(GetEnvironmentSecrets, { + const { data: envTokensData } = useQuery(GetEnvironmentTokens, { variables: { envId: environment.id, }, @@ -302,22 +297,22 @@ export default function Secrets({ params }: { params: { team: string; app: strin privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), } - const { pssEnv, mutationPayload } = await generateEnvironmentSecret( + const { pssEnv, mutationPayload } = await generateEnvironmentToken( environment, secretsData.environmentKeys[0], userKxKeys ) - await createEnvironmentSecret({ + await createEnvironmentToken({ variables: mutationPayload, refetchQueries: [ { - query: GetEnvironmentSecrets, + query: GetEnvironmentTokens, variables: { - envId: environment.id - } - } - ] + envId: environment.id, + }, + }, + ], }) setEnvSecret(pssEnv) @@ -362,9 +357,9 @@ export default function Secrets({ params }: { params: { team: string; app: strin
- {envSecretsData?.environmentSecrets.map((envSecret: EnvironmentSecretType) => ( -
- {envSecret.name} | {envSecret.createdAt} + {envTokensData?.environmentTokens.map((envToken: EnvironmentTokenType) => ( +
+ {envToken.name} | {envToken.createdAt}
))} {envSecret} diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index 8ab2fb56f..a5da3977b 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -113,7 +113,7 @@ export const envKeyring = async (envSeed: string): Promise => { return { publicKey: sodium.to_hex(publicKey), privateKey: sodium.to_hex(privateKey) } } -export const generateEnvironmentSecret = async ( +export const generateEnvironmentToken = async ( environment: EnvironmentType, key: EnvironmentKeyType, useKeyring: { publicKey: string; privateKey: string } From e75673b126845f31d5a8eec201c45d1c2eea66ee Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:20:48 +0530 Subject: [PATCH 031/161] feat: add util to generate user token --- frontend/utils/environments.ts | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index a5da3977b..92d6642ce 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -116,15 +116,15 @@ export const envKeyring = async (envSeed: string): Promise => { export const generateEnvironmentToken = async ( environment: EnvironmentType, key: EnvironmentKeyType, - useKeyring: { publicKey: string; privateKey: string } + userKeyring: { publicKey: string; privateKey: string } ) => { const wrapKey = await newEnvWrapKey() const token = await newEnvToken() const envSeed = await decryptAsymmetric( key.wrappedSeed, - useKeyring.privateKey, - useKeyring.publicKey + userKeyring.privateKey, + userKeyring.publicKey ) const envKeys = await envKeyring(envSeed) @@ -134,14 +134,14 @@ export const generateEnvironmentToken = async ( const envSalt = await decryptAsymmetric( key.wrappedSalt, - useKeyring.privateKey, - useKeyring.publicKey + userKeyring.privateKey, + userKeyring.publicKey ) const pssEnv = `pss_env:v1:${token}:${envKeys.publicKey}:${envSalt}:${keyShares[0]}:${wrapKey}` const mutationPayload = { envId: environment.id, - name: 'testSecret', + name: 'testEnvToken', identityKey: environment.identityKey, token, wrappedKeyShare, @@ -152,3 +152,24 @@ export const generateEnvironmentToken = async ( mutationPayload, } } + +export const generateUserToken = async (userKeyring: { publicKey: string; privateKey: string }) => { + const wrapKey = await newEnvWrapKey() + const token = await newEnvToken() + + const keyShares = await splitSecret(userKeyring.privateKey) + const wrappedKeyShare = await cryptoUtils.wrappedKeyShare(keyShares[1], wrapKey) + + const pssUser = `pss_user:v1:${token}:${userKeyring.publicKey}:${keyShares[0]}:${wrapKey}` + const mutationPayload = { + name: 'testUserToken', + identityKey: userKeyring.publicKey, + token, + wrappedKeyShare, + } + + return { + pssUser, + mutationPayload, + } +} From a23c5d1810e4a11df7e67166e33ee952c959e968 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:57:37 +0530 Subject: [PATCH 032/161] feat: add schema, types and resolvers for user tokens --- .../backend/graphene/mutations/environment.py | 28 +++++++++++++++++-- backend/backend/graphene/types.py | 9 +++++- backend/backend/schema.py | 16 +++++++++-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 91466405b..9ab136976 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -2,8 +2,8 @@ from backend.graphene.utils.permissions import member_can_access_org, user_can_access_app, user_can_access_environment, user_is_org_member import graphene from graphql import GraphQLError -from api.models import App, Environment, EnvironmentKey, EnvironmentToken, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag -from backend.graphene.types import EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, SecretFolderType, SecretTagType, SecretType +from api.models import App, Environment, EnvironmentKey, EnvironmentToken, Organisation, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag, UserToken +from backend.graphene.types import EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, SecretFolderType, SecretTagType, SecretType, UserTokenType class CreateEnvironmentMutation(graphene.Mutation): @@ -100,6 +100,30 @@ def mutate(cls, root, info, env_id, name, identity_key, token, wrapped_key_share return CreateEnvironmentTokenMutation(environment_token=environment_token) +class CreateUserTokenMutation(graphene.Mutation): + class Arguments: + org_id = graphene.ID(required=True) + name = graphene.String(required=True) + identity_key = graphene.String(required=True) + token = graphene.String(required=True) + wrapped_key_share = graphene.String(required=True) + + user_token = graphene.Field(UserTokenType) + + @classmethod + def mutate(cls, root, info, org_id, name, identity_key, token, wrapped_key_share): + user = info.context.user + if user_is_org_member(user.userId, org_id): + + org_member = OrganisationMember.objects.get( + organisation_id=org_id, user_id=user.userId) + + user_token = UserToken.objects.create( + user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + + return CreateUserTokenMutation(user_token=user_token) + + class CreateSecretFolderMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 54a275e70..8f2b2a5ef 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -2,7 +2,7 @@ from enum import Enum from graphene import ObjectType, relay from graphene_django import DjangoObjectType -from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag +from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag, UserToken from logs.dynamodb_models import KMSLog @@ -47,6 +47,13 @@ class Meta: 'wrapped_key_share', 'created_at', 'updated_at') +class UserTokenType(DjangoObjectType): + class Meta: + model = UserToken + fields = ('id', 'name', 'identity_key', 'token', + 'wrapped_key_share', 'created_at', 'updated_at') + + class SecretFolderType(DjangoObjectType): class Meta: model = SecretFolder diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 8058be724..c1b20cd37 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,11 +1,11 @@ -from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, DeleteSecretMutation, EditSecretMutation +from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, CreateUserTokenMutation, DeleteSecretMutation, EditSecretMutation from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation from .graphene.mutations.organisation import CreateOrganisationMutation -from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, KMSLogType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange +from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, KMSLogType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, TimeRange, UserTokenType import graphene from graphql import GraphQLError -from api.models import Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag +from api.models import Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag, UserToken from logs.queries import get_app_log_count, get_app_log_count_range, get_app_logs from datetime import datetime, timedelta from django.conf import settings @@ -40,6 +40,7 @@ class Query(graphene.ObjectType): EnvironmentKeyType, environment_id=graphene.ID()) environment_tokens = graphene.List( EnvironmentTokenType, environment_id=graphene.ID()) + user_tokens = graphene.List(UserTokenType, organisation_id=graphene.ID()) def resolve_organisations(root, info): memberships = OrganisationMember.objects.filter(user=info.context.user) @@ -126,6 +127,14 @@ def resolve_environment_tokens(root, info, environment_id): user=info.context.user, organisation=env.app.organisation) return EnvironmentToken.objects.filter(environment=env, user=org_member) + def resolve_user_tokens(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") + + org_member = OrganisationMember.objects.get( + user=info.context.user, organisation_id=organisation_id) + return UserToken.objects.filter(user=org_member) + 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") @@ -247,6 +256,7 @@ class Mutation(graphene.ObjectType): create_environment = CreateEnvironmentMutation.Field() create_environment_key = CreateEnvironmentKeyMutation.Field() create_environment_token = CreateEnvironmentTokenMutation.Field() + create_user_token = CreateUserTokenMutation.Field() create_secret_folder = CreateSecretFolderMutation.Field() create_secret_tag = CreateSecretTagMutation.Field() create_secret = CreateSecretMutation.Field() From 4fb654b5a8c9202436964ac3e8d98de0fb0bc320 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:58:11 +0530 Subject: [PATCH 033/161] chore: regenerate schema.graphql and types --- frontend/apollo/gql.ts | 13 +++++++------ frontend/apollo/graphql.ts | 30 ++++++++++++++++++++++++++++++ frontend/apollo/schema.graphql | 16 ++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index da2e7513c..b04b67717 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -1,8 +1,8 @@ /* eslint-disable */ -import * as types from './graphql'; -import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +import * as types from './graphql' +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core' -const documents = []; +const documents: never[] = [] /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * @@ -15,10 +15,11 @@ const documents = []; * The query argument is unknown! * Please regenerate the types. */ -export function graphql(source: string): unknown; +export function graphql(source: string): unknown export function graphql(source: string) { - return (documents as any)[source] ?? {}; + return (documents as any)[source] ?? {} } -export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; \ No newline at end of file +export type DocumentType> = + TDocumentNode extends DocumentNode ? TType : never diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 6d40b925a..1dea8a235 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -126,6 +126,11 @@ export type CreateSecretTagMutation = { tag?: Maybe } +export type CreateUserTokenMutation = { + __typename?: 'CreateUserTokenMutation' + userToken?: Maybe +} + export type DeleteAppMutation = { __typename?: 'DeleteAppMutation' app?: Maybe @@ -202,6 +207,7 @@ export type Mutation = { createSecret?: Maybe createSecretFolder?: Maybe createSecretTag?: Maybe + createUserToken?: Maybe deleteApp?: Maybe deleteSecret?: Maybe editSecret?: Maybe @@ -274,6 +280,14 @@ export type MutationCreateSecretTagArgs = { orgId: Scalars['ID'] } +export type MutationCreateUserTokenArgs = { + identityKey: Scalars['String'] + name: Scalars['String'] + orgId: Scalars['ID'] + token: Scalars['String'] + wrappedKeyShare: Scalars['String'] +} + export type MutationDeleteAppArgs = { id: Scalars['ID'] } @@ -338,6 +352,7 @@ export type Query = { secretHistory?: Maybe>> secretTags?: Maybe>> secrets?: Maybe>> + userTokens?: Maybe>> } export type QueryAppActivityChartArgs = { @@ -395,6 +410,10 @@ export type QuerySecretsArgs = { envId?: InputMaybe } +export type QueryUserTokensArgs = { + organisationId?: InputMaybe +} + export type RotateAppKeysMutation = { __typename?: 'RotateAppKeysMutation' app?: Maybe @@ -450,3 +469,14 @@ export enum TimeRange { Week = 'WEEK', Year = 'YEAR', } + +export type UserTokenType = { + __typename?: 'UserTokenType' + createdAt?: Maybe + id: Scalars['String'] + identityKey: Scalars['String'] + name: Scalars['String'] + token: Scalars['String'] + updatedAt: Scalars['DateTime'] + wrappedKeyShare: Scalars['String'] +} diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 33617e6ac..e973b221a 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -12,6 +12,7 @@ type Query { secretTags(orgId: ID): [SecretTagType] environmentKeys(environmentId: ID): [EnvironmentKeyType] environmentTokens(environmentId: ID): [EnvironmentTokenType] + userTokens(organisationId: ID): [UserTokenType] } type OrganisationType { @@ -213,6 +214,16 @@ type EnvironmentTokenType { updatedAt: DateTime! } +type UserTokenType { + id: String! + name: String! + identityKey: String! + token: String! + wrappedKeyShare: String! + createdAt: DateTime + updatedAt: DateTime! +} + type Mutation { createOrganisation(id: ID!, identityKey: String!, name: String!): CreateOrganisationMutation createApp(appSeed: String!, appToken: String!, appVersion: Int!, id: ID!, identityKey: String!, name: String!, organisationId: ID!, wrappedKeyShare: String!): CreateAppMutation @@ -221,6 +232,7 @@ type Mutation { createEnvironment(appId: ID!, envType: String!, id: ID!, identityKey: String!, name: String!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentMutation createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation + createUserToken(identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation createSecretTag(id: ID!, name: String!, orgId: ID!): CreateSecretTagMutation createSecret(comment: String, envId: ID!, folderId: ID, key: String!, keyDigest: String!, tags: [String], value: String!): CreateSecretMutation @@ -256,6 +268,10 @@ type CreateEnvironmentTokenMutation { environmentToken: EnvironmentTokenType } +type CreateUserTokenMutation { + userToken: UserTokenType +} + type CreateSecretFolderMutation { folder: SecretFolderType } From 03e99e5dbb801318b83b807ca60642b47d92b5cc Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:58:26 +0530 Subject: [PATCH 034/161] feat: query and mutation for user tokens --- frontend/apollo/mutations/users/createUserToken.gql | 8 ++++++++ frontend/apollo/queries/users/getUserTokens.gql | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 frontend/apollo/mutations/users/createUserToken.gql create mode 100644 frontend/apollo/queries/users/getUserTokens.gql diff --git a/frontend/apollo/mutations/users/createUserToken.gql b/frontend/apollo/mutations/users/createUserToken.gql new file mode 100644 index 000000000..c55f16f14 --- /dev/null +++ b/frontend/apollo/mutations/users/createUserToken.gql @@ -0,0 +1,8 @@ +mutation CreateUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { + createUserToken(orgId: $orgId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { + userToken { + id + createdAt + } + } +} diff --git a/frontend/apollo/queries/users/getUserTokens.gql b/frontend/apollo/queries/users/getUserTokens.gql new file mode 100644 index 000000000..2660bbd7d --- /dev/null +++ b/frontend/apollo/queries/users/getUserTokens.gql @@ -0,0 +1,8 @@ +query GetUserTokens($organisationId: ID!) { + userTokens(organisationId: $organisationId) { + id + name + wrappedKeyShare + createdAt + } +} From c4c6dc681b98b8291e9dd145403fbdbcdbf01f43 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 12 Aug 2023 16:58:41 +0530 Subject: [PATCH 035/161] feat: test user tokens --- .../app/[team]/apps/[app]/secrets/page.tsx | 99 ++++++++++++++----- frontend/utils/environments.ts | 6 +- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index 308617c45..df1ed4f00 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -7,6 +7,8 @@ import { GetOrganisationAdminsAndSelf } from '@/apollo/queries/organisation/getO import { CreateEnvironment } from '@/apollo/mutations/environments/createEnvironment.gql' import { CreateEnvironmentKey } from '@/apollo/mutations/environments/createEnvironmentKey.gql' import { CreateEnvironmentToken } from '@/apollo/mutations/environments/createEnvironmentToken.gql' +import { CreateUserToken } from '@/apollo/mutations/users/createUserToken.gql' +import { GetUserTokens } from '@/apollo/queries/users/getUserTokens.gql' import { GetEnvironmentTokens } from '@/apollo/queries/secrets/getEnvironmentTokens.gql' import { CreateSecret } from '@/apollo/mutations/environments/createSecret.gql' import { useLazyQuery, useMutation, useQuery } from '@apollo/client' @@ -14,7 +16,13 @@ import { useEffect, useState } from 'react' import { copyToClipBoard } from '@/utils/clipboard' import { toast } from 'react-toastify' import { useSession } from 'next-auth/react' -import { envKeyring, generateEnvironmentToken, newEnvSalt, newEnvSeed } from '@/utils/environments' +import { + envKeyring, + generateEnvironmentToken, + generateUserToken, + newEnvSalt, + newEnvSeed, +} from '@/utils/environments' import { Button } from '@/components/common/Button' import { ApiEnvironmentEnvTypeChoices, @@ -24,6 +32,7 @@ import { EnvironmentType, OrganisationMemberType, SecretType, + UserTokenType, } from '@/apollo/graphql' import { decryptAsymmetric, @@ -45,9 +54,11 @@ export default function Secrets({ params }: { params: { team: string; app: strin const { data: orgsData } = useQuery(GetOrganisations) const [getOrgAdmins, { data: orgAdminsData }] = useLazyQuery(GetOrganisationAdminsAndSelf) - + const [getUserTokens, { data: userTokensData }] = useLazyQuery(GetUserTokens) const [createEnvironment, { loading, error }] = useMutation(CreateEnvironment) - const [createEnvironmentKey] = useMutation(CreateEnvironmentKey) + const [createUserToken] = useMutation(CreateUserToken) + + const [userToken, setUserToken] = useState('') const { data: session } = useSession() @@ -59,8 +70,13 @@ export default function Secrets({ params }: { params: { team: string; app: strin organisationId, }, }) + getUserTokens({ + variables: { + organisationId, + }, + }) } - }, [getOrgAdmins, orgsData, params.app]) + }, [getOrgAdmins, getUserTokens, orgsData, params.app]) const setupRequired = data?.appEnvironments.length === 0 ?? true @@ -136,24 +152,39 @@ export default function Secrets({ params }: { params: { team: string; app: strin }, ], }) + } - console.log(result) + const handleCreateNewUserToken = async () => { + const sudoPass = 'testpassword1234' + const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) + const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) + if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' + const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) + if (!decryptedKeyring) throw 'Failed to decrypt keys' + + const userKxKeys = { + publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), + privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), + } - // if (result.data.createEnvironment.environment) { - // const envKeyMutationPayload = { - // envId: result.data.createEnvironment.environment.id, - // ownerId: owner.id, - // wrappedSeed: ownerWrappedEnv.wrappedSeed, - // wrappedSalt: ownerWrappedEnv.wrappedSalt, - // identityKey: keys.publicKey, - // } + const { pssUser, mutationPayload } = await generateUserToken( + orgsData.organisations[0].id, + userKxKeys + ) - // const keyResult = await createEnvironmentKey({ - // variables: envKeyMutationPayload, - // }) + await createUserToken({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetUserTokens, + variables: { + organisationId: orgsData.organisations[0].id, + }, + }, + ], + }) - // console.log(keyResult) - // } + setUserToken(pssUser) } const EnvironmentCard = (props: { environment: EnvironmentType }) => { @@ -167,7 +198,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin const [key, setKey] = useState('') const [value, setValue] = useState('') const [envKeys, setEnvKeys] = useState(null) - const [envSecret, setEnvSecret] = useState('') + const [envToken, setEnvToken] = useState('') const [createSecret, { data, loading, error }] = useMutation(CreateSecret) const [createEnvironmentToken] = useMutation(CreateEnvironmentToken) const { data: secretsData } = useQuery(GetSecrets, { @@ -282,7 +313,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin setValue('') } - const handleCreateNewEnvSecret = async () => { + const handleCreateNewEnvToken = async () => { const sudoPass = 'testpassword1234' const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) @@ -290,8 +321,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) if (!decryptedKeyring) throw 'Failed to decrypt keys' - const wrappedSeed = secretsData.environmentKeys[0].wrappedSeed - const userKxKeys = { publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), @@ -303,6 +332,10 @@ export default function Secrets({ params }: { params: { team: string; app: strin userKxKeys ) + const { pssUser } = await generateUserToken(orgsData.organisations[0].id, userKxKeys) + + console.log('user token', pssUser) + await createEnvironmentToken({ variables: mutationPayload, refetchQueries: [ @@ -315,7 +348,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin ], }) - setEnvSecret(pssEnv) + setEnvToken(pssEnv) } return ( @@ -362,10 +395,10 @@ export default function Secrets({ params }: { params: { team: string; app: strin {envToken.name} | {envToken.createdAt}
))} - {envSecret} + {envToken}
-
@@ -396,6 +429,20 @@ export default function Secrets({ params }: { params: { team: string; app: strin Create new env + +
+ {userTokensData?.userTokens.map((userToken: UserTokenType) => ( +
+ {userToken.name} | {userToken.createdAt} +
+ ))} + {userToken} +
+ +
+
)} diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index 92d6642ce..f6ece76c1 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -153,7 +153,10 @@ export const generateEnvironmentToken = async ( } } -export const generateUserToken = async (userKeyring: { publicKey: string; privateKey: string }) => { +export const generateUserToken = async ( + orgId: string, + userKeyring: { publicKey: string; privateKey: string } +) => { const wrapKey = await newEnvWrapKey() const token = await newEnvToken() @@ -162,6 +165,7 @@ export const generateUserToken = async (userKeyring: { publicKey: string; privat const pssUser = `pss_user:v1:${token}:${userKeyring.publicKey}:${keyShares[0]}:${wrapKey}` const mutationPayload = { + orgId, name: 'testUserToken', identityKey: userKeyring.publicKey, token, From 36ce323b24015e618a1602f1feb227b7374f672e Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 13 Aug 2023 19:39:46 +0530 Subject: [PATCH 036/161] feat: add user token kms api, misc fixes --- backend/api/serializers.py | 49 ++++++++- backend/api/utils.py | 29 ++++- backend/api/views.py | 104 +++++++++++++++--- backend/backend/graphene/utils/permissions.py | 14 ++- backend/backend/urls.py | 3 +- nginx/default.conf | 15 +++ 6 files changed, 180 insertions(+), 34 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index b2d36507d..e4f29b947 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -1,8 +1,8 @@ -from rest_framework.serializers import ModelSerializer -from .models import CustomUser, Organisation, Secret +from rest_framework import serializers +from .models import CustomUser, Environment, EnvironmentKey, Organisation, Secret, UserToken -class CustomUserSerializer(ModelSerializer): +class CustomUserSerializer(serializers.ModelSerializer): class Meta: model = CustomUser fields = [ @@ -22,7 +22,7 @@ def create(self, validated_data): return user -class OrganisationSerializer(ModelSerializer): +class OrganisationSerializer(serializers.ModelSerializer): class Meta: model = Organisation fields = ['id', 'name', 'identity_key', 'created_at'] @@ -30,7 +30,44 @@ class Meta: def create(self, validated_data): return Organisation(**validated_data) -class SecretSerializer(ModelSerializer): + +class SecretSerializer(serializers.ModelSerializer): class Meta: model = Secret - fields = '__all__' \ No newline at end of file + fields = '__all__' + + +class EnvironmentSerializer(serializers.ModelSerializer): + class Meta: + model = Environment + fields = ['id', 'name', 'env_type'] + + +class EnvironmentKeySerializer(serializers.ModelSerializer): + environment = EnvironmentSerializer() + + class Meta: + model = EnvironmentKey + fields = '__all__' + + +class UserTokenSerializer(serializers.ModelSerializer): + environment_keys = EnvironmentKeySerializer(many=True, read_only=True) + + class Meta: + model = UserToken + fields = ['wrapped_key_share', 'environment_keys'] + + def to_representation(self, instance): + representation = super().to_representation(instance) + # Filter environment_keys to include only those associated with the same user + user = instance.user + + if user is not None: + environment_keys = EnvironmentKey.objects.filter(user=user) + for key in environment_keys: + print('env key', key.id, key.environment.id) + serializer = EnvironmentKeySerializer(environment_keys, many=True) + + representation['environment_keys'] = serializer.data + return representation diff --git a/backend/api/utils.py b/backend/api/utils.py index dfc99f4e6..7c6f64638 100644 --- a/backend/api/utils.py +++ b/backend/api/utils.py @@ -1,4 +1,4 @@ -from api.models import EnvironmentToken +from api.models import EnvironmentToken, UserToken def get_client_ip(request): @@ -10,14 +10,31 @@ def get_client_ip(request): return ip -def get_env_from_token(auth_token): - env_token = auth_token.split("Bearer ")[1] +def get_token_type(auth_token): + return auth_token.split(" ")[1] - if not env_token: + +def get_env_from_service_token(auth_token): + token = auth_token.split(" ")[2] + + if not token: + return False + + try: + env_token = EnvironmentToken.objects.get(token=token) + return env_token.environment, env_token.user + except Exception as ex: + return False + + +def get_org_member_from_user_token(auth_token): + token = auth_token.split(" ")[2] + + if not token: return False try: - env_secret = EnvironmentToken.objects.get(token=env_token) - return env_secret.environment, env_secret.user + user_token = UserToken.objects.get(token=token) + return user_token.user except Exception as ex: return False diff --git a/backend/api/views.py b/backend/api/views.py index ea374453f..86ad5e113 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -1,7 +1,8 @@ from datetime import datetime import json -from api.serializers import SecretSerializer +from api.serializers import EnvironmentKeySerializer, SecretSerializer, UserTokenSerializer +from backend.graphene.utils.permissions import user_can_access_environment from dj_rest_auth.registration.views import SocialLoginView from django.contrib.auth.mixins import LoginRequiredMixin from graphene_django.views import GraphQLView @@ -10,9 +11,9 @@ from rest_framework.permissions import AllowAny from rest_framework.response import Response from django.http import JsonResponse, HttpResponse -from api.utils import get_client_ip, get_env_from_token +from api.utils import get_client_ip, get_env_from_service_token, get_org_member_from_user_token, get_token_type from logs.models import KMSDBLog -from .models import App, EnvironmentToken, Secret, SecretEvent, SecretTag +from .models import App, Environment, EnvironmentKey, EnvironmentToken, Secret, SecretEvent, SecretTag, UserToken import jwt import requests from django.contrib.auth import logout @@ -213,6 +214,24 @@ def kms(request, app_id): return HttpResponse(status=404) +@api_view(['GET']) +@permission_classes([AllowAny]) +def user_token_kms(request): + auth_token = request.headers['authorization'] + token_type = get_token_type(auth_token) + + if token_type != 'User': + return HttpResponse(status=403) + + token = auth_token.split(' ')[2] + + user_token = UserToken.objects.get(token=token) + + serializer = UserTokenSerializer(user_token) + + return Response(serializer.data, status=status.HTTP_200_OK) + + class PrivateGraphQLView(LoginRequiredMixin, GraphQLView): raise_exception = True pass @@ -227,7 +246,22 @@ def dispatch(self, request, *args): def get(self, request): auth_token = request.headers['authorization'] - env, user = get_env_from_token(auth_token) + token_type = get_token_type(auth_token) + + if token_type == 'Environment': + env, org_member = get_env_from_service_token(auth_token) + elif token_type == 'User': + try: + env_id = request.headers['environment'] + + env = Environment.objects.get(id=env_id) + org_member = get_org_member_from_user_token(auth_token) + + if not user_can_access_environment(org_member.user.userId, env_id): + return HttpResponse(status=403) + except Exception as ex: + print('EX:', ex) + return HttpResponse(status=404) if not env.id: return HttpResponse(status=404) @@ -251,7 +285,20 @@ def get(self, request): def post(self, request): auth_token = request.headers['authorization'] - env, user = get_env_from_token(auth_token) + token_type = get_token_type(auth_token) + if token_type == 'Environment': + env, user = get_env_from_service_token(auth_token) + elif token_type == 'User': + try: + env_id = request.headers['environment'] + env = Environment.objects.get(id=env_id) + org_member = get_org_member_from_user_token(auth_token) + + if not user_can_access_environment(org_member.user.userId, env_id): + return HttpResponse(status=403) + except: + return HttpResponse(status=404) + if not env: return HttpResponse(status=404) @@ -281,9 +328,19 @@ def post(self, request): def put(self, request): auth_token = request.headers['authorization'] - env, user = get_env_from_token(auth_token) - if not env: - return HttpResponse(status=404) + token_type = get_token_type(auth_token) + if token_type == 'Environment': + env, user = get_env_from_service_token(auth_token) + elif token_type == 'User': + try: + env_id = request.headers['environment'] + env = Environment.objects.get(id=env_id) + org_member = get_org_member_from_user_token(auth_token) + + if not user_can_access_environment(org_member.user.userId, env_id): + return HttpResponse(status=403) + except: + return HttpResponse(status=404) request_body = json.loads(request.body) @@ -316,17 +373,32 @@ def put(self, request): def delete(self, request): auth_token = request.headers['authorization'] - env, user = get_env_from_token(auth_token) - if not env: - return HttpResponse(status=404) + token_type = get_token_type(auth_token) + if token_type == 'Environment': + env, user = get_env_from_service_token(auth_token) + elif token_type == 'User': + try: + env_id = request.headers['environment'] + org_member = get_org_member_from_user_token(auth_token) + + if not user_can_access_environment(org_member.user.userId, env_id): + return HttpResponse(status=403) + except: + return HttpResponse(status=404) request_body = json.loads(request.body) - for secret in request_body['secrets']: - secret_obj = Secret.objects.get(id=secret['id']) - secret_obj.updated_at = timezone.now() - secret_obj.deleted_at = timezone.now() - secret_obj.save() + secrets_to_delete = Secret.objects.filter( + id__in=request_body['secrets']) + + for secret in secrets_to_delete: + if not user_can_access_environment(user.id, secret.environment.id): + return HttpResponse(status=403) + + for secret in secrets_to_delete: + secret.updated_at = timezone.now() + secret.deleted_at = timezone.now() + secret.save() most_recent_event = SecretEvent.objects.filter( secret=secret).order_by('version').last() diff --git a/backend/backend/graphene/utils/permissions.py b/backend/backend/graphene/utils/permissions.py index 431f2f927..e520eedec 100644 --- a/backend/backend/graphene/utils/permissions.py +++ b/backend/backend/graphene/utils/permissions.py @@ -1,26 +1,30 @@ from api.models import App, Environment, EnvironmentKey, Organisation, OrganisationMember -from graphql import GraphQLError admin_roles = [OrganisationMember.OWNER, OrganisationMember.ADMIN] + def user_is_admin(user_id, org_id): - - member = OrganisationMember.objects.get(user_id=user_id, organisation_id=org_id) + member = OrganisationMember.objects.get( + user_id=user_id, organisation_id=org_id) return member.role in admin_roles + def user_is_org_member(user_id, org_id): return OrganisationMember.objects.filter(user_id=user_id, organisation_id=org_id).exists() + def user_can_access_app(user_id, app_id): org_memberships = OrganisationMember.objects.filter(user_id=user_id) app = App.objects.get(id=app_id) return app.organisation.id in [membership.organisation.id for membership in org_memberships] + def user_can_access_environment(user_id, env_id): env = Environment.objects.get(id=env_id) - org_member = OrganisationMember.objects.get(organisation=env.app.organisation, user_id=user_id) + org_member = OrganisationMember.objects.get( + organisation=env.app.organisation, user_id=user_id) return EnvironmentKey.objects.filter(user_id=org_member, 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).exists() - \ No newline at end of file diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 2d9e465a2..cf8576bac 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -3,7 +3,7 @@ from django.conf import settings from graphene_django.views import GraphQLView from django.views.decorators.csrf import csrf_exempt -from api.views import PrivateGraphQLView, logout_view, health_check, kms, SecretsView +from api.views import PrivateGraphQLView, logout_view, health_check, kms, SecretsView, user_token_kms CLOUD_HOSTED = settings.APP_HOST == 'cloud' @@ -15,6 +15,7 @@ path('graphql/', csrf_exempt(PrivateGraphQLView.as_view(graphiql=True))), path('493c5048-99f9-4eac-ad0d-98c3740b491f/health', health_check), path('secrets/', SecretsView.as_view()), + path('tokens/user/', user_token_kms) ] if not CLOUD_HOSTED: diff --git a/nginx/default.conf b/nginx/default.conf index af66f2cdf..fb1976f03 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -50,6 +50,21 @@ server { proxy_cookie_path / "/; HttpOnly; SameSite=strict"; } + location /tokens/user/ { + rewrite ^/tokens/user/(.*) /tokens/user/$1 break; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://backend:8000; + proxy_redirect off; + + proxy_cookie_path / "/; HttpOnly; SameSite=strict"; + } + location / { include /etc/nginx/mime.types; From bddb6da0ec757159ef3178b880a7f32481703422 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Wed, 16 Aug 2023 20:22:44 +0530 Subject: [PATCH 037/161] fix: add app context to env keys in token auth --- backend/api/serializers.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index e4f29b947..aaa1014d8 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -2,6 +2,13 @@ from .models import CustomUser, Environment, EnvironmentKey, Organisation, Secret, UserToken +def find_index_by_id(dictionaries, target_id): + for index, dictionary in enumerate(dictionaries): + if dictionary.get('id') == target_id: + return index + return -1 + + class CustomUserSerializer(serializers.ModelSerializer): class Meta: model = CustomUser @@ -52,11 +59,11 @@ class Meta: class UserTokenSerializer(serializers.ModelSerializer): - environment_keys = EnvironmentKeySerializer(many=True, read_only=True) + apps = EnvironmentKeySerializer(many=True, read_only=True) class Meta: model = UserToken - fields = ['wrapped_key_share', 'environment_keys'] + fields = ['wrapped_key_share', 'apps'] def to_representation(self, instance): representation = super().to_representation(instance) @@ -65,9 +72,21 @@ def to_representation(self, instance): if user is not None: environment_keys = EnvironmentKey.objects.filter(user=user) + apps = [] for key in environment_keys: - print('env key', key.id, key.environment.id) - serializer = EnvironmentKeySerializer(environment_keys, many=True) - representation['environment_keys'] = serializer.data + serializer = EnvironmentKeySerializer(key) + index = find_index_by_id(apps, key.environment.app.id) + + if index == -1: + + apps.append({ + 'id': key.environment.app.id, + 'name': key.environment.app.name, + 'environment_keys': serializer.data + }) + else: + apps[index]['environment_keys'].append(serializer.data) + representation['apps'] = apps + return representation From 9236cde0eaee9bca75d211030dd418042b91d6b8 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 17 Aug 2023 10:55:22 +0530 Subject: [PATCH 038/161] fix: use salt when hashing keys, encode hashes in hex --- frontend/app/[team]/apps/[app]/secrets/page.tsx | 9 ++++++++- frontend/utils/crypto.ts | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index df1ed4f00..c7b79cf0f 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -191,6 +191,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin type EnvKeyring = { privateKey: string publicKey: string + salt: string } const { environment } = props @@ -240,11 +241,17 @@ export default function Secrets({ params }: { params: { team: string; app: strin userKxKeys.publicKey ) + const salt = await decryptAsymmetric( + secretsData.environmentKeys[0].wrappedSalt, + userKxKeys.privateKey, + userKxKeys.publicKey + ) const { publicKey, privateKey } = await envKeyring(seed) setEnvKeys({ publicKey, privateKey, + salt, }) } @@ -291,7 +298,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin const handleCreateNewSecret = async () => { const encryptedKey = await encryptAsymmetric(key, environment.identityKey) const encryptedValue = await encryptAsymmetric(value, environment.identityKey) - const keyDigest = await digest(key) + const keyDigest = await digest(key, envKeys!.salt) await createSecret({ variables: { diff --git a/frontend/utils/crypto.ts b/frontend/utils/crypto.ts index a74a6b544..2bb43a256 100644 --- a/frontend/utils/crypto.ts +++ b/frontend/utils/crypto.ts @@ -141,10 +141,10 @@ export const getUserKxPrivateKey = async (signingPrivateKey: string) => { ) } -export const digest = async (input: string) => { +export const digest = async (input: string, salt: string) => { await _sodium.ready const sodium = _sodium - const hash = await sodium.crypto_generichash(32, input) - return sodium.to_base64(hash, sodium.base64_variants.ORIGINAL) + const hash = await sodium.crypto_generichash(32, input, salt) + return sodium.to_hex(hash) } From f230fd14927b58c02616ae9c975e5b2e39891556 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 17 Aug 2023 13:54:48 +0530 Subject: [PATCH 039/161] fix: secrets api POST --- backend/api/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 86ad5e113..881cb09b1 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -292,9 +292,9 @@ def post(self, request): try: env_id = request.headers['environment'] env = Environment.objects.get(id=env_id) - org_member = get_org_member_from_user_token(auth_token) + user = get_org_member_from_user_token(auth_token) - if not user_can_access_environment(org_member.user.userId, env_id): + if not user_can_access_environment(user.user.userId, env_id): return HttpResponse(status=403) except: return HttpResponse(status=404) @@ -320,9 +320,9 @@ def post(self, request): 'comment': secret['comment'], } - Secret.objects.create(**secret_data) + secret_obj = Secret.objects.create(**secret_data) SecretEvent.objects.create( - **{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + **{**secret_data, **{'user': user, 'secret': secret_obj, 'event_type': SecretEvent.CREATE}}) return Response(status=status.HTTP_200_OK) From befe4cab1ef6a5ebc3d680030f48c17d50ffa637 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 17 Aug 2023 14:19:29 +0530 Subject: [PATCH 040/161] fix: secrets api DELETE --- backend/api/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 881cb09b1..76097241a 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -267,7 +267,8 @@ def get(self, request): return HttpResponse(status=404) secrets_filter = { - 'environment': env + 'environment': env, + 'deleted_at': None } try: @@ -379,9 +380,9 @@ def delete(self, request): elif token_type == 'User': try: env_id = request.headers['environment'] - org_member = get_org_member_from_user_token(auth_token) + user = get_org_member_from_user_token(auth_token) - if not user_can_access_environment(org_member.user.userId, env_id): + if not user_can_access_environment(user.user.userId, env_id): return HttpResponse(status=403) except: return HttpResponse(status=404) @@ -392,7 +393,7 @@ def delete(self, request): id__in=request_body['secrets']) for secret in secrets_to_delete: - if not user_can_access_environment(user.id, secret.environment.id): + if not user_can_access_environment(user.user.userId, secret.environment.id): return HttpResponse(status=403) for secret in secrets_to_delete: From f07aca0886e0a730864092583cf652967b8bafaa Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 17 Aug 2023 14:20:43 +0530 Subject: [PATCH 041/161] fix: don't return deleted secrets in graphql resolver --- backend/backend/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index c1b20cd37..9079c0569 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -95,7 +95,7 @@ def resolve_secrets(root, info, env_id): if not user_can_access_environment(info.context.user.userId, env_id): raise GraphQLError("You don't have access to this environment") - return Secret.objects.filter(environment_id=env_id) + return Secret.objects.filter(environment_id=env_id, deleted_at=None) def resolve_secret_history(root, info, secret_id): secret = Secret.objects.get(id=secret_id) From 8b3133b7560147cf72652eeec27f120aabda4a1a Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 17 Aug 2023 19:07:28 +0530 Subject: [PATCH 042/161] fix: secrets api PUT --- backend/api/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 76097241a..44caf625b 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -336,9 +336,9 @@ def put(self, request): try: env_id = request.headers['environment'] env = Environment.objects.get(id=env_id) - org_member = get_org_member_from_user_token(auth_token) + user = get_org_member_from_user_token(auth_token) - if not user_can_access_environment(org_member.user.userId, env_id): + if not user_can_access_environment(user.user.userId, env_id): return HttpResponse(status=403) except: return HttpResponse(status=404) @@ -368,7 +368,7 @@ def put(self, request): secret_obj.updated_at = timezone.now() secret_obj.save() SecretEvent.objects.create( - **{**secret_data, **{'user': user, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) + **{**secret_data, **{'user': user, 'secret': secret_obj, 'event_type': SecretEvent.UPDATE}}) return Response(status=status.HTTP_200_OK) From d3d1d99bc1fa7b9cf1537f3a423d8b9432411edd Mon Sep 17 00:00:00 2001 From: Nimish Date: Sat, 19 Aug 2023 18:04:17 +0530 Subject: [PATCH 043/161] feat: modified user token response --- backend/api/serializers.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index aaa1014d8..772aa9a9a 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -60,13 +60,20 @@ class Meta: class UserTokenSerializer(serializers.ModelSerializer): apps = EnvironmentKeySerializer(many=True, read_only=True) - + + # New field 'userId' + userId = serializers.UUIDField(source='user.id', read_only=True) + + # New field 'offline_enabled' with default value False + offline_enabled = serializers.BooleanField(default=False, read_only=True) + class Meta: model = UserToken - fields = ['wrapped_key_share', 'apps'] + fields = ['wrapped_key_share', 'userId', 'offline_enabled', 'apps'] def to_representation(self, instance): representation = super().to_representation(instance) + # Filter environment_keys to include only those associated with the same user user = instance.user @@ -79,14 +86,14 @@ def to_representation(self, instance): index = find_index_by_id(apps, key.environment.app.id) if index == -1: - apps.append({ 'id': key.environment.app.id, 'name': key.environment.app.name, - 'environment_keys': serializer.data + 'environment_keys': [serializer.data] }) else: apps[index]['environment_keys'].append(serializer.data) + representation['apps'] = apps - return representation + return representation \ No newline at end of file From d4b3e95ca33a172bd9594dcdf006c43cd32bc15a Mon Sep 17 00:00:00 2001 From: Nimish Date: Sun, 20 Aug 2023 13:39:18 +0530 Subject: [PATCH 044/161] changed userId to snaky snakes --- backend/api/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 772aa9a9a..b846d2699 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -62,14 +62,14 @@ class UserTokenSerializer(serializers.ModelSerializer): apps = EnvironmentKeySerializer(many=True, read_only=True) # New field 'userId' - userId = serializers.UUIDField(source='user.id', read_only=True) + user_id = serializers.UUIDField(source='user.id', read_only=True) # New field 'offline_enabled' with default value False offline_enabled = serializers.BooleanField(default=False, read_only=True) class Meta: model = UserToken - fields = ['wrapped_key_share', 'userId', 'offline_enabled', 'apps'] + fields = ['wrapped_key_share', 'user_id', 'offline_enabled', 'apps'] def to_representation(self, instance): representation = super().to_representation(instance) From 7c0ab61fd29791b24ab341c4de194d4d73881463 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 20 Aug 2023 17:14:08 +0530 Subject: [PATCH 045/161] refactor: frontend graphql setup --- frontend/apollo/gql.ts | 136 +- frontend/apollo/graphql.ts | 862 +++++--- frontend/app/[team]/apps/[app]/keys/page.tsx | 10 +- frontend/app/[team]/apps/[app]/layout.tsx | 8 +- frontend/app/[team]/apps/[app]/logs/page.tsx | 2 +- frontend/app/[team]/apps/[app]/page.tsx | 6 +- .../app/[team]/apps/[app]/secrets/page.tsx | 230 +- .../app/[team]/apps/[app]/settings/page.tsx | 4 +- frontend/app/[team]/apps/page.tsx | 4 +- frontend/app/[team]/newdevice/page.tsx | 2 +- frontend/app/[team]/page.tsx | 2 +- frontend/app/onboard/page.tsx | 2 +- frontend/app/page.tsx | 2 +- frontend/codegen.ts | 4 +- frontend/components/apps/AppActivityChart.tsx | 2 +- frontend/components/apps/AppCard.tsx | 2 +- frontend/components/apps/AppsHomeCard.tsx | 2 +- frontend/components/apps/DeleteAppDialog.tsx | 6 +- frontend/components/apps/NewAppDialog.tsx | 6 +- frontend/components/layout/Navbar.tsx | 8 +- .../mutations/createApp.gql | 2 +- .../mutations/createOrganisation.gql | 2 +- .../mutations/deleteApp.gql | 2 +- .../environments/createEnvironment.gql | 3 +- .../environments/createEnvironmentKey.gql | 2 +- .../environments/createEnvironmentToken.gql | 2 +- .../mutations/environments/createSecret.gql | 2 +- .../environments/initAppEnvironments.gql | 41 + .../mutations/rotateAppKeys.gql | 2 +- .../mutations/users/createUserToken.gql | 2 +- .../queries/getAppActivityChart.gql | 0 .../queries/getAppDetail.gql | 0 .../queries/getAppLogCount.gql | 0 .../queries/getAppLogs.gql | 0 .../{apollo => graphql}/queries/getApps.gql | 0 .../queries/getOrganisations.gql | 0 .../getOrganisationAdminsAndSelf.gql | 0 .../organisation/getOrganisationMembers.gql | 0 .../queries/secrets/getAppEnvironments.gql | 0 .../queries/secrets/getEnvironmentKey.gql | 2 +- .../queries/secrets/getEnvironmentTokens.gql | 0 .../queries/secrets/getSecrets.gql | 0 .../queries/users/getUserTokens.gql | 0 frontend/package.json | 1 - frontend/yarn.lock | 1846 +++++++++++------ 45 files changed, 2114 insertions(+), 1095 deletions(-) rename frontend/{apollo => graphql}/mutations/createApp.gql (94%) rename frontend/{apollo => graphql}/mutations/createOrganisation.gql (63%) rename frontend/{apollo => graphql}/mutations/deleteApp.gql (58%) rename frontend/{apollo => graphql}/mutations/environments/createEnvironment.gql (60%) rename frontend/{apollo => graphql}/mutations/environments/createEnvironmentKey.gql (60%) rename frontend/{apollo => graphql}/mutations/environments/createEnvironmentToken.gql (59%) rename frontend/{apollo => graphql}/mutations/environments/createSecret.gql (62%) create mode 100644 frontend/graphql/mutations/environments/initAppEnvironments.gql rename frontend/{apollo => graphql}/mutations/rotateAppKeys.gql (58%) rename frontend/{apollo => graphql}/mutations/users/createUserToken.gql (58%) rename frontend/{apollo => graphql}/queries/getAppActivityChart.gql (100%) rename frontend/{apollo => graphql}/queries/getAppDetail.gql (100%) rename frontend/{apollo => graphql}/queries/getAppLogCount.gql (100%) rename frontend/{apollo => graphql}/queries/getAppLogs.gql (100%) rename frontend/{apollo => graphql}/queries/getApps.gql (100%) rename frontend/{apollo => graphql}/queries/getOrganisations.gql (100%) rename frontend/{apollo => graphql}/queries/organisation/getOrganisationAdminsAndSelf.gql (100%) rename frontend/{apollo => graphql}/queries/organisation/getOrganisationMembers.gql (100%) rename frontend/{apollo => graphql}/queries/secrets/getAppEnvironments.gql (100%) rename frontend/{apollo => graphql}/queries/secrets/getEnvironmentKey.gql (69%) rename frontend/{apollo => graphql}/queries/secrets/getEnvironmentTokens.gql (100%) rename frontend/{apollo => graphql}/queries/secrets/getSecrets.gql (100%) rename frontend/{apollo => graphql}/queries/users/getUserTokens.gql (100%) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index b04b67717..3f7dc5234 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -1,8 +1,42 @@ /* eslint-disable */ -import * as types from './graphql' -import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core' +import * as types from './graphql'; +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; + +/** + * Map of all GraphQL operations in the project. + * + * This map has several performance disadvantages: + * 1. It is not tree-shakeable, so it will include all operations in the project. + * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. + * 3. It does not support dead code elimination, so it will add unused operations. + * + * Therefore it is highly recommended to use the babel or swc plugin for production. + */ +const documents = { + "mutation CreateApplication($id: ID!, $organisationId: ID!, $name: String!, $identityKey: String!, $appToken: String!, $appSeed: String!, $wrappedKeyShare: String!, $appVersion: Int!) {\n createApp(\n id: $id\n organisationId: $organisationId\n name: $name\n identityKey: $identityKey\n appToken: $appToken\n appSeed: $appSeed\n wrappedKeyShare: $wrappedKeyShare\n appVersion: $appVersion\n ) {\n app {\n id\n name\n identityKey\n }\n }\n}": types.CreateApplicationDocument, + "mutation CreateOrg($id: ID!, $name: String!, $identityKey: String!) {\n createOrganisation(id: $id, name: $name, identityKey: $identityKey) {\n organisation {\n id\n name\n createdAt\n }\n }\n}": types.CreateOrgDocument, + "mutation DeleteApplication($id: ID!) {\n deleteApp(id: $id) {\n app {\n id\n }\n }\n}": types.DeleteApplicationDocument, + "mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) {\n createEnvironment(\n id: $id\n appId: $appId\n name: $name\n envType: $envType\n identityKey: $identityKey\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n ) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.CreateEnvDocument, + "mutation CreateEnvKey($envId: ID!, $ownerId: ID!, $wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) {\n createEnvironmentKey(\n envId: $envId\n userId: $ownerId\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n identityKey: $identityKey\n ) {\n environmentKey {\n id\n createdAt\n }\n }\n}": types.CreateEnvKeyDocument, + "mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\n }\n }\n}": types.CreateEnvTokenDocument, + "mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}": types.CreateNewSecretDocument, + "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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}": types.CreateNewUserTokenDocument, + "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 }\n}": types.GetAppDetailDocument, + "query GetAppLogCount($appId: ID!, $thisMonth: Boolean) {\n logsCount(appId: $appId, thisMonth: $thisMonth)\n}": types.GetAppLogCountDocument, + "query GetAppLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n logsCount(appId: $appId)\n}": types.GetAppLogsDocument, + "query GetApps($organisationId: ID!, $appId: ID!) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n }\n}": types.GetAppsDocument, + "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n }\n}": types.GetOrganisationsDocument, + "query GetOrganisationAdminsAndSelf($organisationId: ID!) {\n organisationAdminsAndSelf(organisationId: $organisationId) {\n id\n role\n identityKey\n }\n}": types.GetOrganisationAdminsAndSelfDocument, + "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}": types.GetOrganisationMembersDocument, + "query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}": types.GetAppEnvironmentsDocument, + "query GetEnvironmentKey($envId: ID!) {\n environmentKeys(environmentId: $envId) {\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 GetSecrets($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n createdAt\n history {\n id\n key\n value\n timestamp\n eventType\n }\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, + "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetUserTokensDocument, +}; -const documents: never[] = [] /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * @@ -15,11 +49,99 @@ const documents: never[] = [] * The query argument is unknown! * Please regenerate the types. */ -export function graphql(source: string): unknown +export function graphql(source: string): unknown; + +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation CreateApplication($id: ID!, $organisationId: ID!, $name: String!, $identityKey: String!, $appToken: String!, $appSeed: String!, $wrappedKeyShare: String!, $appVersion: Int!) {\n createApp(\n id: $id\n organisationId: $organisationId\n name: $name\n identityKey: $identityKey\n appToken: $appToken\n appSeed: $appSeed\n wrappedKeyShare: $wrappedKeyShare\n appVersion: $appVersion\n ) {\n app {\n id\n name\n identityKey\n }\n }\n}"): (typeof documents)["mutation CreateApplication($id: ID!, $organisationId: ID!, $name: String!, $identityKey: String!, $appToken: String!, $appSeed: String!, $wrappedKeyShare: String!, $appVersion: Int!) {\n createApp(\n id: $id\n organisationId: $organisationId\n name: $name\n identityKey: $identityKey\n appToken: $appToken\n appSeed: $appSeed\n wrappedKeyShare: $wrappedKeyShare\n appVersion: $appVersion\n ) {\n app {\n id\n name\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: "mutation CreateOrg($id: ID!, $name: String!, $identityKey: String!) {\n createOrganisation(id: $id, name: $name, identityKey: $identityKey) {\n organisation {\n id\n name\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateOrg($id: ID!, $name: String!, $identityKey: String!) {\n createOrganisation(id: $id, name: $name, identityKey: $identityKey) {\n organisation {\n id\n name\n createdAt\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 DeleteApplication($id: ID!) {\n deleteApp(id: $id) {\n app {\n id\n }\n }\n}"): (typeof documents)["mutation DeleteApplication($id: ID!) {\n deleteApp(id: $id) {\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 CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) {\n createEnvironment(\n id: $id\n appId: $appId\n name: $name\n envType: $envType\n identityKey: $identityKey\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n ) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"): (typeof documents)["mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) {\n createEnvironment(\n id: $id\n appId: $appId\n name: $name\n envType: $envType\n identityKey: $identityKey\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n ) {\n environment {\n id\n name\n createdAt\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: "mutation CreateEnvKey($envId: ID!, $ownerId: ID!, $wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) {\n createEnvironmentKey(\n envId: $envId\n userId: $ownerId\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n identityKey: $identityKey\n ) {\n environmentKey {\n id\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateEnvKey($envId: ID!, $ownerId: ID!, $wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) {\n createEnvironmentKey(\n envId: $envId\n userId: $ownerId\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n identityKey: $identityKey\n ) {\n environmentKey {\n id\n createdAt\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 CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\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 CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\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 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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\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 GetAppActivityChart($appId: ID!, $period: TimeRange) {\n appActivityChart(appId: $appId, period: $period) {\n index\n date\n data\n }\n}"): (typeof documents)["query GetAppActivityChart($appId: ID!, $period: TimeRange) {\n appActivityChart(appId: $appId, period: $period) {\n index\n date\n data\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 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 }\n}"): (typeof documents)["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 }\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 GetAppLogCount($appId: ID!, $thisMonth: Boolean) {\n logsCount(appId: $appId, thisMonth: $thisMonth)\n}"): (typeof documents)["query GetAppLogCount($appId: ID!, $thisMonth: Boolean) {\n logsCount(appId: $appId, thisMonth: $thisMonth)\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 GetAppLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n logsCount(appId: $appId)\n}"): (typeof documents)["query GetAppLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n logsCount(appId: $appId)\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 GetApps($organisationId: ID!, $appId: ID!) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\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 }\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 GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\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 GetOrganisationAdminsAndSelf($organisationId: ID!) {\n organisationAdminsAndSelf(organisationId: $organisationId) {\n id\n role\n identityKey\n }\n}"): (typeof documents)["query GetOrganisationAdminsAndSelf($organisationId: ID!) {\n organisationAdminsAndSelf(organisationId: $organisationId) {\n id\n role\n identityKey\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 GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}"): (typeof documents)["query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\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 GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}"): (typeof documents)["query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\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 GetEnvironmentKey($envId: ID!) {\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetEnvironmentKey($envId: ID!) {\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 GetEnvironmentTokens($envId: ID!) {\n environmentTokens(environmentId: $envId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}"): (typeof documents)["query GetEnvironmentTokens($envId: ID!) {\n environmentTokens(environmentId: $envId) {\n id\n name\n wrappedKeyShare\n createdAt\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 GetSecrets($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n createdAt\n history {\n id\n key\n value\n timestamp\n eventType\n }\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n createdAt\n history {\n id\n key\n value\n timestamp\n eventType\n }\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}"): (typeof documents)["query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}"]; export function graphql(source: string) { - return (documents as any)[source] ?? {} + return (documents as any)[source] ?? {}; } -export type DocumentType> = - TDocumentNode extends DocumentNode ? TType : never +export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; \ No newline at end of file diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 1dea8a235..e3988fd2c 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -1,29 +1,30 @@ /* eslint-disable */ -export type Maybe = T | null -export type InputMaybe = Maybe -export type Exact = { [K in keyof T]: T[K] } -export type MakeOptional = Omit & { [SubKey in K]?: Maybe } -export type MakeMaybe = Omit & { [SubKey in K]: Maybe } +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: string - String: string - Boolean: boolean - Int: number - Float: number + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; /** * 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. */ - BigInt: any + BigInt: any; /** * The `DateTime` scalar type represents a DateTime * value as specified by * [iso8601](https://en.wikipedia.org/wiki/ISO_8601). */ - DateTime: any -} + DateTime: any; +}; /** An enumeration. */ export enum ApiEnvironmentEnvTypeChoices { @@ -32,7 +33,7 @@ export enum ApiEnvironmentEnvTypeChoices { /** Production */ Prod = 'PROD', /** Staging */ - Staging = 'STAGING', + Staging = 'STAGING' } /** An enumeration. */ @@ -42,7 +43,7 @@ export enum ApiOrganisationMemberRoleChoices { /** Developer */ Dev = 'DEV', /** Owner */ - Owner = 'OWNER', + Owner = 'OWNER' } /** An enumeration. */ @@ -52,7 +53,7 @@ export enum ApiOrganisationPlanChoices { /** Free */ Fr = 'FR', /** Pro */ - Pr = 'PR', + Pr = 'PR' } /** An enumeration. */ @@ -64,401 +65,427 @@ export enum ApiSecretEventEventTypeChoices { /** Read */ R = 'R', /** Update */ - U = 'U', + U = 'U' } export type AppType = { - __typename?: 'AppType' - appSeed: Scalars['String'] - appToken: Scalars['String'] - appVersion: Scalars['Int'] - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - wrappedKeyShare: Scalars['String'] -} + __typename?: 'AppType'; + appSeed: Scalars['String']; + appToken: Scalars['String']; + appVersion: Scalars['Int']; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}; export type ChartDataPointType = { - __typename?: 'ChartDataPointType' - data?: Maybe - date?: Maybe - index?: Maybe -} + __typename?: 'ChartDataPointType'; + data?: Maybe; + date?: Maybe; + index?: Maybe; +}; export type CreateAppMutation = { - __typename?: 'CreateAppMutation' - app?: Maybe -} + __typename?: 'CreateAppMutation'; + app?: Maybe; +}; export type CreateEnvironmentKeyMutation = { - __typename?: 'CreateEnvironmentKeyMutation' - environmentKey?: Maybe -} + __typename?: 'CreateEnvironmentKeyMutation'; + environmentKey?: Maybe; +}; export type CreateEnvironmentMutation = { - __typename?: 'CreateEnvironmentMutation' - environment?: Maybe -} + __typename?: 'CreateEnvironmentMutation'; + environment?: Maybe; +}; export type CreateEnvironmentTokenMutation = { - __typename?: 'CreateEnvironmentTokenMutation' - environmentToken?: Maybe -} + __typename?: 'CreateEnvironmentTokenMutation'; + environmentToken?: Maybe; +}; export type CreateOrganisationMutation = { - __typename?: 'CreateOrganisationMutation' - organisation?: Maybe -} + __typename?: 'CreateOrganisationMutation'; + organisation?: Maybe; +}; export type CreateSecretFolderMutation = { - __typename?: 'CreateSecretFolderMutation' - folder?: Maybe -} + __typename?: 'CreateSecretFolderMutation'; + folder?: Maybe; +}; export type CreateSecretMutation = { - __typename?: 'CreateSecretMutation' - secret?: Maybe -} + __typename?: 'CreateSecretMutation'; + secret?: Maybe; +}; export type CreateSecretTagMutation = { - __typename?: 'CreateSecretTagMutation' - tag?: Maybe -} + __typename?: 'CreateSecretTagMutation'; + tag?: Maybe; +}; export type CreateUserTokenMutation = { - __typename?: 'CreateUserTokenMutation' - userToken?: Maybe -} + __typename?: 'CreateUserTokenMutation'; + userToken?: Maybe; +}; export type DeleteAppMutation = { - __typename?: 'DeleteAppMutation' - app?: Maybe -} + __typename?: 'DeleteAppMutation'; + app?: Maybe; +}; export type DeleteSecretMutation = { - __typename?: 'DeleteSecretMutation' - secret?: Maybe -} + __typename?: 'DeleteSecretMutation'; + secret?: Maybe; +}; export type EditSecretMutation = { - __typename?: 'EditSecretMutation' - secret?: Maybe -} + __typename?: 'EditSecretMutation'; + secret?: Maybe; +}; export type EnvironmentKeyType = { - __typename?: 'EnvironmentKeyType' - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - updatedAt: Scalars['DateTime'] - wrappedSalt: Scalars['String'] - wrappedSeed: Scalars['String'] -} + __typename?: 'EnvironmentKeyType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; export type EnvironmentTokenType = { - __typename?: 'EnvironmentTokenType' - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - token: Scalars['String'] - updatedAt: Scalars['DateTime'] - wrappedKeyShare: Scalars['String'] -} + __typename?: 'EnvironmentTokenType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + token: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedKeyShare: Scalars['String']; +}; export type EnvironmentType = { - __typename?: 'EnvironmentType' - createdAt?: Maybe - envType: ApiEnvironmentEnvTypeChoices - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - updatedAt: Scalars['DateTime'] - wrappedSalt: Scalars['String'] - wrappedSeed: Scalars['String'] -} + __typename?: 'EnvironmentType'; + createdAt?: Maybe; + envType: ApiEnvironmentEnvTypeChoices; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; export type KmsLogType = Node & { - __typename?: 'KMSLogType' - appId?: Maybe - asn?: Maybe - city?: Maybe - country?: Maybe - edgeLocation?: Maybe - eventType?: Maybe - id: Scalars['ID'] - ipAddress?: Maybe - isp?: Maybe - latitude?: Maybe - longitude?: Maybe - phSize?: Maybe - phaseNode?: Maybe - timestamp?: Maybe -} + __typename?: 'KMSLogType'; + appId?: Maybe; + asn?: Maybe; + city?: Maybe; + country?: Maybe; + edgeLocation?: Maybe; + eventType?: Maybe; + id: Scalars['ID']; + ipAddress?: Maybe; + isp?: Maybe; + latitude?: Maybe; + longitude?: Maybe; + phSize?: Maybe; + phaseNode?: Maybe; + timestamp?: Maybe; +}; export type Mutation = { - __typename?: 'Mutation' - createApp?: Maybe - createEnvironment?: Maybe - createEnvironmentKey?: Maybe - createEnvironmentToken?: Maybe - createOrganisation?: Maybe - createSecret?: Maybe - createSecretFolder?: Maybe - createSecretTag?: Maybe - createUserToken?: Maybe - deleteApp?: Maybe - deleteSecret?: Maybe - editSecret?: Maybe - rotateAppKeys?: Maybe -} + __typename?: 'Mutation'; + createApp?: Maybe; + createEnvironment?: Maybe; + createEnvironmentKey?: Maybe; + createEnvironmentToken?: Maybe; + createOrganisation?: Maybe; + createSecret?: Maybe; + createSecretFolder?: Maybe; + createSecretTag?: Maybe; + createUserToken?: Maybe; + deleteApp?: Maybe; + deleteSecret?: Maybe; + editSecret?: Maybe; + rotateAppKeys?: Maybe; +}; + export type MutationCreateAppArgs = { - appSeed: Scalars['String'] - appToken: Scalars['String'] - appVersion: Scalars['Int'] - id: Scalars['ID'] - identityKey: Scalars['String'] - name: Scalars['String'] - organisationId: Scalars['ID'] - wrappedKeyShare: Scalars['String'] -} + appSeed: Scalars['String']; + appToken: Scalars['String']; + appVersion: Scalars['Int']; + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + organisationId: Scalars['ID']; + wrappedKeyShare: Scalars['String']; +}; + export type MutationCreateEnvironmentArgs = { - appId: Scalars['ID'] - envType: Scalars['String'] - id: Scalars['ID'] - identityKey: Scalars['String'] - name: Scalars['String'] - wrappedSalt: Scalars['String'] - wrappedSeed: Scalars['String'] -} + appId: Scalars['ID']; + envType: Scalars['String']; + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; + export type MutationCreateEnvironmentKeyArgs = { - envId: Scalars['ID'] - identityKey: Scalars['String'] - userId: Scalars['ID'] - wrappedSalt: Scalars['String'] - wrappedSeed: Scalars['String'] -} + envId: Scalars['ID']; + identityKey: Scalars['String']; + userId: Scalars['ID']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; + export type MutationCreateEnvironmentTokenArgs = { - envId: Scalars['ID'] - identityKey: Scalars['String'] - name: Scalars['String'] - token: Scalars['String'] - wrappedKeyShare: Scalars['String'] -} + envId: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + token: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}; + export type MutationCreateOrganisationArgs = { - id: Scalars['ID'] - identityKey: Scalars['String'] - name: Scalars['String'] -} + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; +}; + export type MutationCreateSecretArgs = { - comment?: InputMaybe - envId: Scalars['ID'] - folderId?: InputMaybe - key: Scalars['String'] - keyDigest: Scalars['String'] - tags?: InputMaybe>> - value: Scalars['String'] -} + comment?: InputMaybe; + envId: Scalars['ID']; + folderId?: InputMaybe; + key: Scalars['String']; + keyDigest: Scalars['String']; + tags?: InputMaybe>>; + value: Scalars['String']; +}; + export type MutationCreateSecretFolderArgs = { - envId: Scalars['ID'] - id: Scalars['ID'] - name: Scalars['String'] - parentFolderId?: InputMaybe -} + envId: Scalars['ID']; + id: Scalars['ID']; + name: Scalars['String']; + parentFolderId?: InputMaybe; +}; + export type MutationCreateSecretTagArgs = { - id: Scalars['ID'] - name: Scalars['String'] - orgId: Scalars['ID'] -} + id: Scalars['ID']; + name: Scalars['String']; + orgId: Scalars['ID']; +}; + export type MutationCreateUserTokenArgs = { - identityKey: Scalars['String'] - name: Scalars['String'] - orgId: Scalars['ID'] - token: Scalars['String'] - wrappedKeyShare: Scalars['String'] -} + identityKey: Scalars['String']; + name: Scalars['String']; + orgId: Scalars['ID']; + token: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}; + export type MutationDeleteAppArgs = { - id: Scalars['ID'] -} + id: Scalars['ID']; +}; + export type MutationDeleteSecretArgs = { - id: Scalars['ID'] -} + id: Scalars['ID']; +}; + export type MutationEditSecretArgs = { - comment?: InputMaybe - folderId?: InputMaybe - id: Scalars['ID'] - key: Scalars['String'] - keyDigest: Scalars['String'] - tags?: InputMaybe>> - value: Scalars['String'] -} + comment?: InputMaybe; + folderId?: InputMaybe; + id: Scalars['ID']; + key: Scalars['String']; + keyDigest: Scalars['String']; + tags?: InputMaybe>>; + value: Scalars['String']; +}; + export type MutationRotateAppKeysArgs = { - appToken: Scalars['String'] - id: Scalars['ID'] - wrappedKeyShare: Scalars['String'] -} + appToken: Scalars['String']; + id: Scalars['ID']; + wrappedKeyShare: Scalars['String']; +}; /** An object with an ID */ export type Node = { /** The ID of the object */ - id: Scalars['ID'] -} + id: Scalars['ID']; +}; export type OrganisationMemberType = { - __typename?: 'OrganisationMemberType' - createdAt?: Maybe - id: Scalars['String'] - identityKey?: Maybe - role: ApiOrganisationMemberRoleChoices - updatedAt: Scalars['DateTime'] - wrappedKeyring: Scalars['String'] -} + __typename?: 'OrganisationMemberType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey?: Maybe; + role: ApiOrganisationMemberRoleChoices; + updatedAt: Scalars['DateTime']; + wrappedKeyring: Scalars['String']; +}; export type OrganisationType = { - __typename?: 'OrganisationType' - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - plan: ApiOrganisationPlanChoices -} + __typename?: 'OrganisationType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + plan: ApiOrganisationPlanChoices; +}; export type Query = { - __typename?: 'Query' - appActivityChart?: Maybe>> - appEnvironments?: Maybe>> - apps?: Maybe>> - environmentKeys?: Maybe>> - environmentTokens?: Maybe>> - logs?: Maybe>> - logsCount?: Maybe - organisationAdminsAndSelf?: Maybe>> - organisationMembers?: Maybe>> - organisations?: Maybe>> - secretHistory?: Maybe>> - secretTags?: Maybe>> - secrets?: Maybe>> - userTokens?: Maybe>> -} + __typename?: 'Query'; + appActivityChart?: Maybe>>; + appEnvironments?: Maybe>>; + apps?: Maybe>>; + environmentKeys?: Maybe>>; + environmentTokens?: Maybe>>; + logs?: Maybe>>; + logsCount?: Maybe; + organisationAdminsAndSelf?: Maybe>>; + organisationMembers?: Maybe>>; + organisations?: Maybe>>; + secretHistory?: Maybe>>; + secretTags?: Maybe>>; + secrets?: Maybe>>; + userTokens?: Maybe>>; +}; + export type QueryAppActivityChartArgs = { - appId?: InputMaybe - period?: InputMaybe -} + appId?: InputMaybe; + period?: InputMaybe; +}; + export type QueryAppEnvironmentsArgs = { - appId?: InputMaybe -} + appId?: InputMaybe; +}; + export type QueryAppsArgs = { - appId?: InputMaybe - organisationId?: InputMaybe -} + appId?: InputMaybe; + organisationId?: InputMaybe; +}; + export type QueryEnvironmentKeysArgs = { - environmentId?: InputMaybe -} + environmentId?: InputMaybe; +}; + export type QueryEnvironmentTokensArgs = { - environmentId?: InputMaybe -} + environmentId?: InputMaybe; +}; + export type QueryLogsArgs = { - appId?: InputMaybe - end?: InputMaybe - start?: InputMaybe -} + appId?: InputMaybe; + end?: InputMaybe; + start?: InputMaybe; +}; + export type QueryLogsCountArgs = { - appId?: InputMaybe - thisMonth?: InputMaybe -} + appId?: InputMaybe; + thisMonth?: InputMaybe; +}; + export type QueryOrganisationAdminsAndSelfArgs = { - organisationId?: InputMaybe -} + organisationId?: InputMaybe; +}; + export type QueryOrganisationMembersArgs = { - organisationId?: InputMaybe - role?: InputMaybe>> - userId?: InputMaybe -} + organisationId?: InputMaybe; + role?: InputMaybe>>; + userId?: InputMaybe; +}; + export type QuerySecretHistoryArgs = { - secretId?: InputMaybe -} + secretId?: InputMaybe; +}; + export type QuerySecretTagsArgs = { - orgId?: InputMaybe -} + orgId?: InputMaybe; +}; + export type QuerySecretsArgs = { - envId?: InputMaybe -} + envId?: InputMaybe; +}; + export type QueryUserTokensArgs = { - organisationId?: InputMaybe -} + organisationId?: InputMaybe; +}; export type RotateAppKeysMutation = { - __typename?: 'RotateAppKeysMutation' - app?: Maybe -} + __typename?: 'RotateAppKeysMutation'; + app?: Maybe; +}; export type SecretEventType = { - __typename?: 'SecretEventType' - comment: Scalars['String'] - eventType: ApiSecretEventEventTypeChoices - id: Scalars['String'] - key: Scalars['String'] - secret: SecretType - tags: Array - timestamp: Scalars['DateTime'] - value: Scalars['String'] - version: Scalars['Int'] -} + __typename?: 'SecretEventType'; + comment: Scalars['String']; + eventType: ApiSecretEventEventTypeChoices; + id: Scalars['String']; + key: Scalars['String']; + secret: SecretType; + tags: Array; + timestamp: Scalars['DateTime']; + value: Scalars['String']; + version: Scalars['Int']; +}; export type SecretFolderType = { - __typename?: 'SecretFolderType' - createdAt?: Maybe - id: Scalars['String'] - name: Scalars['String'] - updatedAt: Scalars['DateTime'] -} + __typename?: 'SecretFolderType'; + createdAt?: Maybe; + id: Scalars['String']; + name: Scalars['String']; + updatedAt: Scalars['DateTime']; +}; export type SecretTagType = { - __typename?: 'SecretTagType' - id: Scalars['String'] - name: Scalars['String'] -} + __typename?: 'SecretTagType'; + id: Scalars['String']; + name: Scalars['String']; +}; export type SecretType = { - __typename?: 'SecretType' - comment: Scalars['String'] - createdAt?: Maybe - folder?: Maybe - history?: Maybe>> - id: Scalars['String'] - key: Scalars['String'] - tags: Array - updatedAt: Scalars['DateTime'] - value: Scalars['String'] - version: Scalars['Int'] -} + __typename?: 'SecretType'; + comment: Scalars['String']; + createdAt?: Maybe; + folder?: Maybe; + history?: Maybe>>; + id: Scalars['String']; + key: Scalars['String']; + tags: Array; + updatedAt: Scalars['DateTime']; + value: Scalars['String']; + version: Scalars['Int']; +}; /** An enumeration. */ export enum TimeRange { @@ -467,16 +494,231 @@ export enum TimeRange { Hour = 'HOUR', Month = 'MONTH', Week = 'WEEK', - Year = 'YEAR', + Year = 'YEAR' } export type UserTokenType = { - __typename?: 'UserTokenType' - createdAt?: Maybe - id: Scalars['String'] - identityKey: Scalars['String'] - name: Scalars['String'] - token: Scalars['String'] - updatedAt: Scalars['DateTime'] - wrappedKeyShare: Scalars['String'] -} + __typename?: 'UserTokenType'; + createdAt?: Maybe; + id: Scalars['String']; + identityKey: Scalars['String']; + name: Scalars['String']; + token: Scalars['String']; + updatedAt: Scalars['DateTime']; + wrappedKeyShare: Scalars['String']; +}; + +export type CreateApplicationMutationVariables = Exact<{ + id: Scalars['ID']; + organisationId: Scalars['ID']; + name: Scalars['String']; + identityKey: Scalars['String']; + appToken: Scalars['String']; + appSeed: Scalars['String']; + wrappedKeyShare: Scalars['String']; + appVersion: Scalars['Int']; +}>; + + +export type CreateApplicationMutation = { __typename?: 'Mutation', createApp?: { __typename?: 'CreateAppMutation', app?: { __typename?: 'AppType', id: string, name: string, identityKey: string } | null } | null }; + +export type CreateOrgMutationVariables = Exact<{ + id: Scalars['ID']; + name: Scalars['String']; + identityKey: Scalars['String']; +}>; + + +export type CreateOrgMutation = { __typename?: 'Mutation', createOrganisation?: { __typename?: 'CreateOrganisationMutation', organisation?: { __typename?: 'OrganisationType', id: string, name: string, createdAt?: any | null } | null } | null }; + +export type DeleteApplicationMutationVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type DeleteApplicationMutation = { __typename?: 'Mutation', deleteApp?: { __typename?: 'DeleteAppMutation', app?: { __typename?: 'AppType', id: string } | null } | null }; + +export type CreateEnvMutationVariables = Exact<{ + id: Scalars['ID']; + appId: Scalars['ID']; + name: Scalars['String']; + envType: Scalars['String']; + identityKey: Scalars['String']; + wrappedSeed: Scalars['String']; + wrappedSalt: Scalars['String']; +}>; + + +export type CreateEnvMutation = { __typename?: 'Mutation', createEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null }; + +export type CreateEnvKeyMutationVariables = Exact<{ + envId: Scalars['ID']; + ownerId: Scalars['ID']; + wrappedSeed: Scalars['String']; + wrappedSalt: Scalars['String']; + identityKey: Scalars['String']; +}>; + + +export type CreateEnvKeyMutation = { __typename?: 'Mutation', createEnvironmentKey?: { __typename?: 'CreateEnvironmentKeyMutation', environmentKey?: { __typename?: 'EnvironmentKeyType', id: string, createdAt?: any | null } | null } | null }; + +export type CreateEnvTokenMutationVariables = Exact<{ + envId: Scalars['ID']; + name: Scalars['String']; + identityKey: Scalars['String']; + token: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}>; + + +export type CreateEnvTokenMutation = { __typename?: 'Mutation', createEnvironmentToken?: { __typename?: 'CreateEnvironmentTokenMutation', environmentToken?: { __typename?: 'EnvironmentTokenType', id: string, createdAt?: any | null } | null } | null }; + +export type CreateNewSecretMutationVariables = Exact<{ + envId: Scalars['ID']; + key: Scalars['String']; + keyDigest: Scalars['String']; + value: Scalars['String']; +}>; + + +export type CreateNewSecretMutation = { __typename?: 'Mutation', createSecret?: { __typename?: 'CreateSecretMutation', secret?: { __typename?: 'SecretType', id: string, key: string, value: string, createdAt?: any | null } | null } | null }; + +export type RotateAppKeyMutationVariables = Exact<{ + id: Scalars['ID']; + appToken: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}>; + + +export type RotateAppKeyMutation = { __typename?: 'Mutation', rotateAppKeys?: { __typename?: 'RotateAppKeysMutation', app?: { __typename?: 'AppType', id: string } | null } | null }; + +export type CreateNewUserTokenMutationVariables = Exact<{ + orgId: Scalars['ID']; + name: Scalars['String']; + identityKey: Scalars['String']; + token: Scalars['String']; + wrappedKeyShare: Scalars['String']; +}>; + + +export type CreateNewUserTokenMutation = { __typename?: 'Mutation', createUserToken?: { __typename?: 'CreateUserTokenMutation', userToken?: { __typename?: 'UserTokenType', id: string, createdAt?: any | null } | null } | null }; + +export type GetAppActivityChartQueryVariables = Exact<{ + appId: Scalars['ID']; + period?: InputMaybe; +}>; + + +export type GetAppActivityChartQuery = { __typename?: 'Query', appActivityChart?: Array<{ __typename?: 'ChartDataPointType', index?: number | null, date?: any | null, data?: number | null } | null> | null }; + +export type GetAppDetailQueryVariables = Exact<{ + organisationId: Scalars['ID']; + appId: Scalars['ID']; +}>; + + +export type GetAppDetailQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null, appToken: string, appSeed: string, appVersion: number } | null> | null }; + +export type GetAppLogCountQueryVariables = Exact<{ + appId: Scalars['ID']; + thisMonth?: InputMaybe; +}>; + + +export type GetAppLogCountQuery = { __typename?: 'Query', logsCount?: number | null }; + +export type GetAppLogsQueryVariables = Exact<{ + appId: Scalars['ID']; + start?: InputMaybe; + end?: InputMaybe; +}>; + + +export type GetAppLogsQuery = { __typename?: 'Query', logsCount?: number | null, logs?: Array<{ __typename?: 'KMSLogType', id: string, timestamp?: any | null, phaseNode?: string | null, eventType?: string | null, ipAddress?: string | null, country?: string | null, city?: string | null, phSize?: number | null } | null> | null }; + +export type GetAppsQueryVariables = Exact<{ + organisationId: Scalars['ID']; + appId: Scalars['ID']; +}>; + + +export type GetAppsQuery = { __typename?: 'Query', apps?: Array<{ __typename?: 'AppType', id: string, name: string, identityKey: string, createdAt?: any | null } | null> | null }; + +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 } | null> | null }; + +export type GetOrganisationAdminsAndSelfQueryVariables = Exact<{ + organisationId: Scalars['ID']; +}>; + + +export type GetOrganisationAdminsAndSelfQuery = { __typename?: 'Query', organisationAdminsAndSelf?: Array<{ __typename?: 'OrganisationMemberType', id: string, role: ApiOrganisationMemberRoleChoices, identityKey?: string | null } | null> | null }; + +export type GetOrganisationMembersQueryVariables = Exact<{ + organisationId: Scalars['ID']; + role?: InputMaybe> | InputMaybe>; +}>; + + +export type GetOrganisationMembersQuery = { __typename?: 'Query', organisationMembers?: Array<{ __typename?: 'OrganisationMemberType', role: ApiOrganisationMemberRoleChoices, identityKey?: string | null } | null> | null }; + +export type GetAppEnvironmentsQueryVariables = Exact<{ + appId: Scalars['ID']; +}>; + + +export type GetAppEnvironmentsQuery = { __typename?: 'Query', appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string, wrappedSeed: string, wrappedSalt: string, createdAt?: any | null } | null> | null }; + +export type GetEnvironmentKeyQueryVariables = Exact<{ + envId: Scalars['ID']; +}>; + + +export type GetEnvironmentKeyQuery = { __typename?: 'Query', environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; + +export type GetEnvironmentTokensQueryVariables = Exact<{ + envId: Scalars['ID']; +}>; + + +export type GetEnvironmentTokensQuery = { __typename?: 'Query', environmentTokens?: Array<{ __typename?: 'EnvironmentTokenType', id: string, name: string, wrappedKeyShare: string, createdAt?: any | null } | null> | null }; + +export type GetSecretsQueryVariables = Exact<{ + envId: Scalars['ID']; +}>; + + +export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, createdAt?: any | null, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices } | null> | null } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; + +export type GetUserTokensQueryVariables = Exact<{ + organisationId: Scalars['ID']; +}>; + + +export type GetUserTokensQuery = { __typename?: 'Query', userTokens?: Array<{ __typename?: 'UserTokenType', id: string, name: string, wrappedKeyShare: string, createdAt?: any | null } | null> | null }; + + +export const CreateApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateApplication"},"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":"organisationId"}},"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":"appToken"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appSeed"}},"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":"appVersion"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"appToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}}},{"kind":"Argument","name":{"kind":"Name","value":"appSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"appVersion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appVersion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateOrgDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrg"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrganisation"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteApplication"},"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":"deleteApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"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 CreateEnvDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnv"},"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":"appId"}},"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":"envType"}},"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":"wrappedSeed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"envType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envType"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSalt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateEnvKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvKey"},"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":"ownerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentKey"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ownerId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSalt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateEnvTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvToken"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecret"},"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":"key"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"keyDigest"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"value"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"Variable","name":{"kind":"Name","value":"key"}}},{"kind":"Argument","name":{"kind":"Name","value":"keyDigest"},"value":{"kind":"Variable","name":{"kind":"Name","value":"keyDigest"}}},{"kind":"Argument","name":{"kind":"Name","value":"value"},"value":{"kind":"Variable","name":{"kind":"Name","value":"value"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"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":"createdAt"}}]}}]}}]}}]} 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 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"}}}}],"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} 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"}}]}}]}}]} as unknown as DocumentNode; +export const GetAppLogCountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppLogCount"},"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":"thisMonth"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"thisMonth"},"value":{"kind":"Variable","name":{"kind":"Name","value":"thisMonth"}}}]}]}}]} as unknown as DocumentNode; +export const GetAppLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppLogs"},"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":"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":"logsCount"},"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":"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"}}]}}]}}]} 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"}}]}}]}}]} as unknown as DocumentNode; +export const GetOrganisationAdminsAndSelfDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationAdminsAndSelf"},"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":"organisationAdminsAndSelf"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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"}}}}],"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"}}}],"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"}}]}}]}}]} 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"}}}}],"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"}}}],"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 GetSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecrets"},"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":"secrets"},"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":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"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":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}}]}}]}},{"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 GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/app/[team]/apps/[app]/keys/page.tsx b/frontend/app/[team]/apps/[app]/keys/page.tsx index 586ed2a5a..69d073681 100644 --- a/frontend/app/[team]/apps/[app]/keys/page.tsx +++ b/frontend/app/[team]/apps/[app]/keys/page.tsx @@ -1,8 +1,8 @@ 'use client' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' -import { GetAppDetail } from '@/apollo/queries/getAppDetail.gql' -import { RotateAppKeys } from '@/apollo/mutations/rotateAppKeys.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetAppDetail } from '@/graphql/queries/getAppDetail.gql' +import { RotateAppKey } from '@/graphql/mutations/rotateAppKeys.gql' import { useLazyQuery, useQuery, useMutation } from '@apollo/client' import { AppType, ChartDataPointType, TimeRange } from '@/apollo/graphql' import { Fragment, useEffect, useState } from 'react' @@ -62,7 +62,7 @@ export default function App({ params }: { params: { team: string; app: string } const [showPw, setShowPw] = useState(false) const [loading, setLoading] = useState(false) const [isOpen, setIsOpen] = useState(false) - const [rotateAppKeys] = useMutation(RotateAppKeys) + const [rotateAppKeys] = useMutation(RotateAppKey) const closeModal = () => { setPw('') @@ -179,7 +179,7 @@ export default function App({ params }: { params: { team: string; app: string }
Warning: This will revoke your current app keys. Your application - won't be able to decrypt data using the current keys. + won't be able to decrypt data using the current keys.
diff --git a/frontend/app/[team]/apps/[app]/layout.tsx b/frontend/app/[team]/apps/[app]/layout.tsx index fb5f4d7bf..10fcafc1c 100644 --- a/frontend/app/[team]/apps/[app]/layout.tsx +++ b/frontend/app/[team]/apps/[app]/layout.tsx @@ -6,8 +6,8 @@ import clsx from 'clsx' import Link from 'next/link' import { useQuery, useLazyQuery } from '@apollo/client' import { AppType } from '@/apollo/graphql' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' -import { GetAppDetail } from '@/apollo/queries/getAppDetail.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetAppDetail } from '@/graphql/queries/getAppDetail.gql' import { usePathname } from 'next/navigation' import { Button } from '@/components/common/Button' import { FaCopy } from 'react-icons/fa' @@ -63,13 +63,13 @@ export default function AppLayout({ }, { name: 'Secrets', - link: 'secrets' + link: 'secrets', }, { name: 'Logs', link: 'logs', }, - + { name: 'Settings', link: 'settings', diff --git a/frontend/app/[team]/apps/[app]/logs/page.tsx b/frontend/app/[team]/apps/[app]/logs/page.tsx index 03ec1a696..0f10f729d 100644 --- a/frontend/app/[team]/apps/[app]/logs/page.tsx +++ b/frontend/app/[team]/apps/[app]/logs/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { GetAppLogs } from '@/apollo/queries/getAppLogs.gql' +import { GetAppLogs } from '@/graphql/queries/getAppLogs.gql' import { useLazyQuery } from '@apollo/client' import { KmsLogType } from '@/apollo/graphql' import { Disclosure, Transition } from '@headlessui/react' diff --git a/frontend/app/[team]/apps/[app]/page.tsx b/frontend/app/[team]/apps/[app]/page.tsx index 6f6c7b484..94d012a44 100644 --- a/frontend/app/[team]/apps/[app]/page.tsx +++ b/frontend/app/[team]/apps/[app]/page.tsx @@ -1,8 +1,8 @@ 'use client' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' -import { GetAppDetail } from '@/apollo/queries/getAppDetail.gql' -import { GetAppLogCount } from '@/apollo/queries/getAppLogCount.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetAppDetail } from '@/graphql/queries/getAppDetail.gql' +import { GetAppLogCount } from '@/graphql/queries/getAppLogCount.gql' import { useLazyQuery, useQuery } from '@apollo/client' import { AppType } from '@/apollo/graphql' import { useEffect } from 'react' diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index c7b79cf0f..a48dd4a02 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -1,22 +1,24 @@ 'use client' -import { GetAppEnvironments } from '@/apollo/queries/secrets/getAppEnvironments.gql' -import { GetSecrets } from '@/apollo/queries/secrets/getSecrets.gql' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' -import { GetOrganisationAdminsAndSelf } from '@/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql' -import { CreateEnvironment } from '@/apollo/mutations/environments/createEnvironment.gql' -import { CreateEnvironmentKey } from '@/apollo/mutations/environments/createEnvironmentKey.gql' -import { CreateEnvironmentToken } from '@/apollo/mutations/environments/createEnvironmentToken.gql' -import { CreateUserToken } from '@/apollo/mutations/users/createUserToken.gql' -import { GetUserTokens } from '@/apollo/queries/users/getUserTokens.gql' -import { GetEnvironmentTokens } from '@/apollo/queries/secrets/getEnvironmentTokens.gql' -import { CreateSecret } from '@/apollo/mutations/environments/createSecret.gql' -import { useLazyQuery, useMutation, useQuery } from '@apollo/client' +import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' +import { GetSecrets } from '@/graphql/queries/secrets/getSecrets.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetOrganisationAdminsAndSelf } from '@/graphql/queries/organisation/getOrganisationAdminsAndSelf.gql' +import { CreateEnv } from '@/graphql/mutations/environments/createEnvironment.gql' +import { InitAppEnvironments } from '@/graphql/mutations/environments/initAppEnvironments.gql' +import { CreateEnvKey } from '@/graphql/mutations/environments/createEnvironmentKey.gql' +import { CreateEnvToken } from '@/graphql/mutations/environments/createEnvironmentToken.gql' +import { CreateNewUserToken } from '@/graphql/mutations/users/createUserToken.gql' +import { GetUserTokens } from '@/graphql/queries/users/getUserTokens.gql' +import { GetEnvironmentTokens } from '@/graphql/queries/secrets/getEnvironmentTokens.gql' +import { CreateSecret } from '@/graphql/mutations/environments/createSecret.gql' +import { gql, useLazyQuery, useMutation, useQuery } from '@apollo/client' import { useEffect, useState } from 'react' import { copyToClipBoard } from '@/utils/clipboard' import { toast } from 'react-toastify' import { useSession } from 'next-auth/react' import { + EnvKeyring, envKeyring, generateEnvironmentToken, generateUserToken, @@ -30,16 +32,19 @@ import { EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, + MutationCreateEnvironmentArgs, OrganisationMemberType, SecretType, UserTokenType, } from '@/apollo/graphql' import { + createSealedBox, decryptAsymmetric, digest, encryptAsymmetric, getUserKxPrivateKey, getUserKxPublicKey, + openSealedBox, } from '@/utils/crypto' import { cryptoUtils } from '@/utils/auth' import { getLocalKeyring } from '@/utils/localStorage' @@ -55,8 +60,9 @@ export default function Secrets({ params }: { params: { team: string; app: strin const [getOrgAdmins, { data: orgAdminsData }] = useLazyQuery(GetOrganisationAdminsAndSelf) const [getUserTokens, { data: userTokensData }] = useLazyQuery(GetUserTokens) - const [createEnvironment, { loading, error }] = useMutation(CreateEnvironment) - const [createUserToken] = useMutation(CreateUserToken) + const [createEnvironment, { loading, error }] = useMutation(CreateEnv) + const [initAppEnvironments] = useMutation(InitAppEnvironments) + const [createUserToken] = useMutation(CreateNewUserToken) const [userToken, setUserToken] = useState('') @@ -88,11 +94,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin const wrappedSeed = await encryptAsymmetric(envSecrets.seed, userPubKey) const wrappedSalt = await encryptAsymmetric(envSecrets.salt, userPubKey) - console.log({ - wrappedSeed, - wrappedSalt, - }) - return { user, wrappedSeed, @@ -100,49 +101,112 @@ export default function Secrets({ params }: { params: { team: string; app: strin } } - const createEnv = async () => { - const seed = await newEnvSeed() - const keys = await envKeyring(seed) - const salt = await newEnvSalt() - const name = 'testEnv' - const envType = ApiEnvironmentEnvTypeChoices.Dev + const initAppEnvs = async () => { + const owner = orgAdminsData.organisationAdminsAndSelf.find( + (user: OrganisationMemberType) => user.role === ApiOrganisationMemberRoleChoices.Owner + ) - const wrappedEnvSecrets = orgAdminsData.organisationAdminsAndSelf.map( - async (user: OrganisationMemberType) => { - const wrappedSeed = await encryptAsymmetric(seed, user.identityKey!) - const wrappedSalt = await encryptAsymmetric(salt, user.identityKey!) + const envs = [ + { + name: 'devEnv', + envType: ApiEnvironmentEnvTypeChoices.Dev, + seed: await newEnvSeed(), + salt: await newEnvSalt(), + keys: {}, + }, + { + name: 'stagingEnv', + envType: ApiEnvironmentEnvTypeChoices.Staging, + seed: await newEnvSeed(), + salt: await newEnvSalt(), + keys: { publicKey: '', privateKey: '' }, + }, + { + name: 'prodEnv', + envType: ApiEnvironmentEnvTypeChoices.Prod, + seed: await newEnvSeed(), + salt: await newEnvSalt(), + keys: { publicKey: '', privateKey: '' }, + }, + ] - console.log({ - wrappedSeed, - wrappedSalt, - }) + let mutationPayload = { + devEnv: {} as MutationCreateEnvironmentArgs, + stagingEnv: {} as MutationCreateEnvironmentArgs, + prodEnv: {} as MutationCreateEnvironmentArgs, + } - return { - user, - wrappedSeed, - wrappedSalt, + envs.forEach(async (env) => { + env.keys = await envKeyring(env.seed) + const { name, envType, seed, salt, keys } = env + + const ownerWrappedEnv = await wrapEnvSecretsForUser({ seed, salt }, owner) + + if (envType === ApiEnvironmentEnvTypeChoices.Dev) { + mutationPayload.devEnv = { + id: crypto.randomUUID(), + appId: params.app, + name, + envType, + wrappedSeed: ownerWrappedEnv.wrappedSeed, + wrappedSalt: ownerWrappedEnv.wrappedSalt, + identityKey: keys.publicKey, + } + } else if (envType === ApiEnvironmentEnvTypeChoices.Staging) { + mutationPayload.stagingEnv = { + id: crypto.randomUUID(), + appId: params.app, + name, + envType, + wrappedSeed: ownerWrappedEnv.wrappedSeed, + wrappedSalt: ownerWrappedEnv.wrappedSalt, + identityKey: keys.publicKey, + } + } else if (envType === ApiEnvironmentEnvTypeChoices.Prod) { + mutationPayload.prodEnv = { + id: crypto.randomUUID(), + appId: params.app, + name, + envType, + wrappedSeed: ownerWrappedEnv.wrappedSeed, + wrappedSalt: ownerWrappedEnv.wrappedSalt, + identityKey: keys.publicKey, } } - ) + }) - const owner = orgAdminsData.organisationAdminsAndSelf.find( - (user: OrganisationMemberType) => user.role === ApiOrganisationMemberRoleChoices.Owner - ) + console.log(mutationPayload.devEnv) + + await createEnvironment({ + variables: mutationPayload.devEnv, + refetchQueries: [ + { + query: GetAppEnvironments, + variables: { + appId: params.app, + }, + }, + ], + }) - const ownerWrappedEnv = await wrapEnvSecretsForUser({ seed, salt }, owner) + console.log(mutationPayload.stagingEnv) - const envMutationPayload = { - id: crypto.randomUUID(), - appId: params.app, - name, - envType, - wrappedSeed: ownerWrappedEnv.wrappedSeed, - wrappedSalt: ownerWrappedEnv.wrappedSalt, - identityKey: keys.publicKey, - } + await createEnvironment({ + variables: mutationPayload.stagingEnv, + refetchQueries: [ + { + query: GetAppEnvironments, + variables: { + appId: params.app, + }, + }, + ], + }) - const result = await createEnvironment({ - variables: envMutationPayload, + console.log(mutationPayload.prodEnv) + + await createEnvironment({ + variables: mutationPayload.prodEnv, refetchQueries: [ { query: GetAppEnvironments, @@ -152,6 +216,54 @@ export default function Secrets({ params }: { params: { team: string; app: strin }, ], }) + + // await Promise.all([ + // createEnvironment({ + // variables: mutationPayload.devEnv, + // refetchQueries: [ + // { + // query: GetAppEnvironments, + // variables: { + // appId: params.app, + // }, + // }, + // ], + // }), + // createEnvironment({ + // variables: mutationPayload.stagingEnv, + // refetchQueries: [ + // { + // query: GetAppEnvironments, + // variables: { + // appId: params.app, + // }, + // }, + // ], + // }), + // createEnvironment({ + // variables: mutationPayload.prodEnv, + // refetchQueries: [ + // { + // query: GetAppEnvironments, + // variables: { + // appId: params.app, + // }, + // }, + // ], + // }), + // ]) + + // const result = await initAppEnvironments({ + // variables: mutationPayload, + // refetchQueries: [ + // { + // query: GetAppEnvironments, + // variables: { + // appId: params.app, + // }, + // }, + // ], + // }) } const handleCreateNewUserToken = async () => { @@ -201,7 +313,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin const [envKeys, setEnvKeys] = useState(null) const [envToken, setEnvToken] = useState('') const [createSecret, { data, loading, error }] = useMutation(CreateSecret) - const [createEnvironmentToken] = useMutation(CreateEnvironmentToken) + const [createEnvironmentToken] = useMutation(CreateEnvToken) const { data: secretsData } = useQuery(GetSecrets, { variables: { envId: environment.id, @@ -274,6 +386,10 @@ export default function Secrets({ params }: { params: { team: string; app: strin envKeys.privateKey, envKeys.publicKey ) + const box = await createSealedBox(decryptedSecret.value, envKeys.publicKey) + console.log('sealedBox', box) + const openBox = await openSealedBox(box, envKeys.publicKey, envKeys.privateKey) + console.log('openBox', openBox) return decryptedSecret }) ) @@ -422,17 +538,17 @@ export default function Secrets({ params }: { params: { team: string; app: strin

{"You don't have any environments for this app yet"}

- ) : ( -
+
{data?.appEnvironments.map((env: EnvironmentType) => ( ))}
-
diff --git a/frontend/app/[team]/apps/[app]/settings/page.tsx b/frontend/app/[team]/apps/[app]/settings/page.tsx index 2473d86f5..968207b16 100644 --- a/frontend/app/[team]/apps/[app]/settings/page.tsx +++ b/frontend/app/[team]/apps/[app]/settings/page.tsx @@ -1,7 +1,7 @@ 'use client' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' -import { GetAppDetail } from '@/apollo/queries/getAppDetail.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetAppDetail } from '@/graphql/queries/getAppDetail.gql' import { useLazyQuery, useQuery } from '@apollo/client' import { AppType } from '@/apollo/graphql' import { useEffect } from 'react' diff --git a/frontend/app/[team]/apps/page.tsx b/frontend/app/[team]/apps/page.tsx index a1fcf10f6..35ca99842 100644 --- a/frontend/app/[team]/apps/page.tsx +++ b/frontend/app/[team]/apps/page.tsx @@ -1,8 +1,8 @@ 'use client' import { useLazyQuery, useQuery } from '@apollo/client' -import { GetApps } from '@/apollo/queries/getApps.gql' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' +import { GetApps } from '@/graphql/queries/getApps.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' import { AppType, OrganisationType } from '@/apollo/graphql' import NewAppDialog from '@/components/apps/NewAppDialog' import { FaPlus } from 'react-icons/fa' diff --git a/frontend/app/[team]/newdevice/page.tsx b/frontend/app/[team]/newdevice/page.tsx index 0a3778bbe..fab4c75ca 100644 --- a/frontend/app/[team]/newdevice/page.tsx +++ b/frontend/app/[team]/newdevice/page.tsx @@ -6,7 +6,7 @@ import { AccountSeedChecker } from '@/components/onboarding/AccountSeedChecker' import { Step, Stepper } from '@/components/onboarding/Stepper' import { useEffect, useState } from 'react' import { MdContentPaste, MdOutlineKey } from 'react-icons/md' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' import { useQuery } from '@apollo/client' import { OrganisationType } from '@/apollo/graphql' import { cryptoUtils } from '@/utils/auth' diff --git a/frontend/app/[team]/page.tsx b/frontend/app/[team]/page.tsx index f4deb2284..6d5180e64 100644 --- a/frontend/app/[team]/page.tsx +++ b/frontend/app/[team]/page.tsx @@ -2,7 +2,7 @@ import AppsHomeCard from '@/components/apps/AppsHomeCard' import { useQuery } from '@apollo/client' -import GetOrganisations from '@/apollo/queries/getOrganisations.gql' +import GetOrganisations from '@/graphql/queries/getOrganisations.gql' import Link from 'next/link' export default function AppsHome({ params }: { params: { team: string } }) { diff --git a/frontend/app/onboard/page.tsx b/frontend/app/onboard/page.tsx index f44441415..71ca32ed8 100644 --- a/frontend/app/onboard/page.tsx +++ b/frontend/app/onboard/page.tsx @@ -21,7 +21,7 @@ import { toast } from 'react-toastify' import { gql, useMutation } from '@apollo/client' import { useRouter } from 'next/navigation' import Link from 'next/link' -import { CreateOrganisation } from '@/apollo/mutations/createOrganisation.gql' +import { CreateOrganisation } from '@/graphql/mutations/createOrganisation.gql' import { setLocalOrg } from '@/utils/localStorage' const bip39 = require('bip39') diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 3f085b6ad..0f23e327e 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -4,7 +4,7 @@ import { useQuery } from '@apollo/client' import { useRouter } from 'next/navigation' import { useEffect } from 'react' import Loading from './loading' -import { GetOrganisations } from '@/apollo/queries/getOrganisations.gql' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' import { OrganisationType } from '@/apollo/graphql' import { getLocalOrgs } from '@/utils/localStorage' diff --git a/frontend/codegen.ts b/frontend/codegen.ts index ee8c981f3..26aee833a 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -3,11 +3,11 @@ import type { CodegenConfig } from '@graphql-codegen/cli' const config: CodegenConfig = { overwrite: true, schema: 'apollo/schema.graphql', - //documents: 'apollo/**/*.graphql', + documents: ['graphql/**/*.gql'], generates: { 'apollo/': { preset: 'client', - plugins: ['typescript'], + plugins: [], }, }, } diff --git a/frontend/components/apps/AppActivityChart.tsx b/frontend/components/apps/AppActivityChart.tsx index d5de9d204..81845680d 100644 --- a/frontend/components/apps/AppActivityChart.tsx +++ b/frontend/components/apps/AppActivityChart.tsx @@ -1,5 +1,5 @@ import { AppType, ChartDataPointType, TimeRange } from '@/apollo/graphql' -import { GetAppActivityChart } from '@/apollo/queries/getAppActivityChart.gql' +import { GetAppActivityChart } from '@/graphql/queries/getAppActivityChart.gql' import { humanReadableNumber } from '@/utils/dataUnits' import { useLazyQuery } from '@apollo/client' import { useEffect, useState } from 'react' diff --git a/frontend/components/apps/AppCard.tsx b/frontend/components/apps/AppCard.tsx index 2c1c39085..af45aaf40 100644 --- a/frontend/components/apps/AppCard.tsx +++ b/frontend/components/apps/AppCard.tsx @@ -2,7 +2,7 @@ import { useQuery } from '@apollo/client' import { FaCube } from 'react-icons/fa' import { Card } from '../common/Card' import Spinner from '../common/Spinner' -import { GetAppActivityChart } from '@/apollo/queries/getAppActivityChart.gql' +import { GetAppActivityChart } from '@/graphql/queries/getAppActivityChart.gql' import { AppType, ChartDataPointType, TimeRange } from '@/apollo/graphql' import { Area, diff --git a/frontend/components/apps/AppsHomeCard.tsx b/frontend/components/apps/AppsHomeCard.tsx index cb19989b8..04cafeb37 100644 --- a/frontend/components/apps/AppsHomeCard.tsx +++ b/frontend/components/apps/AppsHomeCard.tsx @@ -1,7 +1,7 @@ 'use client' import { useQuery } from '@apollo/client' -import { GetApps } from '@/apollo/queries/getApps.gql' +import { GetApps } from '@/graphql/queries/getApps.gql' import { AppType } from '@/apollo/graphql' import Spinner from '../common/Spinner' import { FaCubes } from 'react-icons/fa' diff --git a/frontend/components/apps/DeleteAppDialog.tsx b/frontend/components/apps/DeleteAppDialog.tsx index 2ac74f64b..4baf5d269 100644 --- a/frontend/components/apps/DeleteAppDialog.tsx +++ b/frontend/components/apps/DeleteAppDialog.tsx @@ -6,8 +6,8 @@ import { Fragment, useState } from 'react' import { FaTrash, FaTimes, FaExclamationTriangle } from 'react-icons/fa' import { toast } from 'react-toastify' import { Button } from '../common/Button' -import { DeleteApp } from '@/apollo/mutations/deleteApp.gql' -import { GetApps } from '@/apollo/queries/getApps.gql' +import { DeleteApplication } from '@/graphql/mutations/deleteApp.gql' +import { GetApps } from '@/graphql/queries/getApps.gql' import { useMutation } from '@apollo/client' import { useRouter } from 'next/navigation' @@ -20,7 +20,7 @@ export default function DeleteAppDialog(props: { const { organisationId, appId, appName, teamName } = props const [isOpen, setIsOpen] = useState(false) const [typedName, setTypedName] = useState('') - const [deleteApp, { loading }] = useMutation(DeleteApp) + const [deleteApp, { loading }] = useMutation(DeleteApplication) const router = useRouter() const reset = () => { diff --git a/frontend/components/apps/NewAppDialog.tsx b/frontend/components/apps/NewAppDialog.tsx index 64c087dd3..104e05840 100644 --- a/frontend/components/apps/NewAppDialog.tsx +++ b/frontend/components/apps/NewAppDialog.tsx @@ -7,8 +7,8 @@ import { Fragment, ReactNode, useEffect, useState } from 'react' import { FaCopy, FaCross, FaExclamationTriangle, FaEye, FaEyeSlash, FaTimes } from 'react-icons/fa' import { toast } from 'react-toastify' import { Button } from '../common/Button' -import { GetApps } from '@/apollo/queries/getApps.gql' -import { CreateApp } from '@/apollo/mutations/createApp.gql' +import { GetApps } from '@/graphql/queries/getApps.gql' +import { CreateApplication } from '@/graphql/mutations/createApp.gql' import { useMutation } from '@apollo/client' import { ApiOrganisationPlanChoices, @@ -35,7 +35,7 @@ export default function NewAppDialog(props: { const [appId, setAppId] = useState('') const [appSecret, setAppSecret] = useState('') const { data: session } = useSession() - const [createApp, { data, loading, error }] = useMutation(CreateApp) + const [createApp, { data, loading, error }] = useMutation(CreateApplication) const IS_CLOUD_HOSTED = process.env.APP_HOST || process.env.NEXT_PUBLIC_APP_HOST diff --git a/frontend/components/layout/Navbar.tsx b/frontend/components/layout/Navbar.tsx index 461889f06..be9cd276b 100644 --- a/frontend/components/layout/Navbar.tsx +++ b/frontend/components/layout/Navbar.tsx @@ -1,8 +1,8 @@ import { Logo } from '../common/Logo' import UserMenu from '../UserMenu' import { useLazyQuery, useQuery } from '@apollo/client' -import GetOrganisations from '@/apollo/queries/getOrganisations.gql' -import { GetApps } from '@/apollo/queries/getApps.gql' +import GetOrganisations from '@/graphql/queries/getOrganisations.gql' +import { GetApps } from '@/graphql/queries/getApps.gql' import { usePathname } from 'next/navigation' import { useEffect } from 'react' import { AppType } from '@/apollo/graphql' @@ -54,7 +54,9 @@ export const NavBar = (props: { team: string }) => {
{IS_CLOUD_HOSTED && } - + + +
diff --git a/frontend/apollo/mutations/createApp.gql b/frontend/graphql/mutations/createApp.gql similarity index 94% rename from frontend/apollo/mutations/createApp.gql rename to frontend/graphql/mutations/createApp.gql index 812a5cfb0..008c79408 100644 --- a/frontend/apollo/mutations/createApp.gql +++ b/frontend/graphql/mutations/createApp.gql @@ -1,4 +1,4 @@ -mutation CreateApp( +mutation CreateApplication( $id: ID! $organisationId: ID! $name: String! diff --git a/frontend/apollo/mutations/createOrganisation.gql b/frontend/graphql/mutations/createOrganisation.gql similarity index 63% rename from frontend/apollo/mutations/createOrganisation.gql rename to frontend/graphql/mutations/createOrganisation.gql index ab5b27b41..1d5759d4a 100644 --- a/frontend/apollo/mutations/createOrganisation.gql +++ b/frontend/graphql/mutations/createOrganisation.gql @@ -1,4 +1,4 @@ -mutation CreateOrganisation($id: ID!, $name: String!, $identityKey: String!) { +mutation CreateOrg($id: ID!, $name: String!, $identityKey: String!) { createOrganisation(id: $id, name: $name, identityKey: $identityKey) { organisation { id diff --git a/frontend/apollo/mutations/deleteApp.gql b/frontend/graphql/mutations/deleteApp.gql similarity index 58% rename from frontend/apollo/mutations/deleteApp.gql rename to frontend/graphql/mutations/deleteApp.gql index b88964f99..674b67070 100644 --- a/frontend/apollo/mutations/deleteApp.gql +++ b/frontend/graphql/mutations/deleteApp.gql @@ -1,4 +1,4 @@ -mutation DeleteApp($id: ID!) { +mutation DeleteApplication($id: ID!) { deleteApp(id: $id) { app { id diff --git a/frontend/apollo/mutations/environments/createEnvironment.gql b/frontend/graphql/mutations/environments/createEnvironment.gql similarity index 60% rename from frontend/apollo/mutations/environments/createEnvironment.gql rename to frontend/graphql/mutations/environments/createEnvironment.gql index e3d701e28..60bf0d8bc 100644 --- a/frontend/apollo/mutations/environments/createEnvironment.gql +++ b/frontend/graphql/mutations/environments/createEnvironment.gql @@ -1,4 +1,5 @@ -mutation CreateEnvironment($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) { + +mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) { createEnvironment(id: $id, appId: $appId, name: $name, envType: $envType, identityKey:$identityKey, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt) { environment { id diff --git a/frontend/apollo/mutations/environments/createEnvironmentKey.gql b/frontend/graphql/mutations/environments/createEnvironmentKey.gql similarity index 60% rename from frontend/apollo/mutations/environments/createEnvironmentKey.gql rename to frontend/graphql/mutations/environments/createEnvironmentKey.gql index bcb586dc1..745ca3581 100644 --- a/frontend/apollo/mutations/environments/createEnvironmentKey.gql +++ b/frontend/graphql/mutations/environments/createEnvironmentKey.gql @@ -1,4 +1,4 @@ -mutation CreateEnvironmentKey($envId: ID!, $ownerId: ID!,$wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) { +mutation CreateEnvKey($envId: ID!, $ownerId: ID!,$wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) { createEnvironmentKey(envId: $envId, userId: $ownerId, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt, identityKey: $identityKey) { environmentKey { id diff --git a/frontend/apollo/mutations/environments/createEnvironmentToken.gql b/frontend/graphql/mutations/environments/createEnvironmentToken.gql similarity index 59% rename from frontend/apollo/mutations/environments/createEnvironmentToken.gql rename to frontend/graphql/mutations/environments/createEnvironmentToken.gql index 2e242fd56..f0d55a1d8 100644 --- a/frontend/apollo/mutations/environments/createEnvironmentToken.gql +++ b/frontend/graphql/mutations/environments/createEnvironmentToken.gql @@ -1,4 +1,4 @@ -mutation CreateEnvironmentToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { +mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { createEnvironmentToken(envId: $envId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { environmentToken { id diff --git a/frontend/apollo/mutations/environments/createSecret.gql b/frontend/graphql/mutations/environments/createSecret.gql similarity index 62% rename from frontend/apollo/mutations/environments/createSecret.gql rename to frontend/graphql/mutations/environments/createSecret.gql index 5187e6a50..17ef1389c 100644 --- a/frontend/apollo/mutations/environments/createSecret.gql +++ b/frontend/graphql/mutations/environments/createSecret.gql @@ -1,4 +1,4 @@ -mutation CreateSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) { +mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) { createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) { secret { id diff --git a/frontend/graphql/mutations/environments/initAppEnvironments.gql b/frontend/graphql/mutations/environments/initAppEnvironments.gql new file mode 100644 index 000000000..228379795 --- /dev/null +++ b/frontend/graphql/mutations/environments/initAppEnvironments.gql @@ -0,0 +1,41 @@ +input CreateEnvironmentInput { + id: ID! + appId: ID! + name: String! + envType: String! + identityKey: String! + wrappedSeed: String! + wrappedSalt: String! +} + + +mutation InitAppEnvironments( + $devEnv: CreateEnvironmentInput! + $stagingEnv: CreateEnvironmentInput! + $prodEnv: CreateEnvironmentInput! +) { + devEnvironment: createEnvironment(input: $devEnv) { + environment { + id + name + createdAt + identityKey + } + } + stagingEnvironment: createEnvironment(input: $stagingEnv) { + environment { + id + name + createdAt + identityKey + } + } + prodEnvironment: createEnvironment(input: $prodEnv) { + environment { + id + name + createdAt + identityKey + } + } +} diff --git a/frontend/apollo/mutations/rotateAppKeys.gql b/frontend/graphql/mutations/rotateAppKeys.gql similarity index 58% rename from frontend/apollo/mutations/rotateAppKeys.gql rename to frontend/graphql/mutations/rotateAppKeys.gql index c0a6123e2..95b8ede33 100644 --- a/frontend/apollo/mutations/rotateAppKeys.gql +++ b/frontend/graphql/mutations/rotateAppKeys.gql @@ -1,4 +1,4 @@ -mutation RotateAppKeys($id: ID!, $appToken: String!, $wrappedKeyShare: String!) { +mutation RotateAppKey($id: ID!, $appToken: String!, $wrappedKeyShare: String!) { rotateAppKeys(id: $id, appToken: $appToken, wrappedKeyShare: $wrappedKeyShare) { app { id diff --git a/frontend/apollo/mutations/users/createUserToken.gql b/frontend/graphql/mutations/users/createUserToken.gql similarity index 58% rename from frontend/apollo/mutations/users/createUserToken.gql rename to frontend/graphql/mutations/users/createUserToken.gql index c55f16f14..09dd22563 100644 --- a/frontend/apollo/mutations/users/createUserToken.gql +++ b/frontend/graphql/mutations/users/createUserToken.gql @@ -1,4 +1,4 @@ -mutation CreateUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { +mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { createUserToken(orgId: $orgId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { userToken { id diff --git a/frontend/apollo/queries/getAppActivityChart.gql b/frontend/graphql/queries/getAppActivityChart.gql similarity index 100% rename from frontend/apollo/queries/getAppActivityChart.gql rename to frontend/graphql/queries/getAppActivityChart.gql diff --git a/frontend/apollo/queries/getAppDetail.gql b/frontend/graphql/queries/getAppDetail.gql similarity index 100% rename from frontend/apollo/queries/getAppDetail.gql rename to frontend/graphql/queries/getAppDetail.gql diff --git a/frontend/apollo/queries/getAppLogCount.gql b/frontend/graphql/queries/getAppLogCount.gql similarity index 100% rename from frontend/apollo/queries/getAppLogCount.gql rename to frontend/graphql/queries/getAppLogCount.gql diff --git a/frontend/apollo/queries/getAppLogs.gql b/frontend/graphql/queries/getAppLogs.gql similarity index 100% rename from frontend/apollo/queries/getAppLogs.gql rename to frontend/graphql/queries/getAppLogs.gql diff --git a/frontend/apollo/queries/getApps.gql b/frontend/graphql/queries/getApps.gql similarity index 100% rename from frontend/apollo/queries/getApps.gql rename to frontend/graphql/queries/getApps.gql diff --git a/frontend/apollo/queries/getOrganisations.gql b/frontend/graphql/queries/getOrganisations.gql similarity index 100% rename from frontend/apollo/queries/getOrganisations.gql rename to frontend/graphql/queries/getOrganisations.gql diff --git a/frontend/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql b/frontend/graphql/queries/organisation/getOrganisationAdminsAndSelf.gql similarity index 100% rename from frontend/apollo/queries/organisation/getOrganisationAdminsAndSelf.gql rename to frontend/graphql/queries/organisation/getOrganisationAdminsAndSelf.gql diff --git a/frontend/apollo/queries/organisation/getOrganisationMembers.gql b/frontend/graphql/queries/organisation/getOrganisationMembers.gql similarity index 100% rename from frontend/apollo/queries/organisation/getOrganisationMembers.gql rename to frontend/graphql/queries/organisation/getOrganisationMembers.gql diff --git a/frontend/apollo/queries/secrets/getAppEnvironments.gql b/frontend/graphql/queries/secrets/getAppEnvironments.gql similarity index 100% rename from frontend/apollo/queries/secrets/getAppEnvironments.gql rename to frontend/graphql/queries/secrets/getAppEnvironments.gql diff --git a/frontend/apollo/queries/secrets/getEnvironmentKey.gql b/frontend/graphql/queries/secrets/getEnvironmentKey.gql similarity index 69% rename from frontend/apollo/queries/secrets/getEnvironmentKey.gql rename to frontend/graphql/queries/secrets/getEnvironmentKey.gql index 62251a0b2..484df61df 100644 --- a/frontend/apollo/queries/secrets/getEnvironmentKey.gql +++ b/frontend/graphql/queries/secrets/getEnvironmentKey.gql @@ -1,5 +1,5 @@ query GetEnvironmentKey($envId: ID!) { - environmentKeys(envId: $envId) { + environmentKeys(environmentId: $envId) { id identityKey wrappedSeed diff --git a/frontend/apollo/queries/secrets/getEnvironmentTokens.gql b/frontend/graphql/queries/secrets/getEnvironmentTokens.gql similarity index 100% rename from frontend/apollo/queries/secrets/getEnvironmentTokens.gql rename to frontend/graphql/queries/secrets/getEnvironmentTokens.gql diff --git a/frontend/apollo/queries/secrets/getSecrets.gql b/frontend/graphql/queries/secrets/getSecrets.gql similarity index 100% rename from frontend/apollo/queries/secrets/getSecrets.gql rename to frontend/graphql/queries/secrets/getSecrets.gql diff --git a/frontend/apollo/queries/users/getUserTokens.gql b/frontend/graphql/queries/users/getUserTokens.gql similarity index 100% rename from frontend/apollo/queries/users/getUserTokens.gql rename to frontend/graphql/queries/users/getUserTokens.gql diff --git a/frontend/package.json b/frontend/package.json index d6e10e2d6..b17331af9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,7 +46,6 @@ "devDependencies": { "@graphql-codegen/cli": "3.2.2", "@graphql-codegen/client-preset": "2.1.1", - "@graphql-codegen/typescript": "^3.0.2", "@graphql-codegen/typescript-react-apollo": "^3.3.7", "@types/country-flag-icons": "^1.2.0", "@types/jsonwebtoken": "^9.0.1", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9fc9201a2..bcab2a374 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -4,7 +4,8 @@ "@ampproject/remapping@^2.2.0": version "2.2.1" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" @@ -29,7 +30,8 @@ "@ardatan/relay-compiler@12.0.0": version "12.0.0" - resolved "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz" + resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz#2e4cca43088e807adc63450e8cab037020e91106" + integrity sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q== dependencies: "@babel/core" "^7.14.0" "@babel/generator" "^7.14.0" @@ -51,201 +53,225 @@ "@ardatan/sync-fetch@^0.0.1": version "0.0.1" - resolved "https://registry.npmjs.org/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz#3385d3feedceb60a896518a1db857ec1e945348f" + integrity sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA== dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz" +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" + integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.22.10" + chalk "^2.4.2" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz" +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@^7.14.0": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz" + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" + integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.10" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.10" + "@babel/parser" "^7.22.10" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" - semver "^6.3.0" + semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz" +"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" + integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.22.10" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz" +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz" +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.10", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" + integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== dependencies: - "@babel/compat-data" "^7.21.4" - "@babel/helper-validator-option" "^7.21.0" - browserslist "^4.21.3" + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz" - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" - -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz" - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz" - dependencies: - "@babel/types" "^7.21.0" - -"@babel/helper-module-imports@^7.18.6": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz" - dependencies: - "@babel/types" "^7.21.4" - -"@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz" - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz" - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz" - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" - -"@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" - -"@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" + integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" + integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helpers@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" + integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" + +"@babel/highlight@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" + integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz" +"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" + integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-object-rest-spread@^7.0.0": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: "@babel/compat-data" "^7.20.5" "@babel/helper-compilation-targets" "^7.20.7" @@ -255,209 +281,244 @@ "@babel/plugin-syntax-class-properties@^7.0.0": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz" +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz#163b820b9e7696ce134df3ee716d9c0c98035859" + integrity sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-import-assertions@7.20.0": - version "7.20.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz" +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz" +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz" + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz#88a1dccc3383899eb5e660534a76a22ecee64faa" + integrity sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.0.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz" - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" + integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.21.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz" + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz#38e2273814a58c810b6c34ea293be4973c4eb5e2" + integrity sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz#0bb17110c7bf5b35a60754b2f00c58302381dee2" + integrity sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-flow" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.22.5" "@babel/plugin-transform-for-of@^7.0.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-function-name@^7.0.0": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-literals@^7.0.0": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.21.2" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== dependencies: - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" "@babel/plugin-transform-object-super@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.21.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" + integrity sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" + integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.21.0" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-spread@^7.0.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/runtime@^7.0.0": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682" + integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + regenerator-runtime "^0.14.0" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0": +"@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz" dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz" - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" + integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== + dependencies: + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.10" + "@babel/types" "^7.22.10" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz" +"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.22.10", "@babel/types@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" + integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@colors/colors@1.5.0": @@ -504,14 +565,16 @@ "@graphql-codegen/add@^4.0.1": version "4.0.1" - resolved "https://registry.npmjs.org/@graphql-codegen/add/-/add-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-4.0.1.tgz#c187af820fdd2fc7a9c1c2453bc389cd4e16699e" + integrity sha512-A7k+9eRfrKyyNfhWEN/0eKz09R5cp4XXxUuNLQAVm/aohmVI2xdMV4lM02rTlM6Pyou3cU/v0iZnhgo6IRpqeg== dependencies: "@graphql-codegen/plugin-helpers" "^4.1.0" tslib "~2.5.0" "@graphql-codegen/cli@3.2.2": version "3.2.2" - resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-3.2.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.2.2.tgz#1a94bc1ff9cfb7d618859336017d523689ab7d15" + integrity sha512-u+dm/SW1heLnUL4Tyf5Uv0AxOFhTCmUPHKwRLq2yE8MPhv7+Ti4vxxUP/XGoaMNRuHlN37wLI7tpFLV1Hhm22Q== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" @@ -551,7 +614,8 @@ "@graphql-codegen/client-preset@2.1.1": version "2.1.1" - resolved "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-2.1.1.tgz#acf065d9520cde7e34ad0a9adada615e7502b884" + integrity sha512-yDFiO2CimwjkH/YE5nwRcLkXoAVz31NC+WWvFQdyrR1UKQqe2tqV/bKLUifnSbBGS238ajHJ6AEzVlzIvI8OYQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" @@ -569,7 +633,8 @@ "@graphql-codegen/core@^3.1.0": version "3.1.0" - resolved "https://registry.npmjs.org/@graphql-codegen/core/-/core-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-3.1.0.tgz#ad859d52d509a4eb2ebe5aabba6543a628fb181b" + integrity sha512-DH1/yaR7oJE6/B+c6ZF2Tbdh7LixF1K8L+8BoSubjNyQ8pNwR4a70mvc1sv6H7qgp6y1bPQ9tKE+aazRRshysw== dependencies: "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-tools/schema" "^9.0.0" @@ -578,7 +643,8 @@ "@graphql-codegen/gql-tag-operations@2.0.2": version "2.0.2" - resolved "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-2.0.2.tgz#6fb693ede76d9ac67c1d7755aa0fc01c42b2be82" + integrity sha512-FB4/Q0xP/lIjwnlxdeGAfGFAiL7AhzIJB9keNrosd4Xe9r8V8NuZ0+0/hGc7KdzHhojYF/ycmJD7V2JLWaf23Q== dependencies: "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-codegen/visitor-plugin-common" "3.0.2" @@ -588,7 +654,8 @@ "@graphql-codegen/plugin-helpers@^2.7.2": version "2.7.2" - resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.7.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.7.2.tgz#6544f739d725441c826a8af6a49519f588ff9bed" + integrity sha512-kln2AZ12uii6U59OQXdjLk5nOlh1pHis1R98cDZGFnfaiAbX9V3fxcZ1MMJkB7qFUymTALzyjZoXXdyVmPMfRg== dependencies: "@graphql-tools/utils" "^8.8.0" change-case-all "1.0.14" @@ -599,7 +666,8 @@ "@graphql-codegen/plugin-helpers@^4.1.0", "@graphql-codegen/plugin-helpers@^4.2.0": version "4.2.0" - resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.2.0.tgz#8324914d0f99162a223cfa01796cdd6be972d2ae" + integrity sha512-THFTCfg+46PXlXobYJ/OoCX6pzjI+9woQqCjdyKtgoI0tn3Xq2HUUCiidndxUpEYVrXb5pRiRXb7b/ZbMQqD0A== dependencies: "@graphql-tools/utils" "^9.0.0" change-case-all "1.0.15" @@ -610,7 +678,8 @@ "@graphql-codegen/schema-ast@^3.0.1": version "3.0.1" - resolved "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-3.0.1.tgz#37b458bb57b95715a9eb4259341c856dae2a461d" + integrity sha512-rTKTi4XiW4QFZnrEqetpiYEWVsOFNoiR/v3rY9mFSttXFbIwNXPme32EspTiGWmEEdHY8UuTDtZN3vEcs/31zw== dependencies: "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-tools/utils" "^9.0.0" @@ -618,7 +687,8 @@ "@graphql-codegen/typed-document-node@^3.0.2": version "3.0.2" - resolved "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-3.0.2.tgz#afdf039c38f1b02a946854b6f487af764429eec8" + integrity sha512-RqX46y0GoMAcCfXjkUabOWpeSQ7tazpS5WyzWJNakpzXxNACx8NACaghU8zTEM+gjqtIp6YbFY/S92HQ34HbRQ== dependencies: "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-codegen/visitor-plugin-common" "3.0.2" @@ -628,7 +698,8 @@ "@graphql-codegen/typescript-operations@^3.0.2": version "3.0.4" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.4.tgz#60163c07f0ef73655779ece450d02c1172c44027" + integrity sha512-6yE2OL2+WJ1vd5MwFEGXpaxsFGzjAGUytPVHDML3Bi3TwP1F3lnQlIko4untwvHW0JhZEGQ7Ck30H9HjcxpdKA== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-codegen/typescript" "^3.0.4" @@ -638,7 +709,8 @@ "@graphql-codegen/typescript-react-apollo@^3.3.7": version "3.3.7" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.3.7.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.3.7.tgz#e856caa22c5f7bc9a546c44f54e5f3bd5801ab67" + integrity sha512-9DUiGE8rcwwEkf/S1kpBT/Py/UUs9Qak14bOnTT1JHWs1MWhiDA7vml+A8opU7YFI1EVbSSaE5jjRv11WHoikQ== dependencies: "@graphql-codegen/plugin-helpers" "^2.7.2" "@graphql-codegen/visitor-plugin-common" "2.13.1" @@ -648,7 +720,8 @@ "@graphql-codegen/typescript@^3.0.2", "@graphql-codegen/typescript@^3.0.4": version "3.0.4" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.4.tgz#e12dc106a2722ebc7d18556980ccf47fa9d0805f" + integrity sha512-x4O47447DZrWNtE/l5CU9QzzW4m1RbmCEdijlA3s2flG/y1Ckqdemob4CWfilSm5/tZ3w1junVDY616RDTSvZw== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-codegen/schema-ast" "^3.0.1" @@ -658,7 +731,8 @@ "@graphql-codegen/visitor-plugin-common@2.13.1": version "2.13.1" - resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.1.tgz#2228660f6692bcdb96b1f6d91a0661624266b76b" + integrity sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg== dependencies: "@graphql-codegen/plugin-helpers" "^2.7.2" "@graphql-tools/optimize" "^1.3.0" @@ -673,7 +747,8 @@ "@graphql-codegen/visitor-plugin-common@3.0.2": version "3.0.2" - resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.0.2.tgz#784c0faaa7e0773072ea5de464fdcae8d7765564" + integrity sha512-dKblRFrB0Fdl3+nPlzlLBka+TN/EGwr/q09mwry0H58z3j6gXkMbsdPr+dc8MhgOV7w/8egRvSPIvd7m6eFCnw== dependencies: "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-tools/optimize" "^1.3.0" @@ -688,7 +763,8 @@ "@graphql-codegen/visitor-plugin-common@3.1.1", "@graphql-codegen/visitor-plugin-common@^3.0.2": version "3.1.1" - resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.1.1.tgz#50c2aa3c537a805ce68d2f115d0a9811b151428c" + integrity sha512-uAfp+zu/009R3HUAuTK2AamR1bxIltM6rrYYI6EXSmkM3rFtFsLTuJhjUDj98HcUCszJZrADppz8KKLGRUVlNg== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-tools/optimize" "^1.3.0" @@ -703,39 +779,43 @@ "@graphql-tools/apollo-engine-loader@^7.3.6": version "7.3.26" - resolved "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.3.26.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.3.26.tgz#91e54460d5579933e42a2010b8688c3459c245d8" + integrity sha512-h1vfhdJFjnCYn9b5EY1Z91JTF0KB3hHVJNQIsiUV2mpQXZdeOXQoaWeYEKaiI5R6kwBw5PP9B0fv3jfUIG8LyQ== dependencies: "@ardatan/sync-fetch" "^0.0.1" "@graphql-tools/utils" "^9.2.1" "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@^8.5.19": - version "8.5.19" - resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.5.19.tgz" +"@graphql-tools/batch-execute@^8.5.22": + version "8.5.22" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.22.tgz#a742aa9d138fe794e786d8fb6429665dc7df5455" + integrity sha512-hcV1JaY6NJQFQEwCKrYhpfLK8frSXDbtNMoTur98u10Cmecy1zrqNKSqhEyGetpgHxaJRqszGzKeI3RuroDN6A== dependencies: "@graphql-tools/utils" "^9.2.1" - dataloader "2.2.2" + dataloader "^2.2.2" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" "@graphql-tools/code-file-loader@^7.3.17": - version "7.3.22" - resolved "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.3.22.tgz" + version "7.3.23" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.23.tgz#33793f9a1f8e74981f8ae6ec4ab7061f9713db15" + integrity sha512-8Wt1rTtyTEs0p47uzsPJ1vAtfAx0jmxPifiNdmo9EOCuUPyQGEbMaik/YkqZ7QUFIEYEQu+Vgfo8tElwOPtx5Q== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.5.1" + "@graphql-tools/graphql-tag-pluck" "7.5.2" "@graphql-tools/utils" "^9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/delegate@^9.0.31": - version "9.0.32" - resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-9.0.32.tgz" + version "9.0.35" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.35.tgz#94683f4bcec63520b4a6c8b2abf2e2e9324ea4f1" + integrity sha512-jwPu8NJbzRRMqi4Vp/5QX1vIUeUPpWmlQpOkXQD2r1X45YsVceyUUBnktCrlJlDB4jPRVy7JQGwmYo3KFiOBMA== dependencies: - "@graphql-tools/batch-execute" "^8.5.19" - "@graphql-tools/executor" "^0.0.18" - "@graphql-tools/schema" "^9.0.18" + "@graphql-tools/batch-execute" "^8.5.22" + "@graphql-tools/executor" "^0.0.20" + "@graphql-tools/schema" "^9.0.19" "@graphql-tools/utils" "^9.2.1" dataloader "^2.2.2" tslib "^2.5.0" @@ -743,14 +823,16 @@ "@graphql-tools/documents@^0.1.0": version "0.1.0" - resolved "https://registry.npmjs.org/@graphql-tools/documents/-/documents-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/documents/-/documents-0.1.0.tgz#9c27faea5a17ab271dbd99edd8d52eee0e43573e" + integrity sha512-1WQeovHv5S1M3xMzQxbSrG3yl6QOnsq2JUBnlg5/0aMM5R4GNMx6Ms+ROByez/dnuA81kstRuSK+2qpe+GaRIw== dependencies: lodash.sortby "^4.7.0" tslib "^2.4.0" "@graphql-tools/executor-graphql-ws@^0.0.14": version "0.0.14" - resolved "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.14.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.14.tgz#e0f53fc4cfc8a06cc461b2bc1edb4bb9a8e837ed" + integrity sha512-P2nlkAsPZKLIXImFhj0YTtny5NQVGSsKnhi7PzXiaHSXc6KkzqbWZHKvikD4PObanqg+7IO58rKFpGXP7eeO+w== dependencies: "@graphql-tools/utils" "^9.2.1" "@repeaterjs/repeater" "3.0.4" @@ -761,8 +843,9 @@ ws "8.13.0" "@graphql-tools/executor-http@^0.1.7", "@graphql-tools/executor-http@^0.1.9": - version "0.1.9" - resolved "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-0.1.9.tgz" + version "0.1.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.10.tgz#faf48e18e62a925796c9653c2f50cf2095bc8e6f" + integrity sha512-hnAfbKv0/lb9s31LhWzawQ5hghBfHS+gYWtqxME6Rl0Aufq9GltiiLBcl7OVVOnkLF0KhwgbYP1mB5VKmgTGpg== dependencies: "@graphql-tools/utils" "^9.2.1" "@repeaterjs/repeater" "^3.0.4" @@ -775,7 +858,8 @@ "@graphql-tools/executor-legacy-ws@^0.0.11": version "0.0.11" - resolved "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.11.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.11.tgz#a1e12be8279e92a363a23d4105461a34cd9e389e" + integrity sha512-4ai+NnxlNfvIQ4c70hWFvOZlSUN8lt7yc+ZsrwtNFbFPH/EroIzFMapAxM9zwyv9bH38AdO3TQxZ5zNxgBdvUw== dependencies: "@graphql-tools/utils" "^9.2.1" "@types/ws" "^8.0.0" @@ -783,21 +867,23 @@ tslib "^2.4.0" ws "8.13.0" -"@graphql-tools/executor@^0.0.18": - version "0.0.18" - resolved "https://registry.npmjs.org/@graphql-tools/executor/-/executor-0.0.18.tgz" +"@graphql-tools/executor@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.20.tgz#d51d159696e839522dd49d936636af251670e425" + integrity sha512-GdvNc4vszmfeGvUqlcaH1FjBoguvMYzxAfT6tDd4/LgwymepHhinqLNA5otqwVLW+JETcDaK7xGENzFomuE6TA== dependencies: "@graphql-tools/utils" "^9.2.1" "@graphql-typed-document-node/core" "3.2.0" - "@repeaterjs/repeater" "3.0.4" + "@repeaterjs/repeater" "^3.0.4" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" "@graphql-tools/git-loader@^7.2.13": - version "7.2.21" - resolved "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-7.2.21.tgz" + version "7.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.3.0.tgz#ca10c17d4f58c4592432d9d2ac1c2b393aebad16" + integrity sha512-gcGAK+u16eHkwsMYqqghZbmDquh8QaO24Scsxq+cVR+vx1ekRlsEiXvu+yXVDbZdcJ6PBIbeLcQbEu+xhDLmvQ== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.5.1" + "@graphql-tools/graphql-tag-pluck" "7.5.2" "@graphql-tools/utils" "^9.2.1" is-glob "4.0.3" micromatch "^4.0.4" @@ -806,7 +892,8 @@ "@graphql-tools/github-loader@^7.3.20": version "7.3.28" - resolved "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-7.3.28.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.28.tgz#a7166b136e8442bd8b3ab943ad3b66c84bcabfcf" + integrity sha512-OK92Lf9pmxPQvjUNv05b3tnVhw0JRfPqOf15jZjyQ8BfdEUrJoP32b4dRQQem/wyRL24KY4wOfArJNqzpsbwCA== dependencies: "@ardatan/sync-fetch" "^0.0.1" "@graphql-tools/executor-http" "^0.1.9" @@ -818,7 +905,8 @@ "@graphql-tools/graphql-file-loader@^7.3.7", "@graphql-tools/graphql-file-loader@^7.5.0": version "7.5.17" - resolved "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.17.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.17.tgz#7c281617ea3ab4db4d42a2bdb49850f2b937f0f9" + integrity sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw== dependencies: "@graphql-tools/import" "6.7.18" "@graphql-tools/utils" "^9.2.1" @@ -826,12 +914,13 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@7.5.1", "@graphql-tools/graphql-tag-pluck@^7.4.6": - version "7.5.1" - resolved "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.5.1.tgz" +"@graphql-tools/graphql-tag-pluck@7.5.2", "@graphql-tools/graphql-tag-pluck@^7.4.6": + version "7.5.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.5.2.tgz#502f1e066e19d832ebdeba5f571d7636dc27572d" + integrity sha512-RW+H8FqOOLQw0BPXaahYepVSRjuOHw+7IL8Opaa5G5uYGOBxoXR7DceyQ7BcpMgktAOOmpDNQ2WtcboChOJSRA== dependencies: "@babel/parser" "^7.16.8" - "@babel/plugin-syntax-import-assertions" "7.20.0" + "@babel/plugin-syntax-import-assertions" "^7.20.0" "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" "@graphql-tools/utils" "^9.2.1" @@ -839,7 +928,8 @@ "@graphql-tools/import@6.7.18": version "6.7.18" - resolved "https://registry.npmjs.org/@graphql-tools/import/-/import-6.7.18.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.18.tgz#ad092d8a4546bb6ffc3e871e499eec7ac368680b" + integrity sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ== dependencies: "@graphql-tools/utils" "^9.2.1" resolve-from "5.0.0" @@ -847,7 +937,8 @@ "@graphql-tools/json-file-loader@^7.3.7", "@graphql-tools/json-file-loader@^7.4.1": version "7.4.18" - resolved "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-7.4.18.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.18.tgz#d78ae40979bde51cfc59717757354afc9e35fba2" + integrity sha512-AJ1b6Y1wiVgkwsxT5dELXhIVUPs/u3VZ8/0/oOtpcoyO/vAeM5rOvvWegzicOOnQw8G45fgBRMkkRfeuwVt6+w== dependencies: "@graphql-tools/utils" "^9.2.1" globby "^11.0.3" @@ -856,7 +947,8 @@ "@graphql-tools/load@^7.5.5", "@graphql-tools/load@^7.8.0": version "7.8.14" - resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-7.8.14.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.14.tgz#f2356f9a5f658a42e33934ae036e4b2cadf2d1e9" + integrity sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg== dependencies: "@graphql-tools/schema" "^9.0.18" "@graphql-tools/utils" "^9.2.1" @@ -864,21 +956,24 @@ tslib "^2.4.0" "@graphql-tools/merge@^8.2.6", "@graphql-tools/merge@^8.4.1": - version "8.4.1" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.1.tgz" + version "8.4.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" + integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== dependencies: "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" "@graphql-tools/optimize@^1.3.0": version "1.4.0" - resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.4.0.tgz#20d6a9efa185ef8fc4af4fd409963e0907c6e112" + integrity sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw== dependencies: tslib "^2.4.0" "@graphql-tools/prisma-loader@^7.2.49": - version "7.2.71" - resolved "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-7.2.71.tgz" + version "7.2.72" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.72.tgz#6304fc23600458396f3ede713d8e2371df7850e3" + integrity sha512-0a7uV7Fky6yDqd0tI9+XMuvgIo6GAqiVzzzFV4OSLry4AwiQlI3igYseBV7ZVOGhedOTqj/URxjpiv07hRcwag== dependencies: "@graphql-tools/url-loader" "^7.17.18" "@graphql-tools/utils" "^9.2.1" @@ -889,8 +984,8 @@ debug "^4.3.1" dotenv "^16.0.0" graphql-request "^6.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" + http-proxy-agent "^6.0.0" + https-proxy-agent "^6.0.0" jose "^4.11.4" js-yaml "^4.0.0" json-stable-stringify "^1.0.1" @@ -901,24 +996,27 @@ "@graphql-tools/relay-operation-optimizer@^6.5.0": version "6.5.18" - resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz#a1b74a8e0a5d0c795b8a4d19629b654cf66aa5ab" + integrity sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg== dependencies: "@ardatan/relay-compiler" "12.0.0" "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" -"@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.18": - version "9.0.18" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.18.tgz" +"@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.18", "@graphql-tools/schema@^9.0.19": + version "9.0.19" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" + integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== dependencies: "@graphql-tools/merge" "^8.4.1" "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.17.18", "@graphql-tools/url-loader@^7.9.7": version "7.17.18" - resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-7.17.18.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.18.tgz#3e253594d23483e4c0dd3a4c3dd2ad5cd0141192" + integrity sha512-ear0CiyTj04jCVAxi7TvgbnGDIN2HgqzXzwsfcqiVg9cvjT40NcMlZ2P1lZDgqMkZ9oyLTV8Bw6j+SyG6A+xPw== dependencies: "@ardatan/sync-fetch" "^0.0.1" "@graphql-tools/delegate" "^9.0.31" @@ -936,20 +1034,23 @@ "@graphql-tools/utils@^8.8.0": version "8.13.1" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.13.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.13.1.tgz#b247607e400365c2cd87ff54654d4ad25a7ac491" + integrity sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw== dependencies: tslib "^2.4.0" "@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1", "@graphql-tools/utils@^9.2.1": version "9.2.1" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" + integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== dependencies: "@graphql-typed-document-node/core" "^3.1.1" tslib "^2.4.0" "@graphql-tools/wrap@^9.4.2": version "9.4.2" - resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-9.4.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.2.tgz#30835587c4c73be1780908a7cb077d8013aa2703" + integrity sha512-DFcd9r51lmcEKn0JW43CWkkI2D6T9XI1juW/Yo86i04v43O9w2/k4/nx2XTJv4Yv+iXwUw7Ok81PGltwGJSDSA== dependencies: "@graphql-tools/delegate" "^9.0.31" "@graphql-tools/schema" "^9.0.18" @@ -959,11 +1060,13 @@ "@graphql-typed-document-node/core@3.1.2": version "3.1.2" - resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.2.tgz#6fc464307cbe3c8ca5064549b806360d84457b04" + integrity sha512-9anpBMM9mEgZN4wr2v8wHJI2/u5TnnggewRN6OlvXTTnuVyoY19X6rOv9XTqKRw6dcGKwZsBi8n0kDE2I5i4VA== "@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" - resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== "@headlessui/react@^1.7.13": version "1.7.14" @@ -997,34 +1100,35 @@ "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.1": version "1.1.2" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" - -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz" + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@motionone/animation@^10.15.1": version "10.15.1" @@ -1145,14 +1249,16 @@ "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" @@ -1315,18 +1421,81 @@ version "1.0.4" resolved "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.4.tgz" +"@parcel/watcher-android-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.2.0.tgz#3d1a71f251ba829ab884dfe119cc4f4c49c7222b" + integrity sha512-nU2wh00CTQT9rr1TIKTjdQ9lAGYpmz6XuKw0nAwAN+S2A5YiD55BK1u+E5WMCT8YOIDe/n6gaj4o/Bi9294SSQ== + +"@parcel/watcher-darwin-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.2.0.tgz#9ed47d9e4146d53a39f62577a993426d6ddec2d2" + integrity sha512-cJl0UZDcodciy3TDMomoK/Huxpjlkkim3SyMgWzjovHGOZKNce9guLz2dzuFwfObBFCjfznbFMIvAZ5syXotYw== + +"@parcel/watcher-darwin-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.2.0.tgz#9b553e61f4a01a0947b1c005619db7b7c9a286d1" + integrity sha512-QI77zxaGrCV1StKcoRYfsUfmUmvPMPfQrubkBBy5XujV2fwaLgZivQOTQMBgp5K2+E19u1ufpspKXAPqSzpbyg== + +"@parcel/watcher-linux-arm-glibc@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.2.0.tgz#2644f6292b862c52d636978da8711bf2f7b14b24" + integrity sha512-I2GPBcAXazPzabCmfsa3HRRW+MGlqxYd8g8RIueJU+a4o5nyNZDz0CR1cu0INT0QSQXEZV7w6UE8Hz9CF8u3Pg== + +"@parcel/watcher-linux-arm64-glibc@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.2.0.tgz#b82c1715a20e6725c89b2697a208d2ae488d22da" + integrity sha512-St5mlfp+2lS9AmgixUqfwJa/DwVmTCJxC1HcOubUTz6YFOKIlkHCeUa1Bxi4E/tR/HSez8+heXHL8HQkJ4Bd8g== + +"@parcel/watcher-linux-arm64-musl@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.2.0.tgz#45511adc69c4c89b10965f0dd1519e56a00f512f" + integrity sha512-jS+qfhhoOBVWwMLP65MaG8xdInMK30pPW8wqTCg2AAuVJh5xepMbzkhHJ4zURqHiyY3EiIRuYu4ONJKCxt8iqA== + +"@parcel/watcher-linux-x64-glibc@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.2.0.tgz#f6e878bf40874b1ce9530ec87e5c2e644e9ad5ac" + integrity sha512-xJvJ7R2wJdi47WZBFS691RDOWvP1j/IAs3EXaWVhDI8FFITbWrWaln7KoNcR0Y3T+ZwimFY/cfb0PNht1q895g== + +"@parcel/watcher-linux-x64-musl@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.2.0.tgz#4a1b7a8f65b42f50b1819185ef56ed3c9e251425" + integrity sha512-D+NMpgr23a+RI5mu8ZPKWy7AqjBOkURFDgP5iIXXEf/K3hm0jJ3ogzi0Ed2237B/CdYREimCgXyeiAlE/FtwyA== + +"@parcel/watcher-win32-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.2.0.tgz#4cdbaf8c1097038e838d12f897ac8595b18dfb1f" + integrity sha512-z225cPn3aygJsyVUOWwfyW+fY0Tvk7N3XCOl66qUPFxpbuXeZuiuuJemmtm8vxyqa3Ur7peU/qJxrpC64aeI7Q== + +"@parcel/watcher-win32-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.2.0.tgz#f2fcc255b7cadfd0afa7c51cde36413632075bcc" + integrity sha512-JqGW0RJ61BkKx+yYzIURt9s53P7xMVbv0uxYPzAXLBINGaFmkIKSuUPyBVfy8TMbvp93lvF4SPBNDzVRJfvgOw== + "@parcel/watcher@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.1.0.tgz" + version "2.2.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.2.0.tgz#92067954e591d239c3ecfa08add205f88f476068" + integrity sha512-71S4TF+IMyAn24PK4KSkdKtqJDR3zRzb0HE3yXpacItqTM7XfF2f5q9NEGLEVl0dAaBAGfNwDCjH120y25F6Tg== dependencies: + detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" - node-addon-api "^3.2.1" - node-gyp-build "^4.3.0" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.2.0" + "@parcel/watcher-darwin-arm64" "2.2.0" + "@parcel/watcher-darwin-x64" "2.2.0" + "@parcel/watcher-linux-arm-glibc" "2.2.0" + "@parcel/watcher-linux-arm64-glibc" "2.2.0" + "@parcel/watcher-linux-arm64-musl" "2.2.0" + "@parcel/watcher-linux-x64-glibc" "2.2.0" + "@parcel/watcher-linux-x64-musl" "2.2.0" + "@parcel/watcher-win32-arm64" "2.2.0" + "@parcel/watcher-win32-x64" "2.2.0" "@peculiar/asn1-schema@^2.3.6": version "2.3.6" - resolved "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" + integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -1334,13 +1503,15 @@ "@peculiar/json-schema@^1.1.12": version "1.1.12" - resolved "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== dependencies: tslib "^2.0.0" "@peculiar/webcrypto@^1.4.0": version "1.4.3" - resolved "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" + integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== dependencies: "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" @@ -1365,7 +1536,8 @@ "@repeaterjs/repeater@3.0.4", "@repeaterjs/repeater@^3.0.4": version "3.0.4" - resolved "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" + integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== "@rushstack/eslint-patch@^1.1.3": version "1.2.0" @@ -1405,7 +1577,8 @@ "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tufjs/canonical-json@1.0.0": version "1.0.0" @@ -1424,7 +1597,8 @@ "@types/js-yaml@^4.0.0": version "4.0.5" - resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" + integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== "@types/json-schema@^7.0.9": version "7.0.11" @@ -1432,7 +1606,8 @@ "@types/json-stable-stringify@^1.0.32": version "1.0.34" - resolved "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz#c0fb25e4d957e0ee2e497c1f553d7f8bb668fd75" + integrity sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw== "@types/json5@^0.0.29": version "0.0.29" @@ -1454,13 +1629,19 @@ version "0.7.10" resolved "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz" -"@types/node@*", "@types/node@18.14.0": +"@types/node@*": + version "20.5.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.1.tgz#178d58ee7e4834152b0e8b4d30cbfab578b9bb30" + integrity sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg== + +"@types/node@18.14.0": version "18.14.0" resolved "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz" "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prop-types@*": version "15.7.5" @@ -1495,8 +1676,9 @@ resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz" "@types/ws@^8.0.0": - version "8.5.4" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz" + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: "@types/node" "*" @@ -1619,25 +1801,28 @@ d3-selection "^3.0.0" d3-transition "^3.0.1" -"@whatwg-node/events@^0.0.2": - version "0.0.2" - resolved "https://registry.npmjs.org/@whatwg-node/events/-/events-0.0.2.tgz" +"@whatwg-node/events@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" + integrity sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA== "@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1", "@whatwg-node/fetch@^0.8.2": - version "0.8.5" - resolved "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.8.5.tgz" + version "0.8.8" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.8.tgz#48c6ad0c6b7951a73e812f09dd22d75e9fa18cae" + integrity sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg== dependencies: "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.3.3" + "@whatwg-node/node-fetch" "^0.3.6" busboy "^1.6.0" - urlpattern-polyfill "^7.0.0" + urlpattern-polyfill "^8.0.0" web-streams-polyfill "^3.2.1" -"@whatwg-node/node-fetch@^0.3.3": - version "0.3.5" - resolved "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.3.5.tgz" +"@whatwg-node/node-fetch@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz#e28816955f359916e2d830b68a64493124faa6d0" + integrity sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA== dependencies: - "@whatwg-node/events" "^0.0.2" + "@whatwg-node/events" "^0.0.3" busboy "^1.6.0" fast-querystring "^1.1.1" fast-url-parser "^1.1.3" @@ -1689,6 +1874,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.3.0" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz" @@ -1699,7 +1891,8 @@ agentkeepalive@^4.2.1: aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" indent-string "^4.0.0" @@ -1727,7 +1920,8 @@ amdefine@>=0.0.4: ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" @@ -1737,7 +1931,8 @@ ansi-regex@^2.0.0: ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^2.2.1: version "2.2.1" @@ -1745,13 +1940,15 @@ ansi-styles@^2.2.1: ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" @@ -1794,7 +1991,8 @@ arg@^5.0.2: argparse@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-query@^5.1.3: version "5.1.3" @@ -1814,7 +2012,8 @@ array-includes@^3.1.5, array-includes@^3.1.6: array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array.prototype.flat@^1.3.1: version "1.3.1" @@ -1846,11 +2045,13 @@ array.prototype.tosorted@^1.1.1: asap@~2.0.3: version "2.0.6" - resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== asn1js@^3.0.1, asn1js@^3.0.5: version "3.0.5" - resolved "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== dependencies: pvtsutils "^1.3.2" pvutils "^1.1.3" @@ -1862,7 +2063,8 @@ ast-types-flow@^0.0.7: astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== asynckit@^0.4.0: version "0.4.0" @@ -1870,7 +2072,8 @@ asynckit@^0.4.0: auto-bind@~4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" + integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== autoprefixer@^10.4.13: version "10.4.14" @@ -1907,11 +2110,13 @@ axobject-query@^3.1.1: babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== babel-preset-fbjs@^3.4.0: version "3.4.0" - resolved "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz" + resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-object-rest-spread" "^7.0.0" @@ -1943,11 +2148,13 @@ babel-preset-fbjs@^3.4.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== big-integer@1.6.49: version "1.6.49" @@ -1974,7 +2181,8 @@ bip39@^3.1.0: bl@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" inherits "^2.0.4" @@ -1986,7 +2194,8 @@ body-scroll-lock@^4.0.0-beta.0: brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -1999,11 +2208,12 @@ brace-expansion@^2.0.1: braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -browserslist@^4.21.3, browserslist@^4.21.5: +browserslist@^4.21.5: version "4.21.5" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" dependencies: @@ -2012,9 +2222,20 @@ browserslist@^4.21.3, browserslist@^4.21.5: node-releases "^2.0.8" update-browserslist-db "^1.0.10" +browserslist@^4.21.9: + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== + dependencies: + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" + bser@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" @@ -2024,7 +2245,8 @@ buffer-equal-constant-time@1.0.1: buffer@^5.5.0: version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" ieee754 "^1.1.13" @@ -2044,7 +2266,8 @@ builtins@^5.0.0: busboy@^1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== dependencies: streamsearch "^1.1.0" @@ -2102,11 +2325,13 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camel-case@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== dependencies: pascal-case "^3.1.2" tslib "^2.0.3" @@ -2121,15 +2346,22 @@ camelcase@^1.0.2: camelcase@^5.0.0: version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001464: version "1.0.30001481" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz" +caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001517: + version "1.0.30001522" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" + integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== + capital-case@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== dependencies: no-case "^3.0.4" tslib "^2.0.3" @@ -2152,9 +2384,10 @@ chalk@^1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0: +chalk@^2.4.2: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" @@ -2162,14 +2395,16 @@ chalk@^2.0.0: chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" change-case-all@1.0.14: version "1.0.14" - resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz" + resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1" + integrity sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA== dependencies: change-case "^4.1.2" is-lower-case "^2.0.2" @@ -2184,7 +2419,8 @@ change-case-all@1.0.14: change-case-all@1.0.15: version "1.0.15" - resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz" + resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.15.tgz#de29393167fc101d646cd76b0ef23e27d09756ad" + integrity sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ== dependencies: change-case "^4.1.2" is-lower-case "^2.0.2" @@ -2199,7 +2435,8 @@ change-case-all@1.0.15: change-case@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== dependencies: camel-case "^4.1.2" capital-case "^1.0.4" @@ -2216,7 +2453,8 @@ change-case@^4.1.2: chardet@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: version "3.5.3" @@ -2256,7 +2494,8 @@ classnames@^2.3.1, classnames@^2.3.2: clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-columns@^4.0.0: version "4.0.0" @@ -2267,13 +2506,15 @@ cli-columns@^4.0.0: cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-spinners@^2.5.0: - version "2.8.0" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.8.0.tgz" + version "2.9.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== cli-table3@^0.6.3: version "0.6.3" @@ -2285,14 +2526,16 @@ cli-table3@^0.6.3: cli-truncate@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: slice-ansi "^3.0.0" string-width "^4.2.0" cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== client-only@0.0.1, client-only@^0.0.1: version "0.0.1" @@ -2308,7 +2551,8 @@ cliui@^2.1.0: cliui@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" @@ -2316,7 +2560,8 @@ cliui@^6.0.0: cliui@^8.0.1: version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.1" @@ -2324,7 +2569,8 @@ cliui@^8.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== clsx@^1.1.1, clsx@^1.2.1: version "1.2.1" @@ -2336,23 +2582,27 @@ cmd-shim@^6.0.0: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-support@^1.1.3: version "1.1.3" @@ -2360,7 +2610,8 @@ color-support@^1.1.3: colorette@^2.0.16: version "2.0.20" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== columnify@^1.6.0: version "1.6.0" @@ -2385,11 +2636,13 @@ common-ancestor-path@^1.0.1: common-tags@1.8.2: version "1.8.2" - resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== console-control-strings@^1.1.0: version "1.1.0" @@ -2397,7 +2650,8 @@ console-control-strings@^1.1.0: constant-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== dependencies: no-case "^3.0.4" tslib "^2.0.3" @@ -2409,7 +2663,8 @@ contour_plot@^0.0.1: convert-source-map@^1.7.0: version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== cookie@^0.5.0: version "0.5.0" @@ -2417,7 +2672,8 @@ cookie@^0.5.0: cosmiconfig@8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" + integrity sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ== dependencies: import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -2426,7 +2682,8 @@ cosmiconfig@8.0.0: cosmiconfig@^7.0.0: version "7.1.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -2583,9 +2840,10 @@ damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" -dataloader@2.2.2, dataloader@^2.2.2: +dataloader@^2.2.2: version "2.2.2" - resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" + integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== date-fns@^2.29.3: version "2.30.0" @@ -2595,7 +2853,8 @@ date-fns@^2.29.3: debounce@^1.2.0: version "1.2.1" - resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" @@ -2652,7 +2911,8 @@ deep-is@^0.1.3: defaults@^1.0.3: version "1.0.4" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" @@ -2685,11 +2945,18 @@ depd@^2.0.0: dependency-graph@^0.11.0: version "0.11.0" - resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" + integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== didyoumean@^1.2.2: version "1.2.2" @@ -2701,7 +2968,8 @@ diff@^5.1.0: dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" @@ -2723,14 +2991,16 @@ doctrine@^3.0.0: dot-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== dependencies: no-case "^3.0.4" tslib "^2.0.3" dotenv@^16.0.0: - version "16.0.3" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz" + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== dotignore@~0.1.2: version "0.1.2" @@ -2740,7 +3010,8 @@ dotignore@~0.1.2: dset@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" + integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== ecdsa-sig-formatter@1.0.11: version "1.0.11" @@ -2748,9 +3019,10 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -electron-to-chromium@^1.4.284: - version "1.4.369" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.369.tgz" +electron-to-chromium@^1.4.284, electron-to-chromium@^1.4.477: + version "1.4.496" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.496.tgz#a57534b70d2bdee7e1ad7dbd4c91e560cbd08db1" + integrity sha512-qeXC3Zbykq44RCrBa4kr8v/dWzYJA8rAwpyh9Qd+NKWoJfjG5vvJqy9XOJ9H4P/lqulZBCgUWAYi+FeK5AuJ8g== ellipsize@^0.2.0: version "0.2.0" @@ -2764,7 +3036,8 @@ ellipsize@^0.5.1: emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" @@ -2793,7 +3066,8 @@ err-code@^2.0.2: error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" @@ -2873,7 +3147,8 @@ es-to-primitive@^1.2.1: escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" @@ -3112,7 +3387,8 @@ events@^3.3.0: external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" @@ -3120,11 +3396,13 @@ external-editor@^3.0.3: extract-files@^11.0.0: version "11.0.0" - resolved "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a" + integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ== fast-decode-uri-component@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" + integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -3134,7 +3412,7 @@ fast-diff@^1.1.2: version "1.2.0" resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" -fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@^3.2.11, fast-glob@^3.2.12: version "3.2.12" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" dependencies: @@ -3144,6 +3422,17 @@ fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" @@ -3153,14 +3442,16 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" fast-querystring@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.1.tgz" + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" + integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== dependencies: fast-decode-uri-component "^1.0.1" fast-url-parser@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== dependencies: punycode "^1.3.2" @@ -3170,23 +3461,27 @@ fastest-levenshtein@^1.0.16: fastq@^1.6.0: version "1.15.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" fbjs-css-vars@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== fbjs@^3.0.0: - version "3.0.4" - resolved "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz" + version "3.0.5" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" + integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== dependencies: cross-fetch "^3.1.5" fbjs-css-vars "^1.0.0" @@ -3194,11 +3489,12 @@ fbjs@^3.0.0: object-assign "^4.1.0" promise "^7.1.1" setimmediate "^1.0.5" - ua-parser-js "^0.7.30" + ua-parser-js "^1.0.35" figures@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -3210,13 +3506,15 @@ file-entry-cache@^6.0.1: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" find-up@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" path-exists "^4.0.0" @@ -3315,7 +3613,8 @@ fs-minipass@^3.0.0, fs-minipass@^3.0.1: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: version "2.3.2" @@ -3370,11 +3669,13 @@ gauge@^5.0.0: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: version "1.2.0" @@ -3397,7 +3698,8 @@ get-tsconfig@^4.2.0: glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" @@ -3418,7 +3720,7 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.7, glob@^7.1.1, glob@^7.1.3: +glob@7.1.7, glob@^7.1.3: version "7.1.7" resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" dependencies: @@ -3429,7 +3731,7 @@ glob@7.1.7, glob@^7.1.1, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.4, glob@~7.2.3: +glob@^7.1.1, glob@^7.1.4, glob@~7.2.3: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" dependencies: @@ -3461,7 +3763,8 @@ glob@^9.3.0, glob@^9.3.1, glob@^9.3.2: globals@^11.1.0: version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: version "13.20.0" @@ -3524,7 +3827,8 @@ grapheme-splitter@^1.0.4: graphql-config@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-4.5.0.tgz" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.5.0.tgz#257c2338950b8dce295a27f75c5f6c39f8f777b2" + integrity sha512-x6D0/cftpLUJ0Ch1e5sj1TZn6Wcxx4oMfmhaG9shM0DKajA9iR+j1z86GSTQ19fShbGvrSSvbIQsHku6aQ6BBw== dependencies: "@graphql-tools/graphql-file-loader" "^7.3.7" "@graphql-tools/json-file-loader" "^7.3.7" @@ -3539,8 +3843,9 @@ graphql-config@^4.5.0: tslib "^2.4.0" graphql-request@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/graphql-request/-/graphql-request-6.0.0.tgz" + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== dependencies: "@graphql-typed-document-node/core" "^3.2.0" cross-fetch "^3.1.5" @@ -3553,7 +3858,8 @@ graphql-tag@^2.11.0, graphql-tag@^2.12.6: graphql-ws@5.12.1: version "5.12.1" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.12.1.tgz" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.12.1.tgz#c62d5ac54dbd409cc6520b0b39de374b3d59d0dd" + integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg== graphql@^16.6.0: version "16.6.0" @@ -3571,11 +3877,13 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0: version "1.0.0" @@ -3609,7 +3917,8 @@ has@^1.0.3, has@~1.0.3: header-case@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== dependencies: capital-case "^1.0.4" tslib "^2.0.3" @@ -3640,19 +3949,37 @@ http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" agent-base "6" debug "4" +http-proxy-agent@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-6.1.1.tgz#dc04f1a84e09511740cfbd984a56f86cc42e4277" + integrity sha512-JRCz+4Whs6yrrIoIlrH+ZTmhrRwtMnmOHsHn8GFEn9O2sVfSE+DAZ3oyyGIKF8tjJEeSJmP89j7aTjVsSqsU0g== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" +https-proxy-agent@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz#0965ab47371b3e531cf6794d1eb148710a992ba7" + integrity sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA== + dependencies: + agent-base "^7.0.2" + debug "4" + human-format@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/human-format/-/human-format-0.11.0.tgz" @@ -3669,7 +3996,8 @@ humanize-ms@^1.2.1: iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" @@ -3691,7 +4019,8 @@ ignore-walk@^6.0.0: ignore@^5.2.0: version "5.2.4" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== immutable@^4.0.0: version "4.3.0" @@ -3699,7 +4028,8 @@ immutable@^4.0.0: immutable@~3.7.6: version "3.7.6" - resolved "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" + integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" @@ -3710,7 +4040,8 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-from@4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-4.0.0.tgz#2710b8d66817d232e16f4166e319248d3d5492e2" + integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== imurmurhash@^0.1.4: version "0.1.4" @@ -3718,7 +4049,8 @@ imurmurhash@^0.1.4: indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== infer-owner@^1.0.4: version "1.0.4" @@ -3726,14 +4058,16 @@ infer-owner@^1.0.4: inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^4.1.0: version "4.1.0" @@ -3752,8 +4086,9 @@ init-package-json@^5.0.0: validate-npm-package-name "^5.0.0" inquirer@^8.0.0: - version "8.2.5" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz" + version "8.2.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -3769,7 +4104,7 @@ inquirer@^8.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" - wrap-ansi "^7.0.0" + wrap-ansi "^6.0.1" install@^0.13.0: version "0.13.0" @@ -3793,7 +4128,8 @@ internmap@^1.0.0: invariant@^2.2.4: version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" @@ -3811,7 +4147,8 @@ ip@^2.0.0: is-absolute@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" is-windows "^1.0.1" @@ -3833,7 +4170,8 @@ is-array-buffer@^3.0.1: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" @@ -3886,11 +4224,13 @@ is-docker@^2.0.0, is-docker@^2.1.1: is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" @@ -3900,7 +4240,8 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== is-lambda@^1.0.1: version "1.0.1" @@ -3908,7 +4249,8 @@ is-lambda@^1.0.1: is-lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" + integrity sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ== dependencies: tslib "^2.0.3" @@ -3928,7 +4270,8 @@ is-number-object@^1.0.4: is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-path-inside@^3.0.3: version "3.0.3" @@ -3943,7 +4286,8 @@ is-regex@^1.0.4, is-regex@^1.1.4, is-regex@~1.1.4: is-relative@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" @@ -3981,17 +4325,20 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: is-unc-path@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649" + integrity sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ== dependencies: tslib "^2.0.3" @@ -4014,7 +4361,8 @@ is-weakset@^2.0.1: is-windows@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^2.2.0: version "2.2.0" @@ -4032,17 +4380,29 @@ isexe@^2.0.0: isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== jiti@1.17.1: version "1.17.1" - resolved "https://registry.npmjs.org/jiti/-/jiti-1.17.1.tgz" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.17.1.tgz#264daa43ee89a03e8be28c3d712ccc4eb9f1e8ed" + integrity sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw== + +jiti@^1.17.1: + version "1.19.3" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.3.tgz#ef554f76465b3c2b222dc077834a71f0d4a37569" + integrity sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w== -jiti@^1.17.1, jiti@^1.17.2: +jiti@^1.17.2: version "1.18.2" resolved "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz" -jose@^4.11.4, jose@^4.14.1: +jose@^4.11.4: + version "4.14.4" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" + integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== + +jose@^4.14.1: version "4.14.1" resolved "https://registry.npmjs.org/jose/-/jose-4.14.1.tgz" @@ -4052,21 +4412,25 @@ js-sdsl@^4.1.4: "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.0.0, js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-parse-even-better-errors@^3.0.0: version "3.0.0" @@ -4082,7 +4446,8 @@ json-stable-stringify-without-jsonify@^1.0.1: json-stable-stringify@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz#e06f23128e0bbe342dc996ed5a19e28b57b580e0" + integrity sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g== dependencies: jsonify "^0.0.1" @@ -4092,7 +4457,8 @@ json-stringify-nice@^1.1.4: json-to-pretty-yaml@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" + integrity sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A== dependencies: remedial "^1.0.7" remove-trailing-spaces "^1.0.6" @@ -4111,11 +4477,13 @@ json5@^1.0.1: json5@^2.2.2: version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonify@^0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== jsonparse@^1.3.1: version "1.3.1" @@ -4306,11 +4674,13 @@ lilconfig@^2.0.5, lilconfig@^2.0.6: lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== listr2@^4.0.5: version "4.0.5" - resolved "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" + integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== dependencies: cli-truncate "^2.1.0" colorette "^2.0.16" @@ -4323,7 +4693,8 @@ listr2@^4.0.5: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" @@ -4347,22 +4718,26 @@ lodash.merge@^4.6.2: lodash.sortby@^4.7.0: version "4.7.0" - resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" is-unicode-supported "^0.1.0" log-update@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: ansi-escapes "^4.3.0" cli-cursor "^3.1.0" @@ -4381,19 +4756,22 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: lower-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b" + integrity sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg== dependencies: tslib "^2.0.3" lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== dependencies: tslib "^2.0.3" lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" @@ -4474,7 +4852,8 @@ make-fetch-happen@^11.1.1: map-cache@^0.2.0: version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== memoize-bind@^1.0.3: version "1.0.3" @@ -4496,15 +4875,18 @@ memoize-weak@^1.0.0: merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== meros@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/meros/-/meros-1.2.1.tgz" + version "1.3.0" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2" + integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" picomatch "^2.3.1" @@ -4521,11 +4903,13 @@ mime-types@^2.1.12: mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimatch@4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.3.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" + integrity sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng== dependencies: brace-expansion "^1.1.7" @@ -4645,7 +5029,8 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: ms@2.1.2, ms@^2.1.1: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@^2.0.0, ms@^2.1.2: version "2.1.3" @@ -4653,7 +5038,8 @@ ms@^2.0.0, ms@^2.1.2: mute-stream@0.0.8: version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== mute-stream@~1.0.0: version "1.0.0" @@ -4723,14 +5109,16 @@ next@13.1.6: no-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: lower-case "^2.0.2" tslib "^2.0.3" -node-addon-api@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz" +node-addon-api@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e" + integrity sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA== node-fetch@2.6.7: version "2.6.7" @@ -4739,15 +5127,12 @@ node-fetch@2.6.7: whatwg-url "^5.0.0" node-fetch@^2.6.1: - version "2.6.9" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz" + version "2.6.13" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.13.tgz#a20acbbec73c2e09f9007de5cda17104122e0010" + integrity sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA== dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.3.0: - version "4.6.0" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz" - node-gyp@^9.0.0, node-gyp@^9.3.1: version "9.3.1" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.1.tgz" @@ -4765,11 +5150,13 @@ node-gyp@^9.0.0, node-gyp@^9.3.1: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" +node-releases@^2.0.13, node-releases@^2.0.8: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== nopt@^6.0.0: version "6.0.0" @@ -4794,7 +5181,8 @@ normalize-package-data@^5.0.0: normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" @@ -4966,7 +5354,8 @@ npmlog@^7.0.1: nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== oauth@^0.9.15: version "0.9.15" @@ -5045,13 +5434,15 @@ oidc-token-hash@^5.0.2: once@^1.3.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.0: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" @@ -5092,7 +5483,8 @@ optionator@^0.9.1: ora@^5.4.1: version "5.4.1" - resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: bl "^4.1.0" chalk "^4.1.0" @@ -5106,7 +5498,8 @@ ora@^5.4.1: os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" @@ -5116,13 +5509,15 @@ p-limit@3.1.0, p-limit@^3.0.2: p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" @@ -5134,13 +5529,15 @@ p-locate@^5.0.0: p-map@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pacote@^15.0.0, pacote@^15.0.8, pacote@^15.1.1: version "15.1.1" @@ -5167,14 +5564,16 @@ pacote@^15.0.0, pacote@^15.0.8, pacote@^15.1.1: param-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== dependencies: dot-case "^3.0.4" tslib "^2.0.3" parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" @@ -5188,7 +5587,8 @@ parse-conflict-json@^3.0.0, parse-conflict-json@^3.0.1: parse-filepath@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== dependencies: is-absolute "^1.0.0" map-cache "^0.2.0" @@ -5196,7 +5596,8 @@ parse-filepath@^1.0.2: parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" error-ex "^1.3.1" @@ -5205,25 +5606,29 @@ parse-json@^5.0.0: pascal-case@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== dependencies: no-case "^3.0.4" tslib "^2.0.3" path-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== dependencies: dot-case "^3.0.4" tslib "^2.0.3" path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.1.0: version "3.1.1" @@ -5235,11 +5640,13 @@ path-parse@^1.0.7: path-root-regex@^0.1.0: version "0.1.2" - resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ== path-root@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg== dependencies: path-root-regex "^0.1.0" @@ -5252,15 +5659,18 @@ path-scurry@^1.6.1: path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" @@ -5396,7 +5806,8 @@ promise-retry@^2.0.1: promise@^7.1.1: version "7.3.1" - resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" @@ -5420,21 +5831,24 @@ proxy-from-env@^1.1.0: punycode@^1.3.2: version "1.4.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0: version "2.3.0" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" pvtsutils@^1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.2.tgz" + version "1.3.5" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" + integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== dependencies: - tslib "^2.4.0" + tslib "^2.6.1" pvutils@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== qrcode-terminal@^0.12.0: version "0.12.0" @@ -5442,7 +5856,8 @@ qrcode-terminal@^0.12.0: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^5.1.1: version "5.1.1" @@ -5614,7 +6029,13 @@ reaviz@^14.4.2: regenerator-runtime@^0.13.11: version "0.13.11" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: version "1.4.3" @@ -5630,7 +6051,8 @@ regexpp@^3.2.0: relay-runtime@12.0.0: version "12.0.0" - resolved "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz" + resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" + integrity sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug== dependencies: "@babel/runtime" "^7.0.0" fbjs "^3.0.0" @@ -5638,15 +6060,18 @@ relay-runtime@12.0.0: remedial@^1.0.7: version "1.0.8" - resolved "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" + integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== remove-trailing-spaces@^1.0.6: version "1.0.8" - resolved "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" + integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== repeat-string@^1.5.2: version "1.6.1" @@ -5654,19 +6079,23 @@ repeat-string@^1.5.2: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-from@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve@^1.1.7, resolve@^1.22.1, resolve@~1.22.1: version "1.22.1" @@ -5690,7 +6119,8 @@ response-iterator@^0.2.6: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" @@ -5707,11 +6137,13 @@ retry@^0.12.0: reusify@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== right-align@^0.1.1: version "0.1.3" @@ -5735,11 +6167,13 @@ rollup@^0.25.8: run-async@^2.4.0: version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" @@ -5748,8 +6182,9 @@ rw@^1.3.2: resolved "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz" rxjs@^7.5.5: - version "7.8.0" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz" + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -5785,11 +6220,13 @@ scheduler@^0.23.0: scuid@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" + integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.0.0, semver@^7.1.1, semver@^7.3.5, semver@^7.5.0: version "7.5.0" @@ -5805,7 +6242,8 @@ semver@^7.3.7, semver@^7.3.8: sentence-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== dependencies: no-case "^3.0.4" tslib "^2.0.3" @@ -5813,11 +6251,13 @@ sentence-case@^3.0.4: set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== shebang-command@^2.0.0: version "2.0.0" @@ -5831,7 +6271,8 @@ shebang-regex@^3.0.0: shell-quote@^1.7.3: version "1.8.1" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== side-channel@^1.0.4: version "1.0.4" @@ -5847,7 +6288,8 @@ signal-exit@^3.0.2, signal-exit@^3.0.7: signedsource@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" + integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== sigstore@^1.0.0: version "1.8.0" @@ -5860,7 +6302,8 @@ sigstore@^1.0.0: slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slash@^4.0.0: version "4.0.0" @@ -5868,7 +6311,8 @@ slash@^4.0.0: slice-ansi@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== dependencies: ansi-styles "^4.0.0" astral-regex "^2.0.0" @@ -5876,7 +6320,8 @@ slice-ansi@^3.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" astral-regex "^2.0.0" @@ -5888,7 +6333,8 @@ smart-buffer@^4.2.0: snake-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== dependencies: dot-case "^3.0.4" tslib "^2.0.3" @@ -5952,7 +6398,8 @@ spdx-license-ids@^3.0.0: sponge-case@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" + integrity sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA== dependencies: tslib "^2.0.3" @@ -5976,11 +6423,13 @@ stop-iteration-iterator@^1.0.0: streamsearch@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" + integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" @@ -6029,7 +6478,8 @@ string.prototype.trimstart@^1.0.6: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" @@ -6041,7 +6491,8 @@ strip-ansi@^3.0.0: strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" @@ -6077,13 +6528,15 @@ supports-color@^2.0.0: supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" @@ -6093,7 +6546,8 @@ supports-preserve-symlinks-flag@^1.0.0: swap-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9" + integrity sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw== dependencies: tslib "^2.0.3" @@ -6209,29 +6663,34 @@ tiny-relative-date@^1.3.0: title-case@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" + integrity sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA== dependencies: tslib "^2.0.3" tmp@^0.0.33: version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== transformation-matrix@^2.9.0: version "2.15.0" @@ -6253,7 +6712,8 @@ ts-invariant@^0.10.3: ts-log@^2.2.3: version "2.2.5" - resolved "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz" + resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.5.tgz#aef3252f1143d11047e2cb6f7cfaac7408d96623" + integrity sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA== tsconfig-paths@^3.14.1: version "3.14.1" @@ -6272,13 +6732,24 @@ tslib@^1.8.1: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@~2.5.0: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@^2.3.0: version "2.5.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" tslib@~2.4.0: version "2.4.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + +tslib@~2.5.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== tsutils@^3.21.0: version "3.21.0" @@ -6306,7 +6777,8 @@ type-fest@^0.20.2: type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== typed-array-length@^1.0.4: version "1.0.4" @@ -6320,9 +6792,10 @@ typescript@4.9.5: version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" -ua-parser-js@^0.7.30: - version "0.7.35" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz" +ua-parser-js@^1.0.35: + version "1.0.35" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.35.tgz#c4ef44343bc3db0a3cbefdf21822f1b1fc1ab011" + integrity sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA== uglify-js@^2.6.2: version "2.8.29" @@ -6348,7 +6821,8 @@ unbox-primitive@^1.0.2: unc-path-regex@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== unique-filename@^2.0.0: version "2.0.1" @@ -6376,26 +6850,30 @@ unique-slug@^4.0.0: unixify@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" + integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== dependencies: normalize-path "^2.1.1" -update-browserslist-db@^1.0.10: +update-browserslist-db@^1.0.10, update-browserslist-db@^1.0.11: version "1.0.11" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" upper-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== dependencies: tslib "^2.0.3" upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== dependencies: tslib "^2.0.3" @@ -6405,11 +6883,10 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urlpattern-polyfill@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-7.0.0.tgz" - dependencies: - braces "^3.0.2" +urlpattern-polyfill@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" + integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" @@ -6432,9 +6909,10 @@ validate-npm-package-name@^5.0.0: dependencies: builtins "^5.0.0" -value-or-promise@1.0.12, value-or-promise@^1.0.11, value-or-promise@^1.0.12: +value-or-promise@^1.0.11, value-or-promise@^1.0.12: version "1.0.12" - resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz" + resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" + integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== walk-up-path@^3.0.1: version "3.0.1" @@ -6448,11 +6926,13 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: web-streams-polyfill@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== webcrypto-core@^1.7.7: version "1.7.7" - resolved "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.7.tgz" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" + integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== dependencies: "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" @@ -6462,11 +6942,13 @@ webcrypto-core@^1.7.7: webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -6492,7 +6974,8 @@ which-collection@^1.0.1: which-module@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== which-typed-array@^1.1.9: version "1.1.9" @@ -6535,9 +7018,10 @@ wordwrap@0.0.2: version "0.0.2" resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" -wrap-ansi@^6.2.0: +wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" @@ -6545,7 +7029,8 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" @@ -6553,7 +7038,8 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^5.0.0: version "5.0.0" @@ -6564,19 +7050,23 @@ write-file-atomic@^5.0.0: ws@8.13.0, ws@^8.12.0: version "8.13.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" @@ -6584,7 +7074,8 @@ yallist@^4.0.0: yaml-ast-parser@^0.0.43: version "0.0.43" - resolved "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz" + resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" + integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" @@ -6592,18 +7083,21 @@ yaml@^1.10.0, yaml@^1.10.2: yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^15.3.1: version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" decamelize "^1.2.0" @@ -6618,8 +7112,9 @@ yargs@^15.3.1: yargs-parser "^18.1.2" yargs@^17.0.0: - version "17.7.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz" + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" @@ -6640,7 +7135,8 @@ yargs@~3.10.0: yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== zen-observable-ts@^1.2.5: version "1.2.5" From 006ab2d9b730978e7d150028c3759ff9492e6fd7 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 20 Aug 2023 17:36:12 +0530 Subject: [PATCH 046/161] feat: create input type for environment mutation --- .../backend/graphene/mutations/environment.py | 36 ++++++++++-------- frontend/apollo/gql.ts | 9 ++++- frontend/apollo/graphql.ts | 38 +++++++++++-------- frontend/apollo/schema.graphql | 12 +++++- .../environments/createEnvironment.gql | 4 +- .../environments/initAppEnvironments.gql | 23 +++-------- 6 files changed, 70 insertions(+), 52 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 9ab136976..283b931a8 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -6,35 +6,39 @@ from backend.graphene.types import EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, SecretFolderType, SecretTagType, SecretType, UserTokenType +class EnvironmentInput(graphene.InputObjectType): + id = graphene.ID(required=True) + app_id = graphene.ID(required=True) + name = graphene.String(required=True) + env_type = graphene.String(required=True) + wrapped_seed = graphene.String(required=True) + wrapped_salt = graphene.String(required=True) + identity_key = graphene.String(required=True) + + class CreateEnvironmentMutation(graphene.Mutation): class Arguments: - id = graphene.ID(required=True) - app_id = graphene.ID(required=True) - name = graphene.String(required=True) - env_type = graphene.String(required=True) - wrapped_seed = graphene.String(required=True) - wrapped_salt = graphene.String(required=True) - identity_key = graphene.String(required=True) + environment_data = EnvironmentInput(required=True) environment = graphene.Field(EnvironmentType) @classmethod - def mutate(cls, root, info, id, app_id, name, env_type, wrapped_seed, wrapped_salt, identity_key): + def mutate(cls, root, info, environment_data): user_id = info.context.user.userId - if not user_can_access_app(user_id, app_id): + if not user_can_access_app(user_id, environment_data.app_id): raise GraphQLError("You don't have access to this app") - app = App.objects.get(id=app_id) + app = App.objects.get(id=environment_data.app_id) environment = Environment.objects.create( - id=id, app=app, name=name, env_type=env_type, identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + id=environment_data.id, app=app, name=environment_data.name, env_type=environment_data.env_type, identity_key=environment_data.identity_key, wrapped_seed=environment_data.wrapped_seed, wrapped_salt=environment_data.wrapped_salt) org_owner = OrganisationMember.objects.get( organisation=environment.app.organisation, role=OrganisationMember.OWNER) EnvironmentKey.objects.create(id=id, environment=environment, user=org_owner, - identity_key=identity_key, wrapped_seed=wrapped_seed, wrapped_salt=wrapped_salt) + identity_key=environment_data.identity_key, wrapped_seed=environment_data.wrapped_seed, wrapped_salt=environment_data.wrapped_salt) return CreateEnvironmentMutation(environment=environment) @@ -188,7 +192,8 @@ def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags raise GraphQLError( "You don't have permission to perform this action") - tag_names = SecretTag.objects.filter(id__in=tags).values('name') + tag_names = SecretTag.objects.filter( + id__in=tags).values_list('name', flat=True) secret_data = { 'environment_id': env.id, @@ -233,7 +238,8 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment raise GraphQLError( "You don't have permission to perform this action") - tag_names = SecretTag.objects.filter(id__in=tags).values('name') + tag_names = SecretTag.objects.filter( + id__in=tags).values_list('name', flat=True) secret_data = { 'environment_id': env.id, @@ -242,7 +248,7 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment 'key_digest': key_digest, 'value': value, 'version': secret.version + 1, - 'tags': tag_names, + 'tags': [], 'comment': comment } diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 3f7dc5234..0ab86ddd5 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -16,10 +16,11 @@ const documents = { "mutation CreateApplication($id: ID!, $organisationId: ID!, $name: String!, $identityKey: String!, $appToken: String!, $appSeed: String!, $wrappedKeyShare: String!, $appVersion: Int!) {\n createApp(\n id: $id\n organisationId: $organisationId\n name: $name\n identityKey: $identityKey\n appToken: $appToken\n appSeed: $appSeed\n wrappedKeyShare: $wrappedKeyShare\n appVersion: $appVersion\n ) {\n app {\n id\n name\n identityKey\n }\n }\n}": types.CreateApplicationDocument, "mutation CreateOrg($id: ID!, $name: String!, $identityKey: String!) {\n createOrganisation(id: $id, name: $name, identityKey: $identityKey) {\n organisation {\n id\n name\n createdAt\n }\n }\n}": types.CreateOrgDocument, "mutation DeleteApplication($id: ID!) {\n deleteApp(id: $id) {\n app {\n id\n }\n }\n}": types.DeleteApplicationDocument, - "mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) {\n createEnvironment(\n id: $id\n appId: $appId\n name: $name\n envType: $envType\n identityKey: $identityKey\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n ) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.CreateEnvDocument, + "mutation CreateEnv($input: EnvironmentInput!) {\n createEnvironment(environmentData: $input) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.CreateEnvDocument, "mutation CreateEnvKey($envId: ID!, $ownerId: ID!, $wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) {\n createEnvironmentKey(\n envId: $envId\n userId: $ownerId\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n identityKey: $identityKey\n ) {\n environmentKey {\n id\n createdAt\n }\n }\n}": types.CreateEnvKeyDocument, "mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\n }\n }\n}": types.CreateEnvTokenDocument, "mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}": types.CreateNewSecretDocument, + "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, "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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}": types.CreateNewUserTokenDocument, "query GetAppActivityChart($appId: ID!, $period: TimeRange) {\n appActivityChart(appId: $appId, period: $period) {\n index\n date\n data\n }\n}": types.GetAppActivityChartDocument, @@ -66,7 +67,7 @@ export function graphql(source: "mutation DeleteApplication($id: ID!) {\n delet /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) {\n createEnvironment(\n id: $id\n appId: $appId\n name: $name\n envType: $envType\n identityKey: $identityKey\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n ) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"): (typeof documents)["mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) {\n createEnvironment(\n id: $id\n appId: $appId\n name: $name\n envType: $envType\n identityKey: $identityKey\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n ) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"]; +export function graphql(source: "mutation CreateEnv($input: EnvironmentInput!) {\n createEnvironment(environmentData: $input) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"): (typeof documents)["mutation CreateEnv($input: EnvironmentInput!) {\n createEnvironment(environmentData: $input) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -79,6 +80,10 @@ export function graphql(source: "mutation CreateEnvToken($envId: ID!, $name: Str * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\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 InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"): (typeof documents)["mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\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 e3988fd2c..f5d513ca0 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -147,6 +147,16 @@ export type EditSecretMutation = { secret?: Maybe; }; +export type EnvironmentInput = { + appId: Scalars['ID']; + envType: Scalars['String']; + id: Scalars['ID']; + identityKey: Scalars['String']; + name: Scalars['String']; + wrappedSalt: Scalars['String']; + wrappedSeed: Scalars['String']; +}; + export type EnvironmentKeyType = { __typename?: 'EnvironmentKeyType'; createdAt?: Maybe; @@ -229,13 +239,7 @@ export type MutationCreateAppArgs = { export type MutationCreateEnvironmentArgs = { - appId: Scalars['ID']; - envType: Scalars['String']; - id: Scalars['ID']; - identityKey: Scalars['String']; - name: Scalars['String']; - wrappedSalt: Scalars['String']; - wrappedSeed: Scalars['String']; + environmentData: EnvironmentInput; }; @@ -539,13 +543,7 @@ export type DeleteApplicationMutationVariables = Exact<{ export type DeleteApplicationMutation = { __typename?: 'Mutation', deleteApp?: { __typename?: 'DeleteAppMutation', app?: { __typename?: 'AppType', id: string } | null } | null }; export type CreateEnvMutationVariables = Exact<{ - id: Scalars['ID']; - appId: Scalars['ID']; - name: Scalars['String']; - envType: Scalars['String']; - identityKey: Scalars['String']; - wrappedSeed: Scalars['String']; - wrappedSalt: Scalars['String']; + input: EnvironmentInput; }>; @@ -583,6 +581,15 @@ export type CreateNewSecretMutationVariables = Exact<{ export type CreateNewSecretMutation = { __typename?: 'Mutation', createSecret?: { __typename?: 'CreateSecretMutation', secret?: { __typename?: 'SecretType', id: string, key: string, value: string, createdAt?: any | null } | null } | null }; +export type InitAppEnvironmentsMutationVariables = Exact<{ + devEnv: EnvironmentInput; + stagingEnv: EnvironmentInput; + prodEnv: EnvironmentInput; +}>; + + +export type InitAppEnvironmentsMutation = { __typename?: 'Mutation', devEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null, stagingEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null, prodEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null }; + export type RotateAppKeyMutationVariables = Exact<{ id: Scalars['ID']; appToken: Scalars['String']; @@ -703,10 +710,11 @@ export type GetUserTokensQuery = { __typename?: 'Query', userTokens?: Array<{ __ export const CreateApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateApplication"},"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":"organisationId"}},"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":"appToken"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appSeed"}},"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":"appVersion"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"appToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}}},{"kind":"Argument","name":{"kind":"Name","value":"appSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"appVersion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appVersion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateOrgDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrg"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrganisation"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteApplication"},"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":"deleteApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"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 CreateEnvDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnv"},"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":"appId"}},"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":"envType"}},"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":"wrappedSeed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"envType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envType"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSalt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateEnvDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnv"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateEnvKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvKey"},"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":"ownerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentKey"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ownerId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSalt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateEnvTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvToken"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecret"},"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":"key"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"keyDigest"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"value"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"Variable","name":{"kind":"Name","value":"key"}}},{"kind":"Argument","name":{"kind":"Name","value":"keyDigest"},"value":{"kind":"Variable","name":{"kind":"Name","value":"keyDigest"}}},{"kind":"Argument","name":{"kind":"Name","value":"value"},"value":{"kind":"Variable","name":{"kind":"Name","value":"value"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} 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 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"}}}}],"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} 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; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index e973b221a..d0a8d190f 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -229,7 +229,7 @@ type Mutation { createApp(appSeed: String!, appToken: String!, appVersion: Int!, id: ID!, identityKey: String!, name: String!, organisationId: ID!, wrappedKeyShare: String!): CreateAppMutation rotateAppKeys(appToken: String!, id: ID!, wrappedKeyShare: String!): RotateAppKeysMutation deleteApp(id: ID!): DeleteAppMutation - createEnvironment(appId: ID!, envType: String!, id: ID!, identityKey: String!, name: String!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentMutation + createEnvironment(environmentData: EnvironmentInput!): CreateEnvironmentMutation createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID!, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation createUserToken(identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation @@ -260,6 +260,16 @@ type CreateEnvironmentMutation { environment: EnvironmentType } +input EnvironmentInput { + id: ID! + appId: ID! + name: String! + envType: String! + wrappedSeed: String! + wrappedSalt: String! + identityKey: String! +} + type CreateEnvironmentKeyMutation { environmentKey: EnvironmentKeyType } diff --git a/frontend/graphql/mutations/environments/createEnvironment.gql b/frontend/graphql/mutations/environments/createEnvironment.gql index 60bf0d8bc..ec020f4a0 100644 --- a/frontend/graphql/mutations/environments/createEnvironment.gql +++ b/frontend/graphql/mutations/environments/createEnvironment.gql @@ -1,6 +1,6 @@ -mutation CreateEnv($id: ID!, $appId: ID!, $name: String!, $envType: String!, $identityKey: String!, $wrappedSeed: String!, $wrappedSalt: String!) { - createEnvironment(id: $id, appId: $appId, name: $name, envType: $envType, identityKey:$identityKey, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt) { +mutation CreateEnv($input: EnvironmentInput!) { + createEnvironment(environmentData: $input) { environment { id name diff --git a/frontend/graphql/mutations/environments/initAppEnvironments.gql b/frontend/graphql/mutations/environments/initAppEnvironments.gql index 228379795..f670ba39d 100644 --- a/frontend/graphql/mutations/environments/initAppEnvironments.gql +++ b/frontend/graphql/mutations/environments/initAppEnvironments.gql @@ -1,20 +1,9 @@ -input CreateEnvironmentInput { - id: ID! - appId: ID! - name: String! - envType: String! - identityKey: String! - wrappedSeed: String! - wrappedSalt: String! -} - - mutation InitAppEnvironments( - $devEnv: CreateEnvironmentInput! - $stagingEnv: CreateEnvironmentInput! - $prodEnv: CreateEnvironmentInput! + $devEnv: EnvironmentInput! + $stagingEnv: EnvironmentInput! + $prodEnv: EnvironmentInput! ) { - devEnvironment: createEnvironment(input: $devEnv) { + devEnvironment: createEnvironment(environmentData: $devEnv) { environment { id name @@ -22,7 +11,7 @@ mutation InitAppEnvironments( identityKey } } - stagingEnvironment: createEnvironment(input: $stagingEnv) { + stagingEnvironment: createEnvironment(environmentData: $stagingEnv) { environment { id name @@ -30,7 +19,7 @@ mutation InitAppEnvironments( identityKey } } - prodEnvironment: createEnvironment(input: $prodEnv) { + prodEnvironment: createEnvironment(environmentData: $prodEnv) { environment { id name From 418c148173b0dfce4b0cae879c6f33bad974f99a Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 20 Aug 2023 18:33:18 +0530 Subject: [PATCH 047/161] feat: setup all three envs for app --- .../backend/graphene/mutations/environment.py | 7 +- frontend/apollo/graphql.ts | 1 - frontend/apollo/schema.graphql | 1 - .../app/[team]/apps/[app]/secrets/page.tsx | 215 +++--------------- frontend/utils/crypto.ts | 24 +- frontend/utils/environments.ts | 51 ++++- 6 files changed, 103 insertions(+), 196 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 283b931a8..b6b183a8f 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -7,7 +7,6 @@ class EnvironmentInput(graphene.InputObjectType): - id = graphene.ID(required=True) app_id = graphene.ID(required=True) name = graphene.String(required=True) env_type = graphene.String(required=True) @@ -31,13 +30,13 @@ def mutate(cls, root, info, environment_data): app = App.objects.get(id=environment_data.app_id) - environment = Environment.objects.create( - id=environment_data.id, app=app, name=environment_data.name, env_type=environment_data.env_type, identity_key=environment_data.identity_key, wrapped_seed=environment_data.wrapped_seed, wrapped_salt=environment_data.wrapped_salt) + environment = Environment.objects.create(app=app, name=environment_data.name, env_type=environment_data.env_type, + identity_key=environment_data.identity_key, wrapped_seed=environment_data.wrapped_seed, wrapped_salt=environment_data.wrapped_salt) org_owner = OrganisationMember.objects.get( organisation=environment.app.organisation, role=OrganisationMember.OWNER) - EnvironmentKey.objects.create(id=id, environment=environment, user=org_owner, + EnvironmentKey.objects.create(environment=environment, user=org_owner, identity_key=environment_data.identity_key, wrapped_seed=environment_data.wrapped_seed, wrapped_salt=environment_data.wrapped_salt) return CreateEnvironmentMutation(environment=environment) diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index f5d513ca0..779d2f3e5 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -150,7 +150,6 @@ export type EditSecretMutation = { export type EnvironmentInput = { appId: Scalars['ID']; envType: Scalars['String']; - id: Scalars['ID']; identityKey: Scalars['String']; name: Scalars['String']; wrappedSalt: Scalars['String']; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index d0a8d190f..9f57dd13c 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -261,7 +261,6 @@ type CreateEnvironmentMutation { } input EnvironmentInput { - id: ID! appId: ID! name: String! envType: String! diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index a48dd4a02..424a992b0 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -6,49 +6,40 @@ import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' import { GetOrganisationAdminsAndSelf } from '@/graphql/queries/organisation/getOrganisationAdminsAndSelf.gql' import { CreateEnv } from '@/graphql/mutations/environments/createEnvironment.gql' import { InitAppEnvironments } from '@/graphql/mutations/environments/initAppEnvironments.gql' -import { CreateEnvKey } from '@/graphql/mutations/environments/createEnvironmentKey.gql' import { CreateEnvToken } from '@/graphql/mutations/environments/createEnvironmentToken.gql' import { CreateNewUserToken } from '@/graphql/mutations/users/createUserToken.gql' import { GetUserTokens } from '@/graphql/queries/users/getUserTokens.gql' import { GetEnvironmentTokens } from '@/graphql/queries/secrets/getEnvironmentTokens.gql' -import { CreateSecret } from '@/graphql/mutations/environments/createSecret.gql' -import { gql, useLazyQuery, useMutation, useQuery } from '@apollo/client' +import { CreateNewSecret } from '@/graphql/mutations/environments/createSecret.gql' +import { useLazyQuery, useMutation, useQuery } from '@apollo/client' import { useEffect, useState } from 'react' -import { copyToClipBoard } from '@/utils/clipboard' -import { toast } from 'react-toastify' import { useSession } from 'next-auth/react' import { - EnvKeyring, + createNewEnvPayload, envKeyring, generateEnvironmentToken, generateUserToken, - newEnvSalt, - newEnvSeed, } from '@/utils/environments' import { Button } from '@/components/common/Button' import { ApiEnvironmentEnvTypeChoices, ApiOrganisationMemberRoleChoices, - EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, - MutationCreateEnvironmentArgs, OrganisationMemberType, SecretType, UserTokenType, } from '@/apollo/graphql' import { - createSealedBox, decryptAsymmetric, digest, encryptAsymmetric, getUserKxPrivateKey, getUserKxPublicKey, - openSealedBox, } from '@/utils/crypto' import { cryptoUtils } from '@/utils/auth' import { getLocalKeyring } from '@/utils/localStorage' -import _sodium, { KeyPair } from 'libsodium-wrappers-sumo' +import _sodium from 'libsodium-wrappers-sumo' export default function Secrets({ params }: { params: { team: string; app: string } }) { const { data } = useQuery(GetAppEnvironments, { @@ -86,127 +77,38 @@ export default function Secrets({ params }: { params: { team: string; app: strin const setupRequired = data?.appEnvironments.length === 0 ?? true - const wrapEnvSecretsForUser = async ( - envSecrets: { seed: string; salt: string }, - user: OrganisationMemberType - ) => { - const userPubKey = await getUserKxPublicKey(user.identityKey!) - const wrappedSeed = await encryptAsymmetric(envSecrets.seed, userPubKey) - const wrappedSalt = await encryptAsymmetric(envSecrets.salt, userPubKey) - - return { - user, - wrappedSeed, - wrappedSalt, - } - } - const initAppEnvs = async () => { const owner = orgAdminsData.organisationAdminsAndSelf.find( (user: OrganisationMemberType) => user.role === ApiOrganisationMemberRoleChoices.Owner ) - const envs = [ - { - name: 'devEnv', - envType: ApiEnvironmentEnvTypeChoices.Dev, - seed: await newEnvSeed(), - salt: await newEnvSalt(), - keys: {}, - }, - { - name: 'stagingEnv', - envType: ApiEnvironmentEnvTypeChoices.Staging, - seed: await newEnvSeed(), - salt: await newEnvSalt(), - keys: { publicKey: '', privateKey: '' }, - }, - { - name: 'prodEnv', - envType: ApiEnvironmentEnvTypeChoices.Prod, - seed: await newEnvSeed(), - salt: await newEnvSalt(), - keys: { publicKey: '', privateKey: '' }, - }, - ] - - let mutationPayload = { - devEnv: {} as MutationCreateEnvironmentArgs, - stagingEnv: {} as MutationCreateEnvironmentArgs, - prodEnv: {} as MutationCreateEnvironmentArgs, + const mutationPayload = { + devEnv: await createNewEnvPayload( + params.app, + 'devEnv', + ApiEnvironmentEnvTypeChoices.Dev, + owner + ), + stagingEnv: await createNewEnvPayload( + params.app, + 'stagingEnv', + ApiEnvironmentEnvTypeChoices.Staging, + owner + ), + prodEnv: await createNewEnvPayload( + params.app, + 'productionEnv', + ApiEnvironmentEnvTypeChoices.Prod, + owner + ), } - envs.forEach(async (env) => { - env.keys = await envKeyring(env.seed) - const { name, envType, seed, salt, keys } = env - - const ownerWrappedEnv = await wrapEnvSecretsForUser({ seed, salt }, owner) - - if (envType === ApiEnvironmentEnvTypeChoices.Dev) { - mutationPayload.devEnv = { - id: crypto.randomUUID(), - appId: params.app, - name, - envType, - wrappedSeed: ownerWrappedEnv.wrappedSeed, - wrappedSalt: ownerWrappedEnv.wrappedSalt, - identityKey: keys.publicKey, - } - } else if (envType === ApiEnvironmentEnvTypeChoices.Staging) { - mutationPayload.stagingEnv = { - id: crypto.randomUUID(), - appId: params.app, - name, - envType, - wrappedSeed: ownerWrappedEnv.wrappedSeed, - wrappedSalt: ownerWrappedEnv.wrappedSalt, - identityKey: keys.publicKey, - } - } else if (envType === ApiEnvironmentEnvTypeChoices.Prod) { - mutationPayload.prodEnv = { - id: crypto.randomUUID(), - appId: params.app, - name, - envType, - wrappedSeed: ownerWrappedEnv.wrappedSeed, - wrappedSalt: ownerWrappedEnv.wrappedSalt, - identityKey: keys.publicKey, - } - } - }) - - console.log(mutationPayload.devEnv) - - await createEnvironment({ - variables: mutationPayload.devEnv, - refetchQueries: [ - { - query: GetAppEnvironments, - variables: { - appId: params.app, - }, - }, - ], - }) - - console.log(mutationPayload.stagingEnv) - - await createEnvironment({ - variables: mutationPayload.stagingEnv, - refetchQueries: [ - { - query: GetAppEnvironments, - variables: { - appId: params.app, - }, - }, - ], - }) - - console.log(mutationPayload.prodEnv) - - await createEnvironment({ - variables: mutationPayload.prodEnv, + await initAppEnvironments({ + variables: { + devEnv: mutationPayload.devEnv, + stagingEnv: mutationPayload.stagingEnv, + prodEnv: mutationPayload.prodEnv, + }, refetchQueries: [ { query: GetAppEnvironments, @@ -216,54 +118,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin }, ], }) - - // await Promise.all([ - // createEnvironment({ - // variables: mutationPayload.devEnv, - // refetchQueries: [ - // { - // query: GetAppEnvironments, - // variables: { - // appId: params.app, - // }, - // }, - // ], - // }), - // createEnvironment({ - // variables: mutationPayload.stagingEnv, - // refetchQueries: [ - // { - // query: GetAppEnvironments, - // variables: { - // appId: params.app, - // }, - // }, - // ], - // }), - // createEnvironment({ - // variables: mutationPayload.prodEnv, - // refetchQueries: [ - // { - // query: GetAppEnvironments, - // variables: { - // appId: params.app, - // }, - // }, - // ], - // }), - // ]) - - // const result = await initAppEnvironments({ - // variables: mutationPayload, - // refetchQueries: [ - // { - // query: GetAppEnvironments, - // variables: { - // appId: params.app, - // }, - // }, - // ], - // }) } const handleCreateNewUserToken = async () => { @@ -312,7 +166,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin const [value, setValue] = useState('') const [envKeys, setEnvKeys] = useState(null) const [envToken, setEnvToken] = useState('') - const [createSecret, { data, loading, error }] = useMutation(CreateSecret) + const [createSecret, { data, loading, error }] = useMutation(CreateNewSecret) const [createEnvironmentToken] = useMutation(CreateEnvToken) const { data: secretsData } = useQuery(GetSecrets, { variables: { @@ -386,10 +240,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin envKeys.privateKey, envKeys.publicKey ) - const box = await createSealedBox(decryptedSecret.value, envKeys.publicKey) - console.log('sealedBox', box) - const openBox = await openSealedBox(box, envKeys.publicKey, envKeys.privateKey) - console.log('openBox', openBox) return decryptedSecret }) ) @@ -547,11 +397,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin {data?.appEnvironments.map((env: EnvironmentType) => ( ))} -
- -
{userTokensData?.userTokens.map((userToken: UserTokenType) => ( diff --git a/frontend/utils/crypto.ts b/frontend/utils/crypto.ts index 2bb43a256..4bb5708b1 100644 --- a/frontend/utils/crypto.ts +++ b/frontend/utils/crypto.ts @@ -2,7 +2,7 @@ import _sodium, { KeyPair } from 'libsodium-wrappers-sumo' import { cryptoUtils } from '@/utils/auth' -const VERSION = 1 +const VERSION = 2 /** * Returns an random key exchange keypair @@ -57,6 +57,28 @@ export const serverSessionKeys = async ( return keys } +export const createSealedBox = async (plaintext: string, publicKey: string) => { + await _sodium.ready + const sodium = _sodium + + const sealedBox = await sodium.crypto_box_seal(plaintext, sodium.from_hex(publicKey)) + return `ph:${VERSION}:${sodium.to_base64(sealedBox)}` +} + +export const openSealedBox = async (ciphertext: string, publicKey: string, privateKey: string) => { + await _sodium.ready + const sodium = _sodium + + const ciphertextSegments = ciphertext.split(':') + + const plaintext = sodium.crypto_box_seal_open( + sodium.from_base64(ciphertextSegments[2]), + sodium.from_hex(publicKey), + sodium.from_hex(privateKey) + ) + return sodium.to_string(plaintext) +} + export const encryptAsymmetric = async (plaintext: string, publicKey: string): Promise => { await _sodium.ready const sodium = _sodium diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index f6ece76c1..be218f7c4 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -1,10 +1,15 @@ import _sodium from 'libsodium-wrappers-sumo' import { cryptoUtils } from '@/utils/auth' import { splitSecret } from './keyshares' -import { EnvironmentKeyType, EnvironmentType } from '@/apollo/graphql' -import { decryptAsymmetric } from './crypto' - -type EnvKeyring = { +import { + ApiEnvironmentEnvTypeChoices, + EnvironmentKeyType, + EnvironmentType, + OrganisationMemberType, +} from '@/apollo/graphql' +import { decryptAsymmetric, encryptAsymmetric, getUserKxPublicKey } from './crypto' + +export type EnvKeyring = { publicKey: string privateKey: string } @@ -177,3 +182,41 @@ export const generateUserToken = async ( mutationPayload, } } + +const wrapEnvSecretsForUser = async ( + envSecrets: { seed: string; salt: string }, + user: OrganisationMemberType +) => { + const userPubKey = await getUserKxPublicKey(user.identityKey!) + const wrappedSeed = await encryptAsymmetric(envSecrets.seed, userPubKey) + const wrappedSalt = await encryptAsymmetric(envSecrets.salt, userPubKey) + + return { + user, + wrappedSeed, + wrappedSalt, + } +} + +export const createNewEnvPayload = async ( + appId: string, + name: string, + envType: ApiEnvironmentEnvTypeChoices, + user: OrganisationMemberType +) => { + const seed = await newEnvSeed() + const keys = await envKeyring(seed) + + const salt = await newEnvSalt() + + const ownerWrappedEnv = await wrapEnvSecretsForUser({ seed, salt }, user) + + return { + appId, + name, + envType, + wrappedSeed: ownerWrappedEnv.wrappedSeed, + wrappedSalt: ownerWrappedEnv.wrappedSalt, + identityKey: keys.publicKey, + } +} From 141283dfa6ad085dfb0789ad43b87d8b00587322 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 20 Aug 2023 18:38:43 +0530 Subject: [PATCH 048/161] fix: default env names --- frontend/app/[team]/apps/[app]/secrets/page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index 424a992b0..fc545ebc9 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -85,19 +85,19 @@ export default function Secrets({ params }: { params: { team: string; app: strin const mutationPayload = { devEnv: await createNewEnvPayload( params.app, - 'devEnv', + 'Development', ApiEnvironmentEnvTypeChoices.Dev, owner ), stagingEnv: await createNewEnvPayload( params.app, - 'stagingEnv', + 'Staging', ApiEnvironmentEnvTypeChoices.Staging, owner ), prodEnv: await createNewEnvPayload( params.app, - 'productionEnv', + 'Production', ApiEnvironmentEnvTypeChoices.Prod, owner ), From 339346c6896f27a6812cd2e8103943fa5be54c1b Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 21 Aug 2023 16:03:56 +0530 Subject: [PATCH 049/161] fix: reset version --- frontend/utils/crypto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/utils/crypto.ts b/frontend/utils/crypto.ts index 4bb5708b1..b9cf06f19 100644 --- a/frontend/utils/crypto.ts +++ b/frontend/utils/crypto.ts @@ -2,7 +2,7 @@ import _sodium, { KeyPair } from 'libsodium-wrappers-sumo' import { cryptoUtils } from '@/utils/auth' -const VERSION = 2 +const VERSION = 1 /** * Returns an random key exchange keypair From 7cc7552cc780bec09d2ddcc5bd90642a318d276e Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Mon, 21 Aug 2023 16:07:28 +0530 Subject: [PATCH 050/161] fix: dont return keys for deleted apps --- backend/api/serializers.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index b846d2699..1ec8e2888 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -60,25 +60,26 @@ class Meta: class UserTokenSerializer(serializers.ModelSerializer): apps = EnvironmentKeySerializer(many=True, read_only=True) - + # New field 'userId' user_id = serializers.UUIDField(source='user.id', read_only=True) - + # New field 'offline_enabled' with default value False offline_enabled = serializers.BooleanField(default=False, read_only=True) - + class Meta: model = UserToken fields = ['wrapped_key_share', 'user_id', 'offline_enabled', 'apps'] def to_representation(self, instance): representation = super().to_representation(instance) - + # Filter environment_keys to include only those associated with the same user user = instance.user if user is not None: - environment_keys = EnvironmentKey.objects.filter(user=user) + environment_keys = EnvironmentKey.objects.filter( + user=user, environment__app__deleted_at=None) apps = [] for key in environment_keys: @@ -93,7 +94,7 @@ def to_representation(self, instance): }) else: apps[index]['environment_keys'].append(serializer.data) - + representation['apps'] = apps - return representation \ No newline at end of file + return representation From 23dcdce15cf0c3170d14816a4218e2431084f8e0 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 22 Aug 2023 13:41:55 +0530 Subject: [PATCH 051/161] feat: user keyring context --- .../app/[team]/apps/[app]/secrets/page.tsx | 197 +++++++++--------- frontend/app/providers.tsx | 5 +- .../components/auth/SudoPasswordDialog.tsx | 119 +++++++++++ frontend/contexts/keyringContext.tsx | 24 +++ frontend/utils/auth.ts | 91 ++++---- 5 files changed, 282 insertions(+), 154 deletions(-) create mode 100644 frontend/components/auth/SudoPasswordDialog.tsx create mode 100644 frontend/contexts/keyringContext.tsx diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index fc545ebc9..e78b09d1b 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -12,7 +12,7 @@ import { GetUserTokens } from '@/graphql/queries/users/getUserTokens.gql' import { GetEnvironmentTokens } from '@/graphql/queries/secrets/getEnvironmentTokens.gql' import { CreateNewSecret } from '@/graphql/mutations/environments/createSecret.gql' import { useLazyQuery, useMutation, useQuery } from '@apollo/client' -import { useEffect, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { useSession } from 'next-auth/react' import { createNewEnvPayload, @@ -40,6 +40,8 @@ import { import { cryptoUtils } from '@/utils/auth' import { getLocalKeyring } from '@/utils/localStorage' import _sodium from 'libsodium-wrappers-sumo' +import { KeyringContext } from '@/contexts/keyringContext' +import SudoPasswordDialog from '@/components/auth/SudoPasswordDialog' export default function Secrets({ params }: { params: { team: string; app: string } }) { const { data } = useQuery(GetAppEnvironments, { @@ -59,6 +61,13 @@ export default function Secrets({ params }: { params: { team: string; app: strin const { data: session } = useSession() + const { keyring, setKeyring } = useContext(KeyringContext) + + useEffect(() => { + if (keyring === null) { + } + }) + useEffect(() => { if (orgsData) { const organisationId = orgsData.organisations[0].id @@ -121,36 +130,33 @@ export default function Secrets({ params }: { params: { team: string; app: strin } const handleCreateNewUserToken = async () => { - const sudoPass = 'testpassword1234' - const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) - const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) - if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' - const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) - if (!decryptedKeyring) throw 'Failed to decrypt keys' - - const userKxKeys = { - publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), - privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), - } + if (keyring) { + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring.publicKey), + privateKey: await getUserKxPrivateKey(keyring.privateKey), + } - const { pssUser, mutationPayload } = await generateUserToken( - orgsData.organisations[0].id, - userKxKeys - ) + const { pssUser, mutationPayload } = await generateUserToken( + orgsData.organisations[0].id, + userKxKeys + ) - await createUserToken({ - variables: mutationPayload, - refetchQueries: [ - { - query: GetUserTokens, - variables: { - organisationId: orgsData.organisations[0].id, + await createUserToken({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetUserTokens, + variables: { + organisationId: orgsData.organisations[0].id, + }, }, - }, - ], - }) + ], + }) - setUserToken(pssUser) + setUserToken(pssUser) + } else { + console.log('keyring unavailable') + } } const EnvironmentCard = (props: { environment: EnvironmentType }) => { @@ -182,24 +188,11 @@ export default function Secrets({ params }: { params: { team: string; app: strin useEffect(() => { const initEnvKeys = async () => { - await _sodium.ready - const sodium = _sodium - - const sudoPass = 'testpassword1234' - const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) - const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) - if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' - const decryptedKeyring = await cryptoUtils.decryptAccountKeyring( - encryptedKeyring!, - deviceKey - ) - if (!decryptedKeyring) throw 'Failed to decrypt keys' - const wrappedSeed = secretsData.environmentKeys[0].wrappedSeed const userKxKeys = { - publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), - privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), + publicKey: await getUserKxPublicKey(keyring!.publicKey), + privateKey: await getUserKxPrivateKey(keyring!.privateKey), } const seed = await decryptAsymmetric( wrappedSeed, @@ -221,8 +214,8 @@ export default function Secrets({ params }: { params: { team: string; app: strin }) } - if (secretsData) initEnvKeys() - }, [secretsData]) + if (secretsData && keyring) initEnvKeys() + }, [secretsData, keyring]) useEffect(() => { if (secretsData && envKeys) { @@ -287,41 +280,34 @@ export default function Secrets({ params }: { params: { team: string; app: strin } const handleCreateNewEnvToken = async () => { - const sudoPass = 'testpassword1234' - const deviceKey = await cryptoUtils.deviceVaultKey(sudoPass, session?.user?.email!) - const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) - if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' - const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) - if (!decryptedKeyring) throw 'Failed to decrypt keys' - - const userKxKeys = { - publicKey: await getUserKxPublicKey(decryptedKeyring.publicKey), - privateKey: await getUserKxPrivateKey(decryptedKeyring.privateKey), - } - - const { pssEnv, mutationPayload } = await generateEnvironmentToken( - environment, - secretsData.environmentKeys[0], - userKxKeys - ) - - const { pssUser } = await generateUserToken(orgsData.organisations[0].id, userKxKeys) + if (keyring) { + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring.publicKey), + privateKey: await getUserKxPrivateKey(keyring.privateKey), + } - console.log('user token', pssUser) + const { pssEnv, mutationPayload } = await generateEnvironmentToken( + environment, + secretsData.environmentKeys[0], + userKxKeys + ) - await createEnvironmentToken({ - variables: mutationPayload, - refetchQueries: [ - { - query: GetEnvironmentTokens, - variables: { - envId: environment.id, + await createEnvironmentToken({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetEnvironmentTokens, + variables: { + envId: environment.id, + }, }, - }, - ], - }) + ], + }) - setEnvToken(pssEnv) + setEnvToken(pssEnv) + } else { + console.log('keyring unavailable') + } } return ( @@ -382,38 +368,43 @@ export default function Secrets({ params }: { params: { team: string; app: strin return (
-
- {setupRequired ? ( -
-

- {"You don't have any environments for this app yet"} -

- -
- ) : ( -
- {data?.appEnvironments.map((env: EnvironmentType) => ( - - ))} + {orgsData?.organisations && ( + + )} + {keyring !== null && ( +
+ {setupRequired ? ( +
+

+ {"You don't have any environments for this app yet"} +

+ +
+ ) : ( +
+ {data?.appEnvironments.map((env: EnvironmentType) => ( + + ))} -
- {userTokensData?.userTokens.map((userToken: UserTokenType) => ( -
- {userToken.name} | {userToken.createdAt} +
+ {userTokensData?.userTokens.map((userToken: UserTokenType) => ( +
+ {userToken.name} | {userToken.createdAt} +
+ ))} + {userToken} +
+
- ))} - {userToken} -
-
-
- )} -
+ )} +
+ )}
) } diff --git a/frontend/app/providers.tsx b/frontend/app/providers.tsx index 3e94646a6..0eb28a01c 100644 --- a/frontend/app/providers.tsx +++ b/frontend/app/providers.tsx @@ -4,12 +4,15 @@ import { ThemeProvider } from '@/contexts/themeContext' import { SessionProvider } from 'next-auth/react' import { ApolloProvider } from '@apollo/client' import { graphQlClient } from '@/apollo/client' +import { KeyringProvider } from '@/contexts/keyringContext' export default function Providers({ children }: { children: React.ReactNode }) { return ( - {children} + + {children} + ) diff --git a/frontend/components/auth/SudoPasswordDialog.tsx b/frontend/components/auth/SudoPasswordDialog.tsx new file mode 100644 index 000000000..a20a0b03f --- /dev/null +++ b/frontend/components/auth/SudoPasswordDialog.tsx @@ -0,0 +1,119 @@ +import { Dialog, Transition } from '@headlessui/react' +import { Fragment, useContext, useEffect, useState } from 'react' +import { FaEye, FaEyeSlash, FaTimes } from 'react-icons/fa' +import { Button } from '../common/Button' +import { KeyringContext } from '@/contexts/keyringContext' +import { cryptoUtils } from '@/utils/auth' +import { getLocalKeyring } from '@/utils/localStorage' +import { useSession } from 'next-auth/react' + +export default function SudoPasswordDialog(props: { organisationId: string }) { + const [password, setPassword] = useState('') + const [showPw, setShowPw] = useState(false) + const [isOpen, setIsOpen] = useState(false) + const { keyring, setKeyring } = useContext(KeyringContext) + const { data: session } = useSession() + + useEffect(() => { + if (keyring === null) openModal() + }) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const decryptLocalKeyring = async (event: { preventDefault: () => void }) => { + event.preventDefault() + const encryptedKeyring = getLocalKeyring(props.organisationId) + if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' + const deviceKey = await cryptoUtils.deviceVaultKey(password, session?.user?.email!) + const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) + if (!decryptedKeyring) throw 'Failed to decrypt keys' + setKeyring(decryptedKeyring) + closeModal() + } + + return ( + <> + + {}}> + +
+ + +
+
+ + + +

+ Unlock User Keyring +

+ +
+
+
+ +
+ setPassword(e.target.value)} + type={showPw ? 'text' : 'password'} + minLength={16} + required + autoFocus + className="w-full " + /> + +
+
+ +
+
+
+
+
+
+
+
+
+ + ) +} diff --git a/frontend/contexts/keyringContext.tsx b/frontend/contexts/keyringContext.tsx new file mode 100644 index 000000000..57a281e67 --- /dev/null +++ b/frontend/contexts/keyringContext.tsx @@ -0,0 +1,24 @@ +import { OrganisationKeyring } from '@/utils/auth' +import { createContext, useState } from 'react' + +interface KeyringContextValue { + keyring: OrganisationKeyring | null + setKeyring: (keyring: OrganisationKeyring) => void +} + +export const KeyringContext = createContext({ + keyring: null, + setKeyring: () => {}, +}) + +interface KeyringProviderProps { + children: React.ReactNode +} + +export const KeyringProvider: React.FC = ({ children }) => { + const [keyring, setKeyring] = useState(null) + + return ( + {children} + ) +} diff --git a/frontend/utils/auth.ts b/frontend/utils/auth.ts index f6bb21e8b..ced17bdf7 100644 --- a/frontend/utils/auth.ts +++ b/frontend/utils/auth.ts @@ -1,6 +1,17 @@ import jwt from 'jsonwebtoken' import _sodium from 'libsodium-wrappers-sumo' +export type OrganisationKeyring = { + symmetricKey: string + publicKey: string + privateKey: string +} + +type AppKeyring = { + publicKey: string + privateKey: string +} + export namespace JwtUtils { export const isJwtExpired = (token: string) => { // offset by 60 seconds, so we will check if the token is "almost expired". @@ -43,23 +54,6 @@ export namespace UrlUtils { } export namespace cryptoUtils { - type OrganisationKeyring = { - symmetricKey: string - publicKey: string - privateKey: string - } - - type AppKeyring = { - publicKey: string - privateKey: string - } - - type ShamirShare = { - bits: number - id: number - data: string - } - /** * Returns 16 bytes from an input string that can be used as a salt for Argon2 * @@ -127,40 +121,37 @@ export namespace cryptoUtils { } /** - * Encrypts a single string with the given key. Returns the ciphertext as a base64 string - * - * @param {string} plaintext - Plaintext string to encrypt - * @param {Uint8Array} key - Symmetric encryption key - * @returns {string} - */ -export const encryptString = async (plaintext: string, key: Uint8Array) => { - await _sodium.ready; - const sodium = _sodium; - - return sodium.to_base64( - await encryptRaw(sodium.from_string(plaintext), key), - sodium.base64_variants.ORIGINAL - ); -}; - -/** - * Decrypts a single base64 ciphertext string with the given key. Returns the plaintext as a string - * - * @param cipherText - base64 string ciphertext with appended nonce - * @param key - Symmetric encryption key - * @returns {string} - */ -export const decryptString = async (cipherText: string, key: Uint8Array) => { - await _sodium.ready; - const sodium = _sodium; - - return sodium.to_string( - await decryptRaw( - sodium.from_base64(cipherText, sodium.base64_variants.ORIGINAL), - key + * Encrypts a single string with the given key. Returns the ciphertext as a base64 string + * + * @param {string} plaintext - Plaintext string to encrypt + * @param {Uint8Array} key - Symmetric encryption key + * @returns {string} + */ + export const encryptString = async (plaintext: string, key: Uint8Array) => { + await _sodium.ready + const sodium = _sodium + + return sodium.to_base64( + await encryptRaw(sodium.from_string(plaintext), key), + sodium.base64_variants.ORIGINAL ) - ); -}; + } + + /** + * Decrypts a single base64 ciphertext string with the given key. Returns the plaintext as a string + * + * @param cipherText - base64 string ciphertext with appended nonce + * @param key - Symmetric encryption key + * @returns {string} + */ + export const decryptString = async (cipherText: string, key: Uint8Array) => { + await _sodium.ready + const sodium = _sodium + + return sodium.to_string( + await decryptRaw(sodium.from_base64(cipherText, sodium.base64_variants.ORIGINAL), key) + ) + } /** * Computes the account recovery key from the mnemonic phrase and orgId. From 8458fa23f8086aa7bf719733cc3cd51d123361ff Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 24 Aug 2023 13:53:02 +0530 Subject: [PATCH 052/161] fix: check that all secrets to be deleted exist --- backend/api/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/api/views.py b/backend/api/views.py index 44caf625b..2f2c554d6 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -395,6 +395,8 @@ def delete(self, request): for secret in secrets_to_delete: if not user_can_access_environment(user.user.userId, secret.environment.id): return HttpResponse(status=403) + if not Secret.objects.filter(id=secret.id).exists(): + return HttpResponse(status=404) for secret in secrets_to_delete: secret.updated_at = timezone.now() From e06e919e49318da0be306526742ebf20ab676136 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 24 Aug 2023 13:59:50 +0530 Subject: [PATCH 053/161] fix: order of checks --- backend/api/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 2f2c554d6..ce4d1c5f7 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -393,11 +393,12 @@ def delete(self, request): id__in=request_body['secrets']) for secret in secrets_to_delete: - if not user_can_access_environment(user.user.userId, secret.environment.id): - return HttpResponse(status=403) if not Secret.objects.filter(id=secret.id).exists(): return HttpResponse(status=404) + if not user_can_access_environment(user.user.userId, secret.environment.id): + return HttpResponse(status=403) + for secret in secrets_to_delete: secret.updated_at = timezone.now() secret.deleted_at = timezone.now() From c8dcb808d75befc5285b49d7ccbc4a6b201501c3 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:05:27 +0530 Subject: [PATCH 054/161] fix: allow querying specific environment --- backend/backend/schema.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 9079c0569..55912faa7 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -32,7 +32,8 @@ class Query(graphene.ObjectType): app_activity_chart = graphene.List(ChartDataPointType, app_id=graphene.ID( ), period=graphene.Argument(graphene.Enum.from_enum(TimeRange))) - app_environments = graphene.List(EnvironmentType, app_id=graphene.ID()) + app_environments = graphene.List(EnvironmentType, app_id=graphene.ID( + ), environment_id=graphene.ID(required=False)) secrets = graphene.List(SecretType, env_id=graphene.ID()) secret_history = graphene.List(SecretEventType, secret_id=graphene.ID()) secret_tags = graphene.List(SecretTagType, org_id=graphene.ID()) @@ -79,7 +80,7 @@ def resolve_apps(root, info, organisation_id, app_id): filter['id'] = app_id return App.objects.filter(**filter) - def resolve_app_environments(root, info, app_id): + def resolve_app_environments(root, info, app_id, environment_id): if not user_can_access_app(info.context.user.userId, app_id): raise GraphQLError("You don't have access to this app") @@ -88,14 +89,21 @@ def resolve_app_environments(root, info, app_id): org_member = OrganisationMember.objects.get( organisation=app.organisation, user_id=info.context.user.userId) - app_environments = Environment.objects.filter(app_id=app_id) + filter = { + 'app_id': app_id + } + + if environment_id: + filter['id'] = environment_id + + app_environments = Environment.objects.filter(**filter) return [app_env for app_env in app_environments if EnvironmentKey.objects.filter(user=org_member, environment_id=app_env.id).exists()] def resolve_secrets(root, info, env_id): if not user_can_access_environment(info.context.user.userId, env_id): raise GraphQLError("You don't have access to this environment") - return Secret.objects.filter(environment_id=env_id, deleted_at=None) + return Secret.objects.filter(environment_id=env_id, deleted_at=None).order_by('created_at') def resolve_secret_history(root, info, secret_id): secret = Secret.objects.get(id=secret_id) From a0ab1d429c3b1d92d12706c26425d8f96698cf08 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:05:50 +0530 Subject: [PATCH 055/161] fix: misc fixes for secret mutations --- .../backend/graphene/mutations/environment.py | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index b6b183a8f..6415da8f3 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -15,6 +15,16 @@ class EnvironmentInput(graphene.InputObjectType): identity_key = graphene.String(required=True) +class SecretInput(graphene.InputObjectType): + env_id = graphene.ID(required=False) + folder_id = graphene.ID(required=False) + key = graphene.String(required=True) + key_digest = graphene.String(required=True) + value = graphene.String(required=True) + tags = graphene.List(graphene.String) + comment = graphene.String() + + class CreateEnvironmentMutation(graphene.Mutation): class Arguments: environment_data = EnvironmentInput(required=True) @@ -173,45 +183,39 @@ def mutate(cls, root, info, id, org_id, name): class CreateSecretMutation(graphene.Mutation): class Arguments: - env_id = graphene.ID(required=True) - folder_id = graphene.ID(required=False) - key = graphene.String(required=True) - key_digest = graphene.String(required=True) - value = graphene.String(required=True) - tags = graphene.List(graphene.String) - comment = graphene.String() + secret_data = SecretInput(SecretInput) secret = graphene.Field(SecretType) @classmethod - def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags=[], comment=''): - env = Environment.objects.get(id=env_id) + def mutate(cls, root, info, secret_data): + env = Environment.objects.get(id=secret_data.env_id) org = env.app.organisation if not user_is_org_member(info.context.user.userId, org.id): raise GraphQLError( "You don't have permission to perform this action") tag_names = SecretTag.objects.filter( - id__in=tags).values_list('name', flat=True) + id__in=secret_data.tags).values_list('name', flat=True) - secret_data = { + secret_obj_data = { 'environment_id': env.id, - 'folder_id': folder_id, - 'key': key, - 'key_digest': key_digest, - 'value': value, + 'folder_id': secret_data.folder_id, + 'key': secret_data.key, + 'key_digest': secret_data.key_digest, + 'value': secret_data.value, 'version': 1, 'tags': [], - 'comment': comment + 'comment': secret_data.comment } - secret = Secret.objects.create(**secret_data) + secret = Secret.objects.create(**secret_obj_data) org_member = OrganisationMember.objects.get( user=info.context.user, organisation=org) SecretEvent.objects.create( - **{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + **{**secret_obj_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) return CreateSecretMutation(secret=secret) @@ -219,17 +223,12 @@ def mutate(cls, root, info, env_id, key, key_digest, value, folder_id=None, tags class EditSecretMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) - folder_id = graphene.ID(required=False) - key = graphene.String(required=True) - key_digest = graphene.String(required=True) - value = graphene.String(required=True) - tags = graphene.List(graphene.String) - comment = graphene.String() + secret_data = SecretInput(SecretInput) secret = graphene.Field(SecretType) @classmethod - def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment): + def mutate(cls, root, info, id, secret_data): secret = Secret.objects.get(id=id) env = secret.environment org = env.app.organisation @@ -238,20 +237,19 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment "You don't have permission to perform this action") tag_names = SecretTag.objects.filter( - id__in=tags).values_list('name', flat=True) + id__in=secret_data.tags).values_list('name', flat=True) - secret_data = { - 'environment_id': env.id, - 'folder_id': folder_id, - 'key': key, - 'key_digest': key_digest, - 'value': value, + secret_obj_data = { + 'folder_id': secret_data.folder_id, + 'key': secret_data.key, + 'key_digest': secret_data.key_digest, + 'value': secret_data.value, 'version': secret.version + 1, 'tags': [], - 'comment': comment + 'comment': secret_data.comment } - for key, value in secret_data.items(): + for key, value in secret_obj_data.items(): setattr(secret, key, value) secret.updated_at = timezone.now() @@ -261,7 +259,7 @@ def mutate(cls, root, info, id, folder_id, key, key_digest, value, tags, comment user=info.context.user, organisation=org) SecretEvent.objects.create( - **{**secret_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) + **{**secret_obj_data, **{'user': org_member, 'environment': env, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) return EditSecretMutation(secret=secret) From f5374285d41e9fabf04f3314f85db7eb7a70e95b Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:06:05 +0530 Subject: [PATCH 056/161] chore: regenerate types and schema --- frontend/apollo/gql.ts | 27 ++++++++++---- frontend/apollo/graphql.ts | 65 +++++++++++++++++++++++----------- frontend/apollo/schema.graphql | 16 +++++++-- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 0ab86ddd5..588bab1e2 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -19,7 +19,9 @@ const documents = { "mutation CreateEnv($input: EnvironmentInput!) {\n createEnvironment(environmentData: $input) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.CreateEnvDocument, "mutation CreateEnvKey($envId: ID!, $ownerId: ID!, $wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) {\n createEnvironmentKey(\n envId: $envId\n userId: $ownerId\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n identityKey: $identityKey\n ) {\n environmentKey {\n id\n createdAt\n }\n }\n}": types.CreateEnvKeyDocument, "mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\n }\n }\n}": types.CreateEnvTokenDocument, - "mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}": types.CreateNewSecretDocument, + "mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}": types.CreateNewSecretDocument, + "mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\n id\n }\n }\n}": types.DeleteSecretOpDocument, + "mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}": types.UpdateSecretDocument, "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, "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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}": types.CreateNewUserTokenDocument, @@ -31,10 +33,11 @@ const documents = { "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n }\n}": types.GetOrganisationsDocument, "query GetOrganisationAdminsAndSelf($organisationId: ID!) {\n organisationAdminsAndSelf(organisationId: $organisationId) {\n id\n role\n identityKey\n }\n}": types.GetOrganisationAdminsAndSelfDocument, "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}": types.GetOrganisationMembersDocument, - "query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}": types.GetAppEnvironmentsDocument, + "query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId, environmentId: null) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}": types.GetAppEnvironmentsDocument, "query GetEnvironmentKey($envId: ID!) {\n environmentKeys(environmentId: $envId) {\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 GetSecrets($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n createdAt\n history {\n id\n key\n value\n timestamp\n eventType\n }\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, + "query GetSecretNames($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretNamesDocument, + "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags\n comment\n createdAt\n history {\n id\n key\n value\n tags\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetUserTokensDocument, }; @@ -79,7 +82,15 @@ export function graphql(source: "mutation CreateEnvToken($envId: ID!, $name: Str /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) {\n createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}"]; +export function graphql(source: "mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\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 DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\n id\n }\n }\n}"): (typeof documents)["mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\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 UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}"): (typeof documents)["mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -127,7 +138,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 GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}"): (typeof documents)["query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}"]; +export function graphql(source: "query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId, environmentId: null) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}"): (typeof documents)["query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId, environmentId: null) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -139,7 +150,11 @@ export function graphql(source: "query GetEnvironmentTokens($envId: ID!) {\n en /** * 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($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n createdAt\n history {\n id\n key\n value\n timestamp\n eventType\n }\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n createdAt\n history {\n id\n key\n value\n timestamp\n eventType\n }\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"]; +export function graphql(source: "query GetSecretNames($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecretNames($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags\n comment\n createdAt\n history {\n id\n key\n value\n tags\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags\n comment\n createdAt\n history {\n id\n key\n value\n tags\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 779d2f3e5..49ccfc02d 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -268,13 +268,7 @@ export type MutationCreateOrganisationArgs = { export type MutationCreateSecretArgs = { - comment?: InputMaybe; - envId: Scalars['ID']; - folderId?: InputMaybe; - key: Scalars['String']; - keyDigest: Scalars['String']; - tags?: InputMaybe>>; - value: Scalars['String']; + secretData?: InputMaybe; }; @@ -313,13 +307,8 @@ export type MutationDeleteSecretArgs = { export type MutationEditSecretArgs = { - comment?: InputMaybe; - folderId?: InputMaybe; id: Scalars['ID']; - key: Scalars['String']; - keyDigest: Scalars['String']; - tags?: InputMaybe>>; - value: Scalars['String']; + secretData?: InputMaybe; }; @@ -381,6 +370,7 @@ export type QueryAppActivityChartArgs = { export type QueryAppEnvironmentsArgs = { appId?: InputMaybe; + environmentId?: InputMaybe; }; @@ -470,6 +460,16 @@ export type SecretFolderType = { updatedAt: Scalars['DateTime']; }; +export type SecretInput = { + comment?: InputMaybe; + envId?: InputMaybe; + folderId?: InputMaybe; + key: Scalars['String']; + keyDigest: Scalars['String']; + tags?: InputMaybe>>; + value: Scalars['String']; +}; + export type SecretTagType = { __typename?: 'SecretTagType'; id: Scalars['String']; @@ -571,15 +571,27 @@ export type CreateEnvTokenMutationVariables = Exact<{ export type CreateEnvTokenMutation = { __typename?: 'Mutation', createEnvironmentToken?: { __typename?: 'CreateEnvironmentTokenMutation', environmentToken?: { __typename?: 'EnvironmentTokenType', id: string, createdAt?: any | null } | null } | null }; export type CreateNewSecretMutationVariables = Exact<{ - envId: Scalars['ID']; - key: Scalars['String']; - keyDigest: Scalars['String']; - value: Scalars['String']; + newSecret: SecretInput; }>; export type CreateNewSecretMutation = { __typename?: 'Mutation', createSecret?: { __typename?: 'CreateSecretMutation', secret?: { __typename?: 'SecretType', id: string, key: string, value: string, createdAt?: any | null } | null } | null }; +export type DeleteSecretOpMutationVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type DeleteSecretOpMutation = { __typename?: 'Mutation', deleteSecret?: { __typename?: 'DeleteSecretMutation', secret?: { __typename?: 'SecretType', id: string } | null } | null }; + +export type UpdateSecretMutationVariables = Exact<{ + id: Scalars['ID']; + secretData: SecretInput; +}>; + + +export type UpdateSecretMutation = { __typename?: 'Mutation', editSecret?: { __typename?: 'EditSecretMutation', secret?: { __typename?: 'SecretType', id: string, updatedAt: any } | null } | null }; + export type InitAppEnvironmentsMutationVariables = Exact<{ devEnv: EnvironmentInput; stagingEnv: EnvironmentInput; @@ -691,12 +703,20 @@ export type GetEnvironmentTokensQueryVariables = Exact<{ export type GetEnvironmentTokensQuery = { __typename?: 'Query', environmentTokens?: Array<{ __typename?: 'EnvironmentTokenType', id: string, name: string, wrappedKeyShare: string, createdAt?: any | null } | null> | null }; +export type GetSecretNamesQueryVariables = Exact<{ + envId: Scalars['ID']; +}>; + + +export type GetSecretNamesQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; + export type GetSecretsQueryVariables = Exact<{ + appId: Scalars['ID']; envId: Scalars['ID']; }>; -export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, createdAt?: any | null, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices } | null> | null } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; +export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, tags: Array, comment: string, createdAt?: any | null, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, tags: Array, version: number, comment: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices } | null> | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; export type GetUserTokensQueryVariables = Exact<{ organisationId: Scalars['ID']; @@ -712,7 +732,9 @@ export const DeleteApplicationDocument = {"kind":"Document","definitions":[{"kin export const CreateEnvDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnv"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateEnvKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvKey"},"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":"ownerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentKey"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ownerId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSalt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateEnvTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvToken"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecret"},"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":"key"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"keyDigest"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"value"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"Variable","name":{"kind":"Name","value":"key"}}},{"kind":"Argument","name":{"kind":"Name","value":"keyDigest"},"value":{"kind":"Variable","name":{"kind":"Name","value":"keyDigest"}}},{"kind":"Argument","name":{"kind":"Name","value":"value"},"value":{"kind":"Variable","name":{"kind":"Name","value":"value"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecret"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"newSecret"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"newSecret"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteSecretOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSecretOp"},"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":"deleteSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpdateSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSecret"},"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":"secretData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} 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 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"}}}}],"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -724,8 +746,9 @@ export const GetAppsDocument = {"kind":"Document","definitions":[{"kind":"Operat 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"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationAdminsAndSelfDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationAdminsAndSelf"},"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":"organisationAdminsAndSelf"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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"}}}}],"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"}}}],"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"}}]}}]}}]} 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"}}}}],"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"}}],"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"}}]}}]}}]} 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"}}}}],"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"}}}],"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 GetSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecrets"},"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":"secrets"},"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":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"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":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}}]}}]}},{"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 GetSecretNamesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretNames"},"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":"secrets"},"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":"key"}}]}},{"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 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"}}}}],"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"}}}],"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":"tags"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"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":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}},{"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":"eventType"}}]}}]}},{"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":"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 GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 9f57dd13c..d0bb5c577 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -6,7 +6,7 @@ type Query { logs(appId: ID, start: BigInt, end: BigInt): [KMSLogType] logsCount(appId: ID, thisMonth: Boolean): Int appActivityChart(appId: ID, period: TimeRange): [ChartDataPointType] - appEnvironments(appId: ID): [EnvironmentType] + appEnvironments(appId: ID, environmentId: ID): [EnvironmentType] secrets(envId: ID): [SecretType] secretHistory(secretId: ID): [SecretEventType] secretTags(orgId: ID): [SecretTagType] @@ -235,8 +235,8 @@ type Mutation { createUserToken(identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation createSecretTag(id: ID!, name: String!, orgId: ID!): CreateSecretTagMutation - createSecret(comment: String, envId: ID!, folderId: ID, key: String!, keyDigest: String!, tags: [String], value: String!): CreateSecretMutation - editSecret(comment: String, folderId: ID, id: ID!, key: String!, keyDigest: String!, tags: [String], value: String!): EditSecretMutation + createSecret(secretData: SecretInput): CreateSecretMutation + editSecret(id: ID!, secretData: SecretInput): EditSecretMutation deleteSecret(id: ID!): DeleteSecretMutation } @@ -293,6 +293,16 @@ type CreateSecretMutation { secret: SecretType } +input SecretInput { + envId: ID + folderId: ID + key: String! + keyDigest: String! + value: String! + tags: [String] + comment: String +} + type EditSecretMutation { secret: SecretType } From bc5fba3dedd4cc939b2fd02786aeffa2a3a1d625 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:06:57 +0530 Subject: [PATCH 057/161] fix: update default input style --- frontend/app/globals.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/globals.css b/frontend/app/globals.css index ec6a3eb82..7f5e6e8e0 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -109,7 +109,7 @@ a { @layer components { input { - @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-black dark:text-white border border-zinc-300 dark:border-none focus:outline outline-emerald-500; + @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-black dark:text-white focus:outline-none focus:ring-1 focus:ring-emerald-500 focus:ring-inset; } textarea { From a408731957522ae2b5ed889eea7798399de8b195 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:07:26 +0530 Subject: [PATCH 058/161] feat: add some utils --- frontend/utils/environments.ts | 86 +++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index be218f7c4..c3b2ab485 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -1,13 +1,19 @@ import _sodium from 'libsodium-wrappers-sumo' -import { cryptoUtils } from '@/utils/auth' +import { OrganisationKeyring, cryptoUtils } from '@/utils/auth' import { splitSecret } from './keyshares' import { ApiEnvironmentEnvTypeChoices, EnvironmentKeyType, EnvironmentType, OrganisationMemberType, + SecretType, } from '@/apollo/graphql' -import { decryptAsymmetric, encryptAsymmetric, getUserKxPublicKey } from './crypto' +import { + decryptAsymmetric, + encryptAsymmetric, + getUserKxPrivateKey, + getUserKxPublicKey, +} from './crypto' export type EnvKeyring = { publicKey: string @@ -198,6 +204,70 @@ const wrapEnvSecretsForUser = async ( } } +export const unwrapEnvSecretsForUser = async ( + wrappedSeed: string, + wrappedSalt: string, + keyring: OrganisationKeyring +) => { + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring!.publicKey), + privateKey: await getUserKxPrivateKey(keyring!.privateKey), + } + const seed = await decryptAsymmetric(wrappedSeed, userKxKeys.privateKey, userKxKeys.publicKey) + + const salt = await decryptAsymmetric(wrappedSalt, userKxKeys.privateKey, userKxKeys.publicKey) + + const { publicKey, privateKey } = await envKeyring(seed) + + return { + publicKey, + privateKey, + salt, + } +} + +export const decryptEnvSecretNames = async ( + encryptedSecrets: SecretType[], + envKeys: { publicKey: string; privateKey: string } +) => { + const decryptedSecrets = await Promise.all( + encryptedSecrets.map(async (secret: SecretType) => { + const decryptedSecret = structuredClone(secret) + decryptedSecret.key = await decryptAsymmetric( + secret.key, + envKeys?.privateKey, + envKeys?.publicKey + ) + + return decryptedSecret + }) + ) + return decryptedSecrets +} + +export const decryptEnvSecrets = async ( + encryptedSecrets: SecretType[], + envKeys: { publicKey: string; privateKey: string } +) => { + const decryptedSecrets = await Promise.all( + encryptedSecrets.map(async (secret: SecretType) => { + const decryptedSecret = structuredClone(secret) + decryptedSecret.key = await decryptAsymmetric( + secret.key, + envKeys?.privateKey, + envKeys?.publicKey + ) + decryptedSecret.value = await decryptAsymmetric( + secret.value, + envKeys.privateKey, + envKeys.publicKey + ) + return decryptedSecret + }) + ) + return decryptedSecrets +} + export const createNewEnvPayload = async ( appId: string, name: string, @@ -220,3 +290,15 @@ export const createNewEnvPayload = async ( identityKey: keys.publicKey, } } + +export const arraysEqual = (arr1: any[], arr2: any[]) => { + if (arr1.length !== arr2.length) { + return false + } + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) { + return false + } + } + return true +} From aa36c498c5519478c3dfbbcc917285c2c725bcfd Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:07:47 +0530 Subject: [PATCH 059/161] feat: add queries and mutations --- .../graphql/mutations/environments/createSecret.gql | 4 ++-- .../graphql/mutations/environments/deleteSecret.gql | 7 +++++++ .../graphql/mutations/environments/editSecret.gql | 8 ++++++++ .../graphql/queries/secrets/getAppEnvironments.gql | 2 +- frontend/graphql/queries/secrets/getSecretNames.gql | 12 ++++++++++++ frontend/graphql/queries/secrets/getSecrets.gql | 13 ++++++++++++- 6 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 frontend/graphql/mutations/environments/deleteSecret.gql create mode 100644 frontend/graphql/mutations/environments/editSecret.gql create mode 100644 frontend/graphql/queries/secrets/getSecretNames.gql diff --git a/frontend/graphql/mutations/environments/createSecret.gql b/frontend/graphql/mutations/environments/createSecret.gql index 17ef1389c..ab9d0b450 100644 --- a/frontend/graphql/mutations/environments/createSecret.gql +++ b/frontend/graphql/mutations/environments/createSecret.gql @@ -1,5 +1,5 @@ -mutation CreateNewSecret($envId: ID!, $key: String!, $keyDigest: String!, $value: String!) { - createSecret(envId: $envId, key: $key, keyDigest: $keyDigest, value: $value) { +mutation CreateNewSecret($newSecret: SecretInput!) { + createSecret(secretData: $newSecret) { secret { id key diff --git a/frontend/graphql/mutations/environments/deleteSecret.gql b/frontend/graphql/mutations/environments/deleteSecret.gql new file mode 100644 index 000000000..dcc5032f3 --- /dev/null +++ b/frontend/graphql/mutations/environments/deleteSecret.gql @@ -0,0 +1,7 @@ +mutation DeleteSecretOp($id: ID!) { + deleteSecret(id: $id) { + secret { + id + } + } +} diff --git a/frontend/graphql/mutations/environments/editSecret.gql b/frontend/graphql/mutations/environments/editSecret.gql new file mode 100644 index 000000000..1df5f4324 --- /dev/null +++ b/frontend/graphql/mutations/environments/editSecret.gql @@ -0,0 +1,8 @@ +mutation UpdateSecret($id: ID!, $secretData: SecretInput!) { + editSecret(id: $id, secretData: $secretData) { + secret { + id + updatedAt + } + } +} diff --git a/frontend/graphql/queries/secrets/getAppEnvironments.gql b/frontend/graphql/queries/secrets/getAppEnvironments.gql index d6b03dc13..779227998 100644 --- a/frontend/graphql/queries/secrets/getAppEnvironments.gql +++ b/frontend/graphql/queries/secrets/getAppEnvironments.gql @@ -1,5 +1,5 @@ query GetAppEnvironments($appId: ID!) { - appEnvironments(appId: $appId) { + appEnvironments(appId: $appId, environmentId: null) { id name envType diff --git a/frontend/graphql/queries/secrets/getSecretNames.gql b/frontend/graphql/queries/secrets/getSecretNames.gql new file mode 100644 index 000000000..0e69a742c --- /dev/null +++ b/frontend/graphql/queries/secrets/getSecretNames.gql @@ -0,0 +1,12 @@ +query GetSecretNames($envId: ID!) { + secrets(envId: $envId) { + id + key + } + environmentKeys(environmentId: $envId) { + id + identityKey + wrappedSeed + wrappedSalt + } +} diff --git a/frontend/graphql/queries/secrets/getSecrets.gql b/frontend/graphql/queries/secrets/getSecrets.gql index 5c1af77c3..829bcb8e4 100644 --- a/frontend/graphql/queries/secrets/getSecrets.gql +++ b/frontend/graphql/queries/secrets/getSecrets.gql @@ -1,17 +1,28 @@ -query GetSecrets($envId: ID!) { +query GetSecrets($appId: ID!, $envId: ID!) { secrets(envId: $envId) { id key value + tags + comment createdAt history { id key value + tags + version + comment timestamp eventType } } + appEnvironments(appId: $appId, environmentId: $envId) { + id + name + envType + identityKey + } environmentKeys(environmentId: $envId) { id identityKey From e01a341f8a9712b9691644557ffa02fb7783221f Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sun, 27 Aug 2023 15:08:19 +0530 Subject: [PATCH 060/161] feat: update secrets overvew page, add environment page --- .../environments/[environment]/page.tsx | 415 ++++++++++++++++++ .../app/[team]/apps/[app]/secrets/page.tsx | 338 +++++--------- ...wordDialog.tsx => UnlockKeyringDialog.tsx} | 5 +- .../components/environments/SecretRow.tsx | 349 +++++++++++++++ 4 files changed, 875 insertions(+), 232 deletions(-) create mode 100644 frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx rename frontend/components/auth/{SudoPasswordDialog.tsx => UnlockKeyringDialog.tsx} (93%) create mode 100644 frontend/components/environments/SecretRow.tsx diff --git a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx new file mode 100644 index 000000000..793c072e4 --- /dev/null +++ b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx @@ -0,0 +1,415 @@ +'use client' + +import { EnvironmentType, SecretInput, SecretType } from '@/apollo/graphql' +import UnlockKeyringDialog from '@/components/auth/UnlockKeyringDialog' +import { KeyringContext } from '@/contexts/keyringContext' +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetSecrets } from '@/graphql/queries/secrets/getSecrets.gql' +import { CreateNewSecret } from '@/graphql/mutations/environments/createSecret.gql' +import { UpdateSecret } from '@/graphql/mutations/environments/editSecret.gql' +import { DeleteSecretOp } from '@/graphql/mutations/environments/deleteSecret.gql' +import { GetEnvironmentTokens } from '@/graphql/queries/secrets/getEnvironmentTokens.gql' +import { CreateEnvToken } from '@/graphql/mutations/environments/createEnvironmentToken.gql' +import { + getUserKxPublicKey, + getUserKxPrivateKey, + decryptAsymmetric, + digest, + encryptAsymmetric, +} from '@/utils/crypto' +import { arraysEqual, envKeyring, generateEnvironmentToken } from '@/utils/environments' +import { useMutation, useQuery } from '@apollo/client' +import { useContext, useEffect, useState } from 'react' +import { Button } from '@/components/common/Button' +import { FaFileDownload, FaKey, FaPlus, FaSearch } from 'react-icons/fa' +import SecretRow from '@/components/environments/SecretRow' + +type EnvKeyring = { + privateKey: string + publicKey: string + salt: string +} + +export default function Environment({ + params, +}: { + params: { team: string; app: string; environment: string } +}) { + const { data: orgsData } = useQuery(GetOrganisations) + + const { data, loading } = useQuery(GetSecrets, { + variables: { + appId: params.app, + envId: params.environment, + }, + }) + + const [createSecret, { loading: createSecretMutationLoading, error }] = + useMutation(CreateNewSecret) + + const [updateSecret] = useMutation(UpdateSecret) + const [deleteSecret] = useMutation(DeleteSecretOp) + + const [createEnvironmentToken] = useMutation(CreateEnvToken) + + const { data: envTokensData } = useQuery(GetEnvironmentTokens, { + variables: { + envId: params.environment, + }, + }) + + const { keyring } = useContext(KeyringContext) + + const [envKeys, setEnvKeys] = useState(null) + const [secrets, setSecrets] = useState([]) + const [updatedSecrets, updateSecrets] = useState([]) + const [searchQuery, setSearchQuery] = useState('') + const [newSecretKey, setNewSecretKey] = useState('') + const [newSecretValue, setNewSecretValue] = useState('') + + const environment = data?.appEnvironments[0] as EnvironmentType + + const handleCreateNewSecret = async () => { + const encryptedKey = await encryptAsymmetric(newSecretKey, environment.identityKey) + const encryptedValue = await encryptAsymmetric(newSecretValue, environment.identityKey) + const keyDigest = await digest(newSecretKey, envKeys!.salt) + + await createSecret({ + variables: { + newSecret: { + envId: params.environment, + key: encryptedKey, + keyDigest, + value: encryptedValue, + folderId: null, + comment: '', + tags: [], + } as SecretInput, + }, + refetchQueries: [ + { + query: GetSecrets, + variables: { + appId: params.app, + envId: params.environment, + }, + }, + ], + }) + setNewSecretKey('') + setNewSecretValue('') + } + + const handleUpdateSecret = async (id: string) => { + const { key, value, comment } = updatedSecrets.find((secret) => secret.id === id)! + + const encryptedKey = await encryptAsymmetric(key, environment.identityKey) + const encryptedValue = await encryptAsymmetric(value, environment.identityKey) + const keyDigest = await digest(key, envKeys!.salt) + const encryptedComment = await encryptAsymmetric(comment, environment.identityKey) + + await updateSecret({ + variables: { + id, + secretData: { + key: encryptedKey, + keyDigest, + value: encryptedValue, + folderId: null, + comment: encryptedComment, + tags: [], + } as SecretInput, + }, + refetchQueries: [ + { + query: GetSecrets, + variables: { + appId: params.app, + envId: params.environment, + }, + }, + ], + }) + setNewSecretKey('') + setNewSecretValue('') + } + + const handleDeleteSecret = async (id: string) => { + await deleteSecret({ + variables: { + id, + }, + refetchQueries: [ + { + query: GetSecrets, + variables: { + appId: params.app, + envId: params.environment, + }, + }, + ], + }) + } + + const handleCreateNewEnvToken = async () => { + if (keyring) { + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring.publicKey), + privateKey: await getUserKxPrivateKey(keyring.privateKey), + } + + const { pssEnv, mutationPayload } = await generateEnvironmentToken( + environment, + data.environmentKeys[0], + userKxKeys + ) + + await createEnvironmentToken({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetEnvironmentTokens, + variables: { + envId: environment.id, + }, + }, + ], + }) + + console.log(pssEnv) + } else { + console.log('keyring unavailable') + } + } + + useEffect(() => { + const initEnvKeys = async () => { + const wrappedSeed = data.environmentKeys[0].wrappedSeed + + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring!.publicKey), + privateKey: await getUserKxPrivateKey(keyring!.privateKey), + } + const seed = await decryptAsymmetric(wrappedSeed, userKxKeys.privateKey, userKxKeys.publicKey) + + const salt = await decryptAsymmetric( + data.environmentKeys[0].wrappedSalt, + userKxKeys.privateKey, + userKxKeys.publicKey + ) + const { publicKey, privateKey } = await envKeyring(seed) + + setEnvKeys({ + publicKey, + privateKey, + salt, + }) + } + + if (data && keyring) initEnvKeys() + }, [data, keyring]) + + useEffect(() => { + if (data && envKeys) { + const decryptSecrets = async () => { + const decryptedSecrets = await Promise.all( + data.secrets.map(async (secret: SecretType) => { + const decryptedSecret = structuredClone(secret) + + decryptedSecret.key = await decryptAsymmetric( + secret.key, + envKeys?.privateKey, + envKeys?.publicKey + ) + + decryptedSecret.value = await decryptAsymmetric( + secret.value, + envKeys.privateKey, + envKeys.publicKey + ) + + if (decryptedSecret.comment !== '') + decryptedSecret.comment = await decryptAsymmetric( + secret.comment, + envKeys.privateKey, + envKeys.publicKey + ) + + return decryptedSecret + }) + ) + return decryptedSecrets + } + + decryptSecrets().then((decryptedSecrets) => { + setSecrets(decryptedSecrets) + updateSecrets(decryptedSecrets) + }) + } + }, [envKeys, data]) + + const handleUpdateSecretProperty = (id: string, property: string, value: any) => { + const updatedSecretList = updatedSecrets.map((secret) => { + if (secret.id === id) { + return { ...secret, [property]: value } + } + return secret + }) + + updateSecrets(updatedSecretList) + } + + const unsavedChanges = secrets.some((secret, index) => { + const updatedSecret = updatedSecrets[index] + + // Compare the properties that can be edited (comment, key, tags, value) + return ( + secret.comment !== updatedSecret.comment || + secret.key !== updatedSecret.key || + !arraysEqual(secret.tags, updatedSecret.tags) || + secret.value !== updatedSecret.value + ) + }) + + const getUpdatedSecrets = () => { + const changedElements = [] + + for (let i = 0; i < secrets.length; i++) { + const originalSecret = secrets[i] + const updatedSecret = updatedSecrets[i] + + if ( + originalSecret.comment !== updatedSecret.comment || + originalSecret.key !== updatedSecret.key || + !arraysEqual(originalSecret.tags, updatedSecret.tags) || + originalSecret.value !== updatedSecret.value + ) { + changedElements.push(updatedSecret) + } + } + + return changedElements + } + + const handleSaveChanges = async () => { + const updates = getUpdatedSecrets().map((secret) => handleUpdateSecret(secret.id)) + + await Promise.all(updates) + } + + const filteredSecrets = + searchQuery === '' + ? updatedSecrets + : updatedSecrets.filter((secret) => { + const searchRegex = new RegExp(searchQuery, 'i') + return searchRegex.test(secret.key) + }) + + const downloadEnvFile = () => { + const envContent = secrets + .map((secret) => { + const comment = secret.comment ? `#${secret.comment}\n` : '' + return `${comment}${secret.key}=${secret.value}` + }) + .join('\n') + + const blob = new Blob([envContent], { type: 'text/plain' }) + const url = URL.createObjectURL(blob) + + const a = document.createElement('a') + a.href = url + a.download = `${environment.name}.env` + + document.body.appendChild(a) + a.click() + + document.body.removeChild(a) + URL.revokeObjectURL(url) + } + + // useEffect(() => { + // const warningText = 'You have unsaved changes - are you sure you wish to leave this page?' + // const handleWindowClose = (e: BeforeUnloadEvent) => { + // if (!unsavedChanges) return + // e.preventDefault() + // return (e.returnValue = warningText) + // } + // const handleBrowseAway = () => { + // if (!unsavedChanges) return + // if (window.confirm(warningText)) return + // router.events.emit('routeChangeError') + // throw 'routeChange aborted.' + // } + // window.addEventListener('beforeunload', handleWindowClose) + // router.events.on('routeChangeStart', handleBrowseAway) + // return () => { + // window.removeEventListener('beforeunload', handleWindowClose) + // router.events.off('routeChangeStart', handleBrowseAway) + // } + // }, [unsavedChanges]) + + return ( +
+ {orgsData?.organisations && ( + + )} + {keyring !== null && !loading && ( +
+
+ {environment.name} + {unsavedChanges && *} +
+
+
+
+ +
+ setSearchQuery(e.target.value)} + /> +
+
+ + + +
+
+
+ {filteredSecrets.map((secret: SecretType) => ( + + ))} + +
+ +
+
+
+ )} +
+ ) +} diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index e78b09d1b..0d224127d 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -1,47 +1,41 @@ 'use client' import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' -import { GetSecrets } from '@/graphql/queries/secrets/getSecrets.gql' +import { GetSecretNames } from '@/graphql/queries/secrets/getSecretNames.gql' import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' import { GetOrganisationAdminsAndSelf } from '@/graphql/queries/organisation/getOrganisationAdminsAndSelf.gql' -import { CreateEnv } from '@/graphql/mutations/environments/createEnvironment.gql' import { InitAppEnvironments } from '@/graphql/mutations/environments/initAppEnvironments.gql' -import { CreateEnvToken } from '@/graphql/mutations/environments/createEnvironmentToken.gql' import { CreateNewUserToken } from '@/graphql/mutations/users/createUserToken.gql' import { GetUserTokens } from '@/graphql/queries/users/getUserTokens.gql' -import { GetEnvironmentTokens } from '@/graphql/queries/secrets/getEnvironmentTokens.gql' -import { CreateNewSecret } from '@/graphql/mutations/environments/createSecret.gql' import { useLazyQuery, useMutation, useQuery } from '@apollo/client' -import { useContext, useEffect, useState } from 'react' -import { useSession } from 'next-auth/react' +import { useCallback, useContext, useEffect, useState } from 'react' import { createNewEnvPayload, - envKeyring, - generateEnvironmentToken, + decryptEnvSecretNames, generateUserToken, + unwrapEnvSecretsForUser, } from '@/utils/environments' import { Button } from '@/components/common/Button' import { ApiEnvironmentEnvTypeChoices, ApiOrganisationMemberRoleChoices, - EnvironmentTokenType, EnvironmentType, OrganisationMemberType, SecretType, UserTokenType, } from '@/apollo/graphql' -import { - decryptAsymmetric, - digest, - encryptAsymmetric, - getUserKxPrivateKey, - getUserKxPublicKey, -} from '@/utils/crypto' -import { cryptoUtils } from '@/utils/auth' -import { getLocalKeyring } from '@/utils/localStorage' +import { getUserKxPrivateKey, getUserKxPublicKey } from '@/utils/crypto' import _sodium from 'libsodium-wrappers-sumo' import { KeyringContext } from '@/contexts/keyringContext' -import SudoPasswordDialog from '@/components/auth/SudoPasswordDialog' +import UnlockKeyringDialog from '@/components/auth/UnlockKeyringDialog' +import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa' +import Link from 'next/link' +import { usePathname } from 'next/navigation' + +type EnvSecrets = { + env: EnvironmentType + secrets: SecretType[] +} export default function Secrets({ params }: { params: { team: string; app: string } }) { const { data } = useQuery(GetAppEnvironments, { @@ -53,15 +47,23 @@ export default function Secrets({ params }: { params: { team: string; app: strin const [getOrgAdmins, { data: orgAdminsData }] = useLazyQuery(GetOrganisationAdminsAndSelf) const [getUserTokens, { data: userTokensData }] = useLazyQuery(GetUserTokens) - const [createEnvironment, { loading, error }] = useMutation(CreateEnv) + const [getEnvSecrets] = useLazyQuery(GetSecretNames) const [initAppEnvironments] = useMutation(InitAppEnvironments) const [createUserToken] = useMutation(CreateNewUserToken) + const [commonSecrets, setCommonSecrets] = useState([]) + const [envSecrets, setEnvSecrets] = useState([]) - const [userToken, setUserToken] = useState('') + const sortedEnvSecrets = [...envSecrets].sort((a, b) => { + const order = ['Development', 'Staging', 'Production'] + const indexA = order.indexOf(a.env.name) + const indexB = order.indexOf(b.env.name) + + return indexA - indexB + }) - const { data: session } = useSession() + const [userToken, setUserToken] = useState('') - const { keyring, setKeyring } = useContext(KeyringContext) + const { keyring } = useContext(KeyringContext) useEffect(() => { if (keyring === null) { @@ -86,6 +88,46 @@ export default function Secrets({ params }: { params: { team: string; app: strin const setupRequired = data?.appEnvironments.length === 0 ?? true + const commonSecretsKeys = commonSecrets.map((commonSecret: SecretType) => commonSecret.key) + + const updateCommonSecrets = useCallback((decryptedSecrets: SecretType[]) => { + setCommonSecrets((prevCommonSecrets) => [...prevCommonSecrets, ...decryptedSecrets]) + }, []) + + const fetchAndDecryptAppEnvs = async (appEnvironments: EnvironmentType[]) => { + let envCards = [] as EnvSecrets[] + + appEnvironments.forEach(async (env: EnvironmentType) => { + const { data } = await getEnvSecrets({ + variables: { + envId: env.id, + }, + }) + + const { wrappedSeed, wrappedSalt } = data.environmentKeys[0] + + const { publicKey, privateKey } = await unwrapEnvSecretsForUser( + wrappedSeed, + wrappedSalt, + keyring! + ) + + const decryptedSecrets = await decryptEnvSecretNames(data.secrets, { + publicKey, + privateKey, + }) + + envCards.push({ env, secrets: decryptedSecrets }) + updateCommonSecrets(decryptedSecrets) + }) + + setEnvSecrets(envCards) + } + + useEffect(() => { + if (keyring !== null && data?.appEnvironments) fetchAndDecryptAppEnvs(data?.appEnvironments) + }, [data?.appEnvironments, keyring]) + const initAppEnvs = async () => { const owner = orgAdminsData.organisationAdminsAndSelf.find( (user: OrganisationMemberType) => user.role === ApiOrganisationMemberRoleChoices.Owner @@ -159,217 +201,46 @@ export default function Secrets({ params }: { params: { team: string; app: strin } } - const EnvironmentCard = (props: { environment: EnvironmentType }) => { - type EnvKeyring = { - privateKey: string - publicKey: string - salt: string - } - - const { environment } = props - - const [key, setKey] = useState('') - const [value, setValue] = useState('') - const [envKeys, setEnvKeys] = useState(null) - const [envToken, setEnvToken] = useState('') - const [createSecret, { data, loading, error }] = useMutation(CreateNewSecret) - const [createEnvironmentToken] = useMutation(CreateEnvToken) - const { data: secretsData } = useQuery(GetSecrets, { - variables: { - envId: environment.id, - }, - }) - const { data: envTokensData } = useQuery(GetEnvironmentTokens, { - variables: { - envId: environment.id, - }, - }) - const [secrets, setSecrets] = useState([]) - - useEffect(() => { - const initEnvKeys = async () => { - const wrappedSeed = secretsData.environmentKeys[0].wrappedSeed - - const userKxKeys = { - publicKey: await getUserKxPublicKey(keyring!.publicKey), - privateKey: await getUserKxPrivateKey(keyring!.privateKey), - } - const seed = await decryptAsymmetric( - wrappedSeed, - userKxKeys.privateKey, - userKxKeys.publicKey - ) - - const salt = await decryptAsymmetric( - secretsData.environmentKeys[0].wrappedSalt, - userKxKeys.privateKey, - userKxKeys.publicKey - ) - const { publicKey, privateKey } = await envKeyring(seed) - - setEnvKeys({ - publicKey, - privateKey, - salt, - }) - } - - if (secretsData && keyring) initEnvKeys() - }, [secretsData, keyring]) - - useEffect(() => { - if (secretsData && envKeys) { - const decryptSecrets = async () => { - const decryptedSecrets = await Promise.all( - secretsData.secrets.map(async (secret: SecretType) => { - const decryptedSecret = structuredClone(secret) - decryptedSecret.key = await decryptAsymmetric( - secret.key, - envKeys?.privateKey, - envKeys?.publicKey - ) - decryptedSecret.value = await decryptAsymmetric( - secret.value, - envKeys.privateKey, - envKeys.publicKey - ) - return decryptedSecret - }) - ) - return decryptedSecrets - } - - decryptSecrets().then((decryptedSecrets) => { - setSecrets(decryptedSecrets) - }) - } - }, [envKeys, secretsData]) - - const decryptSecretField = async (encryptedField: string) => { - const decryptedField = await decryptAsymmetric( - encryptedField, - envKeys!.privateKey, - envKeys!.publicKey - ) - return decryptedField - } - - const handleCreateNewSecret = async () => { - const encryptedKey = await encryptAsymmetric(key, environment.identityKey) - const encryptedValue = await encryptAsymmetric(value, environment.identityKey) - const keyDigest = await digest(key, envKeys!.salt) + const EnvCard = (props: { envSecrets: EnvSecrets }) => { + const { env, secrets } = props.envSecrets - await createSecret({ - variables: { - envId: environment.id, - key: encryptedKey, - keyDigest, - value: encryptedValue, - }, - refetchQueries: [ - { - query: GetSecrets, - variables: { - envId: environment.id, - }, - }, - ], - }) - setKey('') - setValue('') + const secretExistsInEnv = (key: string) => { + return secrets.find((s: SecretType) => s.key === key) } - const handleCreateNewEnvToken = async () => { - if (keyring) { - const userKxKeys = { - publicKey: await getUserKxPublicKey(keyring.publicKey), - privateKey: await getUserKxPrivateKey(keyring.privateKey), - } - - const { pssEnv, mutationPayload } = await generateEnvironmentToken( - environment, - secretsData.environmentKeys[0], - userKxKeys - ) - - await createEnvironmentToken({ - variables: mutationPayload, - refetchQueries: [ - { - query: GetEnvironmentTokens, - variables: { - envId: environment.id, - }, - }, - ], - }) - - setEnvToken(pssEnv) - } else { - console.log('keyring unavailable') - } - } + const pathname = usePathname() return ( -
-
- {environment.name} - {environment.envType} +
+
+ {env.name}
-
- {envKeys !== null && - secrets.map((secret: SecretType) => ( -
-
{secret.key}
-
{secret.value}
-
- ))} -
- - setKey(e.target.value)} - /> -
-
- - setValue(e.target.value)} - /> -
-
- -
- -
- {envTokensData?.environmentTokens.map((envToken: EnvironmentTokenType) => ( -
- {envToken.name} | {envToken.createdAt} -
- ))} - {envToken} -
- +
+ {commonSecretsKeys.map((key: string, index: number) => ( +
+ {secretExistsInEnv(key) ? ( +
+ +
+ ) : ( + + )}
-
+ ))} +
+
+ + +
) } return ( -
+
{orgsData?.organisations && ( - + )} {keyring !== null && (
@@ -383,12 +254,23 @@ export default function Secrets({ params }: { params: { team: string; app: strin
) : ( -
- {data?.appEnvironments.map((env: EnvironmentType) => ( - - ))} - -
+ <> +
+
+
KEY
+
+ {commonSecretsKeys.map((secret: string, index: number) => ( +
+
{secret}
+
+ ))} +
+
+ {sortedEnvSecrets.map((envS: EnvSecrets) => ( + + ))} +
+
{userTokensData?.userTokens.map((userToken: UserTokenType) => (
{userToken.name} | {userToken.createdAt} @@ -401,7 +283,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin
-
+ )} )} diff --git a/frontend/components/auth/SudoPasswordDialog.tsx b/frontend/components/auth/UnlockKeyringDialog.tsx similarity index 93% rename from frontend/components/auth/SudoPasswordDialog.tsx rename to frontend/components/auth/UnlockKeyringDialog.tsx index a20a0b03f..5512edff4 100644 --- a/frontend/components/auth/SudoPasswordDialog.tsx +++ b/frontend/components/auth/UnlockKeyringDialog.tsx @@ -7,7 +7,7 @@ import { cryptoUtils } from '@/utils/auth' import { getLocalKeyring } from '@/utils/localStorage' import { useSession } from 'next-auth/react' -export default function SudoPasswordDialog(props: { organisationId: string }) { +export default function UnlockKeyringDialog(props: { organisationId: string }) { const [password, setPassword] = useState('') const [showPw, setShowPw] = useState(false) const [isOpen, setIsOpen] = useState(false) @@ -69,9 +69,6 @@ export default function SudoPasswordDialog(props: { organisationId: string }) {

Unlock User Keyring

-
diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx new file mode 100644 index 000000000..28d57141f --- /dev/null +++ b/frontend/components/environments/SecretRow.tsx @@ -0,0 +1,349 @@ +import { + ApiEnvironmentEnvTypeChoices, + ApiSecretEventEventTypeChoices, + SecretType, +} from '@/apollo/graphql' +import { Fragment, useState } from 'react' +import { FaEyeSlash, FaEye, FaTimes, FaRegCommentDots, FaTrashAlt, FaHistory } from 'react-icons/fa' +import { Button } from '../common/Button' +import { Dialog, Transition } from '@headlessui/react' + +import clsx from 'clsx' +import { relativeTimeFromDates } from '@/utils/time' + +const HistoryDialog = (props: { secret: SecretType }) => { + const { secret } = props + + const [isOpen, setIsOpen] = useState(false) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const getEventTypeColor = (eventType: ApiSecretEventEventTypeChoices) => { + if (eventType === ApiSecretEventEventTypeChoices.C) return 'bg-emerald-500' + if (eventType === ApiSecretEventEventTypeChoices.U) return 'bg-yellow-500' + if (eventType === ApiSecretEventEventTypeChoices.R) return 'bg-blue-500' + if (eventType === ApiSecretEventEventTypeChoices.D) return 'bg-red-500' + } + + const getEventTypeText = (eventType: ApiSecretEventEventTypeChoices) => { + if (eventType === ApiSecretEventEventTypeChoices.C) return 'Created' + if (eventType === ApiSecretEventEventTypeChoices.U) return 'Updated' + if (eventType === ApiSecretEventEventTypeChoices.R) return 'Read' + if (eventType === ApiSecretEventEventTypeChoices.D) return 'Deleted' + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ {secret.key} history +

+ + +
+ +
+
+ {secret.history?.map((historyItem) => ( +
+
+ + {/* {historyItem!.version} */} +
{getEventTypeText(historyItem!.eventType)}
+
+ {relativeTimeFromDates(new Date(historyItem!.timestamp))} +
+ {/*
{historyItem!.value}
*/} +
+
+ ))} +
+
+ +
+
+
+
+
+
+
+
+ + ) +} + +const CommentDialog = (props: { + secretId: string + secretName: string + comment: string + handlePropertyChange: Function +}) => { + const { secretId, secretName, comment, handlePropertyChange } = props + + const [commentValue, setCommentValue] = useState(comment) + + const [isOpen, setIsOpen] = useState(false) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const handleClose = () => { + handlePropertyChange(secretId, 'comment', commentValue) + closeModal() + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Update {secretName} comment +

+ + +
+ +
+ +
+ +
+
+
+
+
+
+
+
+ + ) +} + +const DeleteConfirmDialog = (props: { + secretId: string + secretName: string + onDelete: Function +}) => { + const { secretName, secretId, onDelete } = props + + const [isOpen, setIsOpen] = useState(false) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Delete {secretName} +

+ + +
+ +
+

Are you sure you want to delete this secret?

+
+ + +
+
+
+
+
+
+
+
+ + ) +} + +export default function SecretRow(props: { + secret: SecretType + handlePropertyChange: Function + handleDelete: Function +}) { + const { secret, handlePropertyChange, handleDelete } = props + + const [isRevealed, setIsRevealed] = useState(false) + + const toggleReveal = () => setIsRevealed(!isRevealed) + + return ( +
+
+ handlePropertyChange(secret.id, 'key', e.target.value.toUpperCase())} + /> +
+
+ handlePropertyChange(secret.id, 'value', e.target.value)} + /> +
+ + + + +
+
+
+ ) +} From fb62f97a20ae24d4d6cc788acc988ec561cf6688 Mon Sep 17 00:00:00 2001 From: Rohan Chaturvedi Date: Mon, 28 Aug 2023 14:05:07 +0530 Subject: [PATCH 061/161] fix: encrypting string util --- frontend/utils/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/utils/auth.ts b/frontend/utils/auth.ts index ced17bdf7..9e3d6deba 100644 --- a/frontend/utils/auth.ts +++ b/frontend/utils/auth.ts @@ -132,7 +132,7 @@ export namespace cryptoUtils { const sodium = _sodium return sodium.to_base64( - await encryptRaw(sodium.from_string(plaintext), key), + await encryptRaw(plaintext, key), sodium.base64_variants.ORIGINAL ) } From 9cfc6b8cf9e2abcbc89497b308ffdc176697d14e Mon Sep 17 00:00:00 2001 From: Rohan Chaturvedi Date: Mon, 28 Aug 2023 14:26:20 +0530 Subject: [PATCH 062/161] fix: create org operation name --- frontend/app/onboard/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/onboard/page.tsx b/frontend/app/onboard/page.tsx index 71ca32ed8..76cbfc398 100644 --- a/frontend/app/onboard/page.tsx +++ b/frontend/app/onboard/page.tsx @@ -21,7 +21,7 @@ import { toast } from 'react-toastify' import { gql, useMutation } from '@apollo/client' import { useRouter } from 'next/navigation' import Link from 'next/link' -import { CreateOrganisation } from '@/graphql/mutations/createOrganisation.gql' +import { CreateOrg } from '@/graphql/mutations/createOrganisation.gql' import { setLocalOrg } from '@/utils/localStorage' const bip39 = require('bip39') @@ -36,7 +36,7 @@ const Onboard = () => { const [inputs, setInputs] = useState>([]) const [step, setStep] = useState(0) const [showWelcome, setShowWelcome] = useState(true) - const [createOrganisation, { data, loading, error }] = useMutation(CreateOrganisation) + const [createOrganisation, { data, loading, error }] = useMutation(CreateOrg) const [isloading, setIsLoading] = useState(false) const [seedDownloaded, setSeedDownloaded] = useState(false) const [success, setSuccess] = useState(false) From 211ca91cfcd68e34914808586aea5ff03ec30297 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 29 Aug 2023 14:14:45 +0530 Subject: [PATCH 063/161] fix: update models and schema for tags --- ...er_color_remove_secret_tags_secret_tags.py | 28 ++++++++++++++++++ ...move_secretfolder_color_secrettag_color.py | 23 +++++++++++++++ ...emove_secretevent_tags_secretevent_tags.py | 22 ++++++++++++++ backend/api/models.py | 11 ++----- .../backend/graphene/mutations/environment.py | 7 +++-- backend/backend/graphene/types.py | 2 +- frontend/apollo/gql.ts | 14 +++++++-- frontend/apollo/graphql.ts | 29 +++++++++++++++---- frontend/apollo/schema.graphql | 17 ++++++----- 9 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 backend/api/migrations/0026_secretfolder_color_remove_secret_tags_secret_tags.py create mode 100644 backend/api/migrations/0027_remove_secretfolder_color_secrettag_color.py create mode 100644 backend/api/migrations/0028_remove_secretevent_tags_secretevent_tags.py diff --git a/backend/api/migrations/0026_secretfolder_color_remove_secret_tags_secret_tags.py b/backend/api/migrations/0026_secretfolder_color_remove_secret_tags_secret_tags.py new file mode 100644 index 000000000..85430e626 --- /dev/null +++ b/backend/api/migrations/0026_secretfolder_color_remove_secret_tags_secret_tags.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.3 on 2023-08-29 08:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0025_rename_environmentsecret_environmenttoken_usertoken'), + ] + + operations = [ + migrations.AddField( + model_name='secretfolder', + name='color', + field=models.CharField(default='', max_length=64), + preserve_default=False, + ), + migrations.RemoveField( + model_name='secret', + name='tags', + ), + migrations.AddField( + model_name='secret', + name='tags', + field=models.ManyToManyField(to='api.secrettag'), + ), + ] diff --git a/backend/api/migrations/0027_remove_secretfolder_color_secrettag_color.py b/backend/api/migrations/0027_remove_secretfolder_color_secrettag_color.py new file mode 100644 index 000000000..b2fc0597d --- /dev/null +++ b/backend/api/migrations/0027_remove_secretfolder_color_secrettag_color.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.3 on 2023-08-29 08:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0026_secretfolder_color_remove_secret_tags_secret_tags'), + ] + + operations = [ + migrations.RemoveField( + model_name='secretfolder', + name='color', + ), + migrations.AddField( + model_name='secrettag', + name='color', + field=models.CharField(default='', max_length=64), + preserve_default=False, + ), + ] diff --git a/backend/api/migrations/0028_remove_secretevent_tags_secretevent_tags.py b/backend/api/migrations/0028_remove_secretevent_tags_secretevent_tags.py new file mode 100644 index 000000000..0b20cd757 --- /dev/null +++ b/backend/api/migrations/0028_remove_secretevent_tags_secretevent_tags.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.3 on 2023-08-29 08:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0027_remove_secretfolder_color_secrettag_color'), + ] + + operations = [ + migrations.RemoveField( + model_name='secretevent', + name='tags', + ), + migrations.AddField( + model_name='secretevent', + name='tags', + field=models.ManyToManyField(to='api.secrettag'), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 3f4b2b5bb..3d14ddd64 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -235,6 +235,7 @@ class SecretTag(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) name = models.CharField(max_length=64) + color = models.CharField(max_length=64) 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) @@ -249,10 +250,7 @@ class Secret(models.Model): key_digest = models.TextField() value = models.TextField() version = models.IntegerField(default=1) - tags = ArrayField( - models.CharField(max_length=64), - size=10, - ) + tags = models.ManyToManyField(SecretTag) comment = models.TextField() created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) updated_at = models.DateTimeField(auto_now=True) @@ -284,10 +282,7 @@ class SecretEvent(models.Model): key_digest = models.TextField() value = models.TextField() version = models.IntegerField(default=1) - tags = ArrayField( - models.CharField(max_length=64), - size=10, - ) + tags = models.ManyToManyField(SecretTag) comment = models.TextField() event_type = models.CharField( max_length=1, diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 6415da8f3..e26668a1a 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -158,14 +158,14 @@ def mutate(cls, root, info, id, env_id, name, parent_folder_id=None): class CreateSecretTagMutation(graphene.Mutation): class Arguments: - id = graphene.ID(required=True) org_id = graphene.ID(required=True) name = graphene.String(required=True) + color = graphene.String(required=True) tag = graphene.Field(SecretTagType) @classmethod - def mutate(cls, root, info, id, org_id, name): + def mutate(cls, root, info, org_id, name, color): if not user_is_org_member(info.context.user.userId, org_id): raise GraphQLError( @@ -176,7 +176,8 @@ def mutate(cls, root, info, id, org_id, name): if SecretTag.objects.filter(organisation=org, name=name).exists(): raise GraphQLError('This tag already exists!') - tag = SecretTag.objects.create(id=id, organisation=org, name=name) + tag = SecretTag.objects.create( + organisation=org, name=name, color=color) return CreateSecretTagMutation(tag=tag) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 8f2b2a5ef..3922535e2 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -64,7 +64,7 @@ class Meta: class SecretTagType(DjangoObjectType): class Meta: model = SecretTag - fields = ('id', 'name') + fields = ('id', 'name', 'color') class SecretEventType(DjangoObjectType): diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 588bab1e2..c30ebe3d1 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -20,6 +20,7 @@ const documents = { "mutation CreateEnvKey($envId: ID!, $ownerId: ID!, $wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) {\n createEnvironmentKey(\n envId: $envId\n userId: $ownerId\n wrappedSeed: $wrappedSeed\n wrappedSalt: $wrappedSalt\n identityKey: $identityKey\n ) {\n environmentKey {\n id\n createdAt\n }\n }\n}": types.CreateEnvKeyDocument, "mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\n }\n }\n}": types.CreateEnvTokenDocument, "mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}": types.CreateNewSecretDocument, + "mutation CreateNewSecretTag($orgId: ID!, $name: String!, $color: String!) {\n createSecretTag(orgId: $orgId, name: $name, color: $color) {\n tag {\n id\n }\n }\n}": types.CreateNewSecretTagDocument, "mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\n id\n }\n }\n}": types.DeleteSecretOpDocument, "mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}": types.UpdateSecretDocument, "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, @@ -37,7 +38,8 @@ const documents = { "query GetEnvironmentKey($envId: ID!) {\n environmentKeys(environmentId: $envId) {\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 GetSecretNames($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretNamesDocument, - "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags\n comment\n createdAt\n history {\n id\n key\n value\n tags\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, + "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, + "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n name\n color\n }\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetUserTokensDocument, }; @@ -83,6 +85,10 @@ export function graphql(source: "mutation CreateEnvToken($envId: ID!, $name: Str * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\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 CreateNewSecretTag($orgId: ID!, $name: String!, $color: String!) {\n createSecretTag(orgId: $orgId, name: $name, color: $color) {\n tag {\n id\n }\n }\n}"): (typeof documents)["mutation CreateNewSecretTag($orgId: ID!, $name: String!, $color: String!) {\n createSecretTag(orgId: $orgId, name: $name, color: $color) {\n tag {\n id\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -154,7 +160,11 @@ export function graphql(source: "query GetSecretNames($envId: ID!) {\n secrets( /** * 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!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags\n comment\n createdAt\n history {\n id\n key\n value\n tags\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags\n comment\n createdAt\n history {\n id\n key\n value\n tags\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"]; +export function graphql(source: "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}"): (typeof documents)["query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\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 GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n name\n color\n }\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n name\n color\n }\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 49ccfc02d..0d1c261b2 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -281,7 +281,7 @@ export type MutationCreateSecretFolderArgs = { export type MutationCreateSecretTagArgs = { - id: Scalars['ID']; + color: Scalars['String']; name: Scalars['String']; orgId: Scalars['ID']; }; @@ -446,7 +446,7 @@ export type SecretEventType = { id: Scalars['String']; key: Scalars['String']; secret: SecretType; - tags: Array; + tags: Array; timestamp: Scalars['DateTime']; value: Scalars['String']; version: Scalars['Int']; @@ -472,6 +472,7 @@ export type SecretInput = { export type SecretTagType = { __typename?: 'SecretTagType'; + color: Scalars['String']; id: Scalars['String']; name: Scalars['String']; }; @@ -484,7 +485,7 @@ export type SecretType = { history?: Maybe>>; id: Scalars['String']; key: Scalars['String']; - tags: Array; + tags: Array; updatedAt: Scalars['DateTime']; value: Scalars['String']; version: Scalars['Int']; @@ -577,6 +578,15 @@ export type CreateNewSecretMutationVariables = Exact<{ export type CreateNewSecretMutation = { __typename?: 'Mutation', createSecret?: { __typename?: 'CreateSecretMutation', secret?: { __typename?: 'SecretType', id: string, key: string, value: string, createdAt?: any | null } | null } | null }; +export type CreateNewSecretTagMutationVariables = Exact<{ + orgId: Scalars['ID']; + name: Scalars['String']; + color: Scalars['String']; +}>; + + +export type CreateNewSecretTagMutation = { __typename?: 'Mutation', createSecretTag?: { __typename?: 'CreateSecretTagMutation', tag?: { __typename?: 'SecretTagType', id: string } | null } | null }; + export type DeleteSecretOpMutationVariables = Exact<{ id: Scalars['ID']; }>; @@ -710,13 +720,20 @@ export type GetSecretNamesQueryVariables = Exact<{ export type GetSecretNamesQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; +export type GetSecretTagsQueryVariables = Exact<{ + orgId: Scalars['ID']; +}>; + + +export type GetSecretTagsQuery = { __typename?: 'Query', secretTags?: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string } | null> | null }; + export type GetSecretsQueryVariables = Exact<{ appId: Scalars['ID']; envId: Scalars['ID']; }>; -export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, tags: Array, comment: string, createdAt?: any | null, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, tags: Array, version: number, comment: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices } | null> | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; +export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, comment: string, createdAt?: any | null, tags: Array<{ __typename?: 'SecretTagType', name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, version: number, comment: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', name: string, color: string }> } | null> | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; export type GetUserTokensQueryVariables = Exact<{ organisationId: Scalars['ID']; @@ -733,6 +750,7 @@ export const CreateEnvDocument = {"kind":"Document","definitions":[{"kind":"Oper export const CreateEnvKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvKey"},"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":"ownerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}},"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentKey"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ownerId"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedSalt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedSalt"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateEnvTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvToken"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecret"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"newSecret"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"newSecret"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateNewSecretTagDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecretTag"},"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":"color"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecretTag"},"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":"color"},"value":{"kind":"Variable","name":{"kind":"Name","value":"color"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tag"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteSecretOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSecretOp"},"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":"deleteSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSecret"},"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":"secretData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -750,5 +768,6 @@ export const GetAppEnvironmentsDocument = {"kind":"Document","definitions":[{"ki 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"}}}}],"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"}}}],"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 GetSecretNamesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretNames"},"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":"secrets"},"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":"key"}}]}},{"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 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"}}}}],"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"}}}],"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":"tags"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"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":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}},{"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":"eventType"}}]}}]}},{"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":"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"}}}}],"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"}}}],"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":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"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":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"eventType"}}]}}]}},{"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":"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 GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index d0bb5c577..4a5ca10a5 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -149,7 +149,7 @@ type SecretType { key: String! value: String! version: Int! - tags: [String!]! + tags: [SecretTagType!]! comment: String! createdAt: DateTime updatedAt: DateTime! @@ -163,13 +163,19 @@ type SecretFolderType { updatedAt: DateTime! } +type SecretTagType { + id: String! + name: String! + color: String! +} + type SecretEventType { id: String! secret: SecretType! key: String! value: String! version: Int! - tags: [String!]! + tags: [SecretTagType!]! comment: String! eventType: ApiSecretEventEventTypeChoices! timestamp: DateTime! @@ -190,11 +196,6 @@ enum ApiSecretEventEventTypeChoices { D } -type SecretTagType { - id: String! - name: String! -} - type EnvironmentKeyType { id: String! identityKey: String! @@ -234,7 +235,7 @@ type Mutation { createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation createUserToken(identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation - createSecretTag(id: ID!, name: String!, orgId: ID!): CreateSecretTagMutation + createSecretTag(color: String!, name: String!, orgId: ID!): CreateSecretTagMutation createSecret(secretData: SecretInput): CreateSecretMutation editSecret(id: ID!, secretData: SecretInput): EditSecretMutation deleteSecret(id: ID!): DeleteSecretMutation From 4d31054eca84e35c786898e3250ff5881950549c Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 29 Aug 2023 14:15:25 +0530 Subject: [PATCH 064/161] feat: update secrets query for new tags schema --- frontend/graphql/queries/secrets/getSecrets.gql | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/graphql/queries/secrets/getSecrets.gql b/frontend/graphql/queries/secrets/getSecrets.gql index 829bcb8e4..a345d6d57 100644 --- a/frontend/graphql/queries/secrets/getSecrets.gql +++ b/frontend/graphql/queries/secrets/getSecrets.gql @@ -3,14 +3,20 @@ query GetSecrets($appId: ID!, $envId: ID!) { id key value - tags + tags { + name + color + } comment createdAt history { id key value - tags + tags { + name + color + } version comment timestamp From 4da0fa8aeb1e480d31b0fe21dc136b44f25f66b9 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 29 Aug 2023 14:15:37 +0530 Subject: [PATCH 065/161] feat: mutation and query to create and fetch tags --- .../graphql/mutations/environments/createSecretTag.gql | 7 +++++++ frontend/graphql/queries/secrets/getSecretTags.gql | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 frontend/graphql/mutations/environments/createSecretTag.gql create mode 100644 frontend/graphql/queries/secrets/getSecretTags.gql diff --git a/frontend/graphql/mutations/environments/createSecretTag.gql b/frontend/graphql/mutations/environments/createSecretTag.gql new file mode 100644 index 000000000..b6bcbd4ee --- /dev/null +++ b/frontend/graphql/mutations/environments/createSecretTag.gql @@ -0,0 +1,7 @@ +mutation CreateNewSecretTag($orgId: ID!, $name: String!, $color: String!) { + createSecretTag(orgId: $orgId, name: $name, color: $color) { + tag { + id + } + } +} diff --git a/frontend/graphql/queries/secrets/getSecretTags.gql b/frontend/graphql/queries/secrets/getSecretTags.gql new file mode 100644 index 000000000..679e9d1e3 --- /dev/null +++ b/frontend/graphql/queries/secrets/getSecretTags.gql @@ -0,0 +1,7 @@ +query GetSecretTags($orgId: ID!) { + secretTags(orgId: $orgId) { + id + name + color + } +} From 711e1f8a674ba2b1bda8244c9bb5393222af0335 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Wed, 30 Aug 2023 15:25:15 +0530 Subject: [PATCH 066/161] fix: misc fixes for tags --- backend/api/views.py | 23 ++++++++++++------- .../backend/graphene/mutations/environment.py | 21 ++++++++++------- backend/backend/schema.py | 2 +- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index ce4d1c5f7..4e03b2bfe 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -307,8 +307,8 @@ def post(self, request): for secret in request_body['secrets']: - tag_names = SecretTag.objects.filter( - id__in=secret['tags']).values('name') + tags = SecretTag.objects.filter( + id__in=secret['tags']) secret_data = { 'environment': env, @@ -317,13 +317,17 @@ def post(self, request): 'value': secret['value'], 'folder_id': secret['folderId'], 'version': 1, - 'tags': [], 'comment': secret['comment'], } secret_obj = Secret.objects.create(**secret_data) - SecretEvent.objects.create( + secret_obj.tags.set(tags) + secret_obj.save() + + event = SecretEvent.objects.create( **{**secret_data, **{'user': user, 'secret': secret_obj, 'event_type': SecretEvent.CREATE}}) + event.tags.set(tags) + event.save() return Response(status=status.HTTP_200_OK) @@ -348,8 +352,8 @@ def put(self, request): for secret in request_body['secrets']: secret_obj = Secret.objects.get(id=secret['id']) - tag_names = SecretTag.objects.filter( - id__in=secret['tags']).values('name') + tags = SecretTag.objects.filter( + id__in=secret['tags']) secret_data = { 'environment': env, @@ -358,7 +362,6 @@ def put(self, request): 'value': secret['value'], 'folder_id': secret['folderId'], 'version': secret_obj.version + 1, - 'tags': [], 'comment': secret['comment'], } @@ -366,9 +369,13 @@ def put(self, request): setattr(secret_obj, key, value) secret_obj.updated_at = timezone.now() + secret_obj.tags.set(tags) secret_obj.save() - SecretEvent.objects.create( + + event = SecretEvent.objects.create( **{**secret_data, **{'user': user, 'secret': secret_obj, 'event_type': SecretEvent.UPDATE}}) + event.tags.set(tags) + event.save() return Response(status=status.HTTP_200_OK) diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index e26668a1a..fdee49925 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -196,8 +196,8 @@ def mutate(cls, root, info, secret_data): raise GraphQLError( "You don't have permission to perform this action") - tag_names = SecretTag.objects.filter( - id__in=secret_data.tags).values_list('name', flat=True) + tags = SecretTag.objects.filter( + id__in=secret_data.tags) secret_obj_data = { 'environment_id': env.id, @@ -206,17 +206,20 @@ def mutate(cls, root, info, secret_data): 'key_digest': secret_data.key_digest, 'value': secret_data.value, 'version': 1, - 'tags': [], 'comment': secret_data.comment } secret = Secret.objects.create(**secret_obj_data) + secret.tags.set(tags) + secret.save() org_member = OrganisationMember.objects.get( user=info.context.user, organisation=org) - SecretEvent.objects.create( + event = SecretEvent.objects.create( **{**secret_obj_data, **{'user': org_member, 'secret': secret, 'event_type': SecretEvent.CREATE}}) + event.tags.set(tags) + event.save() return CreateSecretMutation(secret=secret) @@ -237,8 +240,8 @@ def mutate(cls, root, info, id, secret_data): raise GraphQLError( "You don't have permission to perform this action") - tag_names = SecretTag.objects.filter( - id__in=secret_data.tags).values_list('name', flat=True) + tags = SecretTag.objects.filter( + id__in=secret_data.tags) secret_obj_data = { 'folder_id': secret_data.folder_id, @@ -246,7 +249,6 @@ def mutate(cls, root, info, id, secret_data): 'key_digest': secret_data.key_digest, 'value': secret_data.value, 'version': secret.version + 1, - 'tags': [], 'comment': secret_data.comment } @@ -254,13 +256,16 @@ def mutate(cls, root, info, id, secret_data): setattr(secret, key, value) secret.updated_at = timezone.now() + secret.tags.set(tags) secret.save() org_member = OrganisationMember.objects.get( user=info.context.user, organisation=org) - SecretEvent.objects.create( + event = SecretEvent.objects.create( **{**secret_obj_data, **{'user': org_member, 'environment': env, 'secret': secret, 'event_type': SecretEvent.UPDATE}}) + event.tags.set(tags) + event.save() return EditSecretMutation(secret=secret) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 55912faa7..a3599a343 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -115,7 +115,7 @@ def resolve_secret_tags(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") - return SecretTag.objects.filter(org_id=org_id) + return SecretTag.objects.filter(organisation_id=org_id) def resolve_environment_keys(root, info, environment_id): if not user_can_access_environment(info.context.user.userId, environment_id): From 1ba8dd6ad5291040eec079f987257579f3706565 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Wed, 30 Aug 2023 17:17:57 +0530 Subject: [PATCH 067/161] feat: allow querying email and username for org members --- backend/backend/graphene/types.py | 15 +++++++++++++-- frontend/apollo/gql.ts | 4 ++-- frontend/apollo/graphql.ts | 7 +++++-- frontend/apollo/schema.graphql | 3 +++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 3922535e2..131a1bb43 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -13,11 +13,22 @@ class Meta: class OrganisationMemberType(DjangoObjectType): + email = graphene.String() + username = graphene.String() + class Meta: model = OrganisationMember - fields = ('id', 'role', 'identity_key', + fields = ('id', 'email', 'username', 'role', 'identity_key', 'wrapped_keyring', 'created_at', 'updated_at') + def resolve_email(self, info): + org_member = OrganisationMember.objects.get(id=self.id) + return org_member.user.email + + def resolve_username(self, info): + org_member = OrganisationMember.objects.get(id=self.id) + return org_member.user.username + class AppType(DjangoObjectType): class Meta: @@ -71,7 +82,7 @@ class SecretEventType(DjangoObjectType): class Meta: model = SecretEvent fields = ('id', 'secret', 'collection', 'key', 'value', - 'version', 'tags', 'comment', 'event_type', 'timestamp') + 'version', 'tags', 'comment', 'event_type', 'timestamp', 'user') class SecretType(DjangoObjectType): diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index c30ebe3d1..ff6b8b087 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -39,7 +39,7 @@ const documents = { "query GetEnvironmentTokens($envId: ID!) {\n environmentTokens(environmentId: $envId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetEnvironmentTokensDocument, "query GetSecretNames($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretNamesDocument, "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, - "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n name\n color\n }\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, + "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetUserTokensDocument, }; @@ -164,7 +164,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!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n name\n color\n }\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n name\n color\n }\n version\n comment\n timestamp\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"]; +export function graphql(source: "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 0d1c261b2..8b9afe26b 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -327,10 +327,12 @@ export type Node = { export type OrganisationMemberType = { __typename?: 'OrganisationMemberType'; createdAt?: Maybe; + email?: Maybe; id: Scalars['String']; identityKey?: Maybe; role: ApiOrganisationMemberRoleChoices; updatedAt: Scalars['DateTime']; + username?: Maybe; wrappedKeyring: Scalars['String']; }; @@ -448,6 +450,7 @@ export type SecretEventType = { secret: SecretType; tags: Array; timestamp: Scalars['DateTime']; + user?: Maybe; value: Scalars['String']; version: Scalars['Int']; }; @@ -733,7 +736,7 @@ export type GetSecretsQueryVariables = Exact<{ }>; -export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, comment: string, createdAt?: any | null, tags: Array<{ __typename?: 'SecretTagType', name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, version: number, comment: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', name: string, color: string }> } | null> | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; +export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, comment: string, createdAt?: any | null, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, version: number, comment: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null } | null } | null> | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; export type GetUserTokensQueryVariables = Exact<{ organisationId: Scalars['ID']; @@ -769,5 +772,5 @@ export const GetEnvironmentKeyDocument = {"kind":"Document","definitions":[{"kin 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 GetSecretNamesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretNames"},"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":"secrets"},"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":"key"}}]}},{"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"}}}}],"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"}}}],"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":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"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":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"eventType"}}]}}]}},{"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":"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 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"}}}}],"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"}}}],"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":"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":"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":"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":"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":"eventType"}}]}}]}},{"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":"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 GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 4a5ca10a5..7eb308256 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -49,6 +49,8 @@ type OrganisationMemberType { wrappedKeyring: String! createdAt: DateTime updatedAt: DateTime! + email: String + username: String } """An enumeration.""" @@ -172,6 +174,7 @@ type SecretTagType { type SecretEventType { id: String! secret: SecretType! + user: OrganisationMemberType key: String! value: String! version: Int! From 876c6d0b8e77670cc552172b52056be6f5142428 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Wed, 30 Aug 2023 17:18:17 +0530 Subject: [PATCH 068/161] fix: get secret history users --- frontend/graphql/queries/secrets/getSecrets.gql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/graphql/queries/secrets/getSecrets.gql b/frontend/graphql/queries/secrets/getSecrets.gql index a345d6d57..c8ddd736b 100644 --- a/frontend/graphql/queries/secrets/getSecrets.gql +++ b/frontend/graphql/queries/secrets/getSecrets.gql @@ -4,6 +4,7 @@ query GetSecrets($appId: ID!, $envId: ID!) { key value tags { + id name color } @@ -14,12 +15,17 @@ query GetSecrets($appId: ID!, $envId: ID!) { key value tags { + id name color } version comment timestamp + user { + email + username + } eventType } } From 5bdc2d8ae99a292867b031c7fa5ea90656454fdc Mon Sep 17 00:00:00 2001 From: Nimish Date: Wed, 30 Aug 2023 20:35:26 +0530 Subject: [PATCH 069/161] feat: added encryption key to user token response --- backend/api/serializers.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 1ec8e2888..8e488078c 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -86,15 +86,18 @@ def to_representation(self, instance): 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', # Adding encryption to each app + } + if index == -1: - apps.append({ - 'id': key.environment.app.id, - 'name': key.environment.app.name, - 'environment_keys': [serializer.data] - }) + app_data['environment_keys'] = [serializer.data] + apps.append(app_data) else: apps[index]['environment_keys'].append(serializer.data) representation['apps'] = apps - return representation + return representation \ No newline at end of file From 727a46409e91e83b9af3ba850da5e1d17cf88f6a Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 13:19:45 +0530 Subject: [PATCH 070/161] feat: add warning button variant --- frontend/components/common/Button.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/components/common/Button.tsx b/frontend/components/common/Button.tsx index b55b5a38d..9900bfa7f 100644 --- a/frontend/components/common/Button.tsx +++ b/frontend/components/common/Button.tsx @@ -32,6 +32,8 @@ function ArrowIcon(props: { className: string }) { const variantStyles: Record = { primary: 'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-emerald-400/10 dark:text-emerald-400 dark:ring-1 dark:ring-inset dark:ring-emerald-400/20 dark:hover:bg-emerald-400/10 dark:hover:text-emerald-300 dark:hover:ring-emerald-300', + warning: + 'rounded-full bg-orange-700 py-1 px-3 text-white hover:bg-orange-600 dark:bg-orange-400/10 dark:text-orange-400 dark:ring-1 dark:ring-inset dark:ring-orange-400/20 dark:hover:bg-orange-400/10 dark:hover:text-orange-300 dark:hover:ring-orange-300', secondary: 'rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300', filled: From b72fe95e6b0c7b7be0c5b22c9aee75bd1d15c9bc Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 13:19:59 +0530 Subject: [PATCH 071/161] feat: misc ui improvements to keyring unlock dialog --- .../components/auth/UnlockKeyringDialog.tsx | 91 ++++++++++++------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/frontend/components/auth/UnlockKeyringDialog.tsx b/frontend/components/auth/UnlockKeyringDialog.tsx index 5512edff4..424b582da 100644 --- a/frontend/components/auth/UnlockKeyringDialog.tsx +++ b/frontend/components/auth/UnlockKeyringDialog.tsx @@ -1,11 +1,13 @@ import { Dialog, Transition } from '@headlessui/react' import { Fragment, useContext, useEffect, useState } from 'react' -import { FaEye, FaEyeSlash, FaTimes } from 'react-icons/fa' +import { FaEye, FaEyeSlash, FaLock, FaTimes } from 'react-icons/fa' import { Button } from '../common/Button' import { KeyringContext } from '@/contexts/keyringContext' import { cryptoUtils } from '@/utils/auth' import { getLocalKeyring } from '@/utils/localStorage' import { useSession } from 'next-auth/react' +import clsx from 'clsx' +import { toast } from 'react-toastify' export default function UnlockKeyringDialog(props: { organisationId: string }) { const [password, setPassword] = useState('') @@ -29,12 +31,21 @@ export default function UnlockKeyringDialog(props: { organisationId: string }) { const decryptLocalKeyring = async (event: { preventDefault: () => void }) => { event.preventDefault() const encryptedKeyring = getLocalKeyring(props.organisationId) - if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' - const deviceKey = await cryptoUtils.deviceVaultKey(password, session?.user?.email!) - const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) - if (!decryptedKeyring) throw 'Failed to decrypt keys' - setKeyring(decryptedKeyring) - closeModal() + if (!encryptedKeyring) { + toast.error('Error fetching local encrypted keys from browser') + return false + } + try { + const deviceKey = await cryptoUtils.deviceVaultKey(password, session?.user?.email!) + const decryptedKeyring = await cryptoUtils.decryptAccountKeyring(encryptedKeyring!, deviceKey) + setKeyring(decryptedKeyring) + toast.success('Unlocked user keyring!', { autoClose: 2000 }) + closeModal() + } catch (e) { + console.log(`Error unlocking user keyring: ${e}`) + toast.error('Failed to decrypt keys. Please verify your sudo password and try again.') + return false + } } return ( @@ -65,38 +76,52 @@ export default function UnlockKeyringDialog(props: { organisationId: string }) { leaveTo="opacity-0 scale-95" > - + +

Unlock User Keyring

-
- -
- setPassword(e.target.value)} - type={showPw ? 'text' : 'password'} - minLength={16} - required - autoFocus - className="w-full " - /> - + Sudo password + +
+ setPassword(e.target.value)} + type={showPw ? 'text' : 'password'} + minLength={16} + required + autoFocus + className="w-full " + /> + +
- + {unsavedChanges && ( + + )}
- {filteredSecrets.map((secret: SecretType) => ( - + {filteredSecrets.map((secret: SecretType, index: number) => ( +
+ {index + 1} + +
))}
diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 7f5e6e8e0..3e1ea40ca 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -109,7 +109,11 @@ a { @layer components { input { - @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-black dark:text-white focus:outline-none focus:ring-1 focus:ring-emerald-500 focus:ring-inset; + @apply p-2 focus:outline-none + } + + input:not(.secrets) { + @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-black dark:text-white focus:ring-1 focus:ring-emerald-500 focus:ring-inset; } textarea { diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index 28d57141f..88582d1ed 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -1,15 +1,204 @@ +import { ApiSecretEventEventTypeChoices, SecretTagType, SecretType } from '@/apollo/graphql' +import { Fragment, useEffect, useState } from 'react' import { - ApiEnvironmentEnvTypeChoices, - ApiSecretEventEventTypeChoices, - SecretType, -} from '@/apollo/graphql' -import { Fragment, useState } from 'react' -import { FaEyeSlash, FaEye, FaTimes, FaRegCommentDots, FaTrashAlt, FaHistory } from 'react-icons/fa' + FaEyeSlash, + FaEye, + FaTimes, + FaRegCommentDots, + FaTrashAlt, + FaHistory, + FaPlus, + FaUser, + FaTags, + FaCheckSquare, + FaSquare, +} from 'react-icons/fa' import { Button } from '../common/Button' import { Dialog, Transition } from '@headlessui/react' - +import { GetSecretTags } from '@/graphql/queries/secrets/getSecretTags.gql' +import { CreateNewSecretTag } from '@/graphql/mutations/environments/createSecretTag.gql' import clsx from 'clsx' import { relativeTimeFromDates } from '@/utils/time' +import { useLazyQuery, useMutation } from '@apollo/client' + +const TagsDialog = (props: { + orgId: string + secretId: string + secretName: string + tags: Array + handlePropertyChange: Function +}) => { + const { orgId, secretId, secretName, tags, handlePropertyChange } = props + + const [getOrgTags, { data: orgTags }] = useLazyQuery(GetSecretTags) + const [createSecretTag] = useMutation(CreateNewSecretTag) + + const [secretTags, setSecretTags] = useState>(tags) + + const [isOpen, setIsOpen] = useState(false) + + const [newTag, setNewTag] = useState>({ name: '', color: '' }) + + useEffect(() => { + if (isOpen) + getOrgTags({ + variables: { + orgId, + }, + }) + }, [getOrgTags, isOpen, orgId]) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const handleClose = () => { + handlePropertyChange(secretId, 'tags', secretTags) + closeModal() + } + + const handleNewTagNameChange = (name: string) => setNewTag({ ...newTag, ...{ name } }) + + const handleNewTagColorChange = (color: string) => setNewTag({ ...newTag, ...{ color } }) + + const handleCreateTag = () => { + const { name, color } = newTag + createSecretTag({ + variables: { + orgId, + name, + color, + }, + refetchQueries: [ + { + query: GetSecretTags, + variables: { + orgId, + }, + }, + ], + }) + } + + const Tag = (props: { id: string; name: string; color: string }) => { + const { id, name, color } = props + + const isSelected = secretTags.map((secretTag) => secretTag.name).includes(name) + + const handleTagClick = () => { + console.log('secretTags', secretTags) + if (isSelected) { + setSecretTags(secretTags.filter((tag) => tag.name !== name)) + } else setSecretTags([...secretTags, ...[{ id, name, color }]]) + } + + return ( +
+ {isSelected ? ( + + ) : ( + + )} +
+
+ {name} +
+
+ ) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Update {secretName} tags +

+ + +
+ +
+
+ {orgTags?.secretTags.map((tag: SecretTagType) => ( + + ))} +
+
+ handleNewTagNameChange(e.target.value)} + /> + handleNewTagColorChange(e.target.value)} + /> +
+ +
+
+ {/*
+ +
*/} +
+
+
+
+
+
+
+ + ) +} const HistoryDialog = (props: { secret: SecretType }) => { const { secret } = props @@ -82,31 +271,32 @@ const HistoryDialog = (props: { secret: SecretType }) => { -
-
- {secret.history?.map((historyItem) => ( -
-
- - {/* {historyItem!.version} */} -
{getEventTypeText(historyItem!.eventType)}
-
- {relativeTimeFromDates(new Date(historyItem!.timestamp))} +
+
+
+ {secret.history?.map((historyItem) => ( +
+
+ + {/* {historyItem!.version} */} +
{getEventTypeText(historyItem!.eventType)}
+
+ {relativeTimeFromDates(new Date(historyItem!.timestamp))} +
+ {/*
{historyItem!.value}
*/} +
+
+ + {historyItem!.user!.username}
- {/*
{historyItem!.value}
*/}
-
- ))} -
-
- + ))} +
@@ -295,28 +485,70 @@ const DeleteConfirmDialog = (props: { } export default function SecretRow(props: { + orgId: string secret: SecretType + cannonicalSecret: SecretType + secretNames: Array> handlePropertyChange: Function handleDelete: Function }) { - const { secret, handlePropertyChange, handleDelete } = props + const { orgId, secret, cannonicalSecret, secretNames, handlePropertyChange, handleDelete } = props const [isRevealed, setIsRevealed] = useState(false) const toggleReveal = () => setIsRevealed(!isRevealed) + const INPUT_BASE_STYLE = + 'w-full text-zinc-300 font-mono secrets bg-white dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-black dark:text-white ' + + const keyIsBlank = secret.key.length === 0 + + const keyIsDuplicate = + secretNames.findIndex((s) => s.key === secret.key && s.id !== secret.id) > -1 + + const isTagSame = (tag1: SecretTagType, tag2: SecretTagType) => { + return tag1.color === tag2.color && tag1.name === tag2.name + } + + const areTagsAreSame = (tags1: SecretTagType[], tags2: SecretTagType[]) => { + if (tags1.length !== tags2.length) return false + + tags1.forEach((tag, index) => { + if (!isTagSame(tag, tags2[index])) return false + }) + + return true + } + + const secretHasBeenModified = () => { + return ( + secret.key !== cannonicalSecret.key || + secret.value !== cannonicalSecret.value || + secret.comment !== cannonicalSecret.comment || + !areTagsAreSame(secret.tags, cannonicalSecret.tags) + ) + } + return (
handlePropertyChange(secret.id, 'key', e.target.value.toUpperCase())} />
handlePropertyChange(secret.id, 'value', e.target.value)} @@ -330,6 +562,13 @@ export default function SecretRow(props: { {isRevealed ? : }{' '} {isRevealed ? 'Mask' : 'Reveal'} + Date: Thu, 31 Aug 2023 14:42:20 +0530 Subject: [PATCH 073/161] fix: use full screen width for secrets --- .../apps/[app]/secrets/environments/[environment]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx index 7528e7d3e..253f84a9b 100644 --- a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx @@ -394,7 +394,7 @@ export default function Environment({ )} {keyring !== null && !loading && ( -
+
{environment.name} {unsavedChanges && ( From 268d79c16525bddd57003bb3cef9d57070975453 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 14:42:37 +0530 Subject: [PATCH 074/161] fix: misc improvements to secret row ui --- .../components/environments/SecretRow.tsx | 151 +++++++++++------- 1 file changed, 93 insertions(+), 58 deletions(-) diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index 88582d1ed..38a76dfba 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -21,6 +21,17 @@ import clsx from 'clsx' import { relativeTimeFromDates } from '@/utils/time' import { useLazyQuery, useMutation } from '@apollo/client' +export const Tag = (props: { tag: SecretTagType }) => { + const { name, color } = props.tag + + return ( +
+
+ {name} +
+ ) +} + const TagsDialog = (props: { orgId: string secretId: string @@ -65,9 +76,12 @@ const TagsDialog = (props: { const handleNewTagColorChange = (color: string) => setNewTag({ ...newTag, ...{ color } }) - const handleCreateTag = () => { + const handleCreateTag = async () => { const { name, color } = newTag - createSecretTag({ + + if (!name) return false + + await createSecretTag({ variables: { orgId, name, @@ -82,15 +96,15 @@ const TagsDialog = (props: { }, ], }) + setNewTag({ name: '', color: '' }) } - const Tag = (props: { id: string; name: string; color: string }) => { - const { id, name, color } = props + const TagSelector = (props: { tag: SecretTagType }) => { + const { id, name, color } = props.tag const isSelected = secretTags.map((secretTag) => secretTag.name).includes(name) const handleTagClick = () => { - console.log('secretTags', secretTags) if (isSelected) { setSecretTags(secretTags.filter((tag) => tag.name !== name)) } else setSecretTags([...secretTags, ...[{ id, name, color }]]) @@ -99,18 +113,12 @@ const TagsDialog = (props: { return (
{isSelected ? ( - + ) : ( )} -
-
- {name} +
+
) @@ -118,11 +126,19 @@ const TagsDialog = (props: { return ( <> -
- -
+ {tags.length > 0 ? ( +
+ {tags.map((tag) => ( + + ))} +
+ ) : ( +
+ +
+ )} @@ -163,7 +179,7 @@ const TagsDialog = (props: {
{orgTags?.secretTags.map((tag: SecretTagType) => ( - + ))}
@@ -178,17 +194,16 @@ const TagsDialog = (props: { onChange={(e) => handleNewTagColorChange(e.target.value)} />
-
- {/*
- -
*/}
@@ -420,9 +435,9 @@ const DeleteConfirmDialog = (props: { return ( <>
-
@@ -499,7 +514,7 @@ export default function SecretRow(props: { const toggleReveal = () => setIsRevealed(!isRevealed) const INPUT_BASE_STYLE = - 'w-full text-zinc-300 font-mono secrets bg-white dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-black dark:text-white ' + 'w-full text-zinc-300 font-mono secrets bg-white dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-black dark:text-white transition ease' const keyIsBlank = secret.key.length === 0 @@ -531,7 +546,7 @@ export default function SecretRow(props: { return (
-
+
handlePropertyChange(secret.id, 'key', e.target.value.toUpperCase())} /> +
+
+ +
+
handlePropertyChange(secret.id, 'value', e.target.value)} /> -
- +
+ +
+ +
+ +
- {isRevealed ? : }{' '} - {isRevealed ? 'Mask' : 'Reveal'} - - - - - + +
+
+ +
) } From e1c6c77373870f5dd1c6db26e4e3deee952044bd Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 14:56:04 +0530 Subject: [PATCH 075/161] fix: active search query causes secret to appear modified --- .../apps/[app]/secrets/environments/[environment]/page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx index 253f84a9b..6a966cfa5 100644 --- a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx @@ -345,6 +345,8 @@ export default function Environment({ return searchRegex.test(secret.key) }) + const cannonicalSecret = (id: string) => secrets.find((secret) => secret.id === id) + const downloadEnvFile = () => { const envContent = secrets .map((secret) => { @@ -455,7 +457,7 @@ export default function Environment({ Date: Thu, 31 Aug 2023 14:56:21 +0530 Subject: [PATCH 076/161] fix: focus styles --- frontend/components/environments/SecretRow.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index 38a76dfba..230a62886 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -502,7 +502,7 @@ const DeleteConfirmDialog = (props: { export default function SecretRow(props: { orgId: string secret: SecretType - cannonicalSecret: SecretType + cannonicalSecret: SecretType | undefined secretNames: Array> handlePropertyChange: Function handleDelete: Function @@ -514,7 +514,7 @@ export default function SecretRow(props: { const toggleReveal = () => setIsRevealed(!isRevealed) const INPUT_BASE_STYLE = - 'w-full text-zinc-300 font-mono secrets bg-white dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-black dark:text-white transition ease' + 'w-full text-zinc-300 font-mono secrets bg-white dark:bg-zinc-800 rounded-sm text-black dark:text-white transition ease' const keyIsBlank = secret.key.length === 0 @@ -536,6 +536,7 @@ export default function SecretRow(props: { } const secretHasBeenModified = () => { + if (cannonicalSecret === undefined) return false return ( secret.key !== cannonicalSecret.key || secret.value !== cannonicalSecret.value || @@ -554,7 +555,7 @@ export default function SecretRow(props: { ? 'ring-1 ring-inset ring-red-500' : keyIsDuplicate ? 'ring-1 ring-inset ring-orange-500' - : 'focus:ring-1 focus:ring-inset focus:ring-emerald-500', + : 'focus:ring-1 focus:ring-inset focus:ring-zinc-500', secretHasBeenModified() && '!text-orange-500' )} value={secret.key} @@ -579,7 +580,7 @@ export default function SecretRow(props: {
handlePropertyChange(secret.id, 'value', e.target.value)} From 185616a328ff981f77b4ffda862c29968fa57564 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 18:56:38 +0530 Subject: [PATCH 077/161] fix: misc fixes for light theme --- .../environments/[environment]/page.tsx | 6 +-- .../app/[team]/apps/[app]/secrets/page.tsx | 15 ++++--- frontend/app/globals.css | 10 +++-- .../components/auth/UnlockKeyringDialog.tsx | 4 +- .../components/environments/SecretRow.tsx | 43 ++++++++++++------- 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx index 6a966cfa5..bed446e4d 100644 --- a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx @@ -397,7 +397,7 @@ export default function Environment({ )} {keyring !== null && !loading && (
-
+
{environment.name} {unsavedChanges && ( )}
-
+
setSearchQuery(e.target.value)} /> diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index 0d224127d..5cf5525c6 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -126,6 +126,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin useEffect(() => { if (keyring !== null && data?.appEnvironments) fetchAndDecryptAppEnvs(data?.appEnvironments) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [data?.appEnvironments, keyring]) const initAppEnvs = async () => { @@ -211,7 +212,7 @@ export default function Secrets({ params }: { params: { team: string; app: strin const pathname = usePathname() return ( -
+
{env.name}
@@ -255,8 +256,8 @@ export default function Secrets({ params }: { params: { team: string; app: strin
) : ( <> -
-
+
+
KEY
{commonSecretsKeys.map((secret: string, index: number) => ( @@ -266,9 +267,11 @@ export default function Secrets({ params }: { params: { team: string; app: strin ))}
- {sortedEnvSecrets.map((envS: EnvSecrets) => ( - - ))} +
+ {sortedEnvSecrets.map((envS: EnvSecrets) => ( + + ))} +
{userTokensData?.userTokens.map((userToken: UserTokenType) => ( diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 3e1ea40ca..ca8f0b296 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -39,6 +39,8 @@ --callout-border-rgb: 172, 175, 176; --card-rgb: 180, 185, 188; --card-border-rgb: 131, 134, 135; + + --toastify-color-success: rgb(16 185 129)!important; } @media (prefers-color-scheme: dark) { @@ -71,6 +73,8 @@ --callout-border-rgb: 108, 108, 108; --card-rgb: 100, 100, 100; --card-border-rgb: 200, 200, 200; + + --toastify-color-success: rgb(16 185 129)!important; } } @@ -113,14 +117,14 @@ a { } input:not(.secrets) { - @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-black dark:text-white focus:ring-1 focus:ring-emerald-500 focus:ring-inset; + @apply bg-zinc-100 dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-zinc-800 dark:text-white ring-1 ring-inset ring-zinc-200 focus:ring-1 focus:ring-emerald-500 focus:ring-inset; } textarea { - @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-black dark:text-white border border-zinc-300 dark:border-none focus:outline outline-emerald-500; + @apply bg-zinc-100 dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-zinc-800 dark:text-white border border-zinc-300 dark:border-none focus:outline outline-emerald-500; } select { - @apply bg-white dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-black dark:text-white border border-zinc-300 dark:border-none focus:outline outline-emerald-500; + @apply bg-zinc-100 dark:bg-zinc-800 dark:bg-opacity-60 p-2 rounded-md text-zinc-800 dark:text-white border border-zinc-300 dark:border-none focus:outline outline-emerald-500; } } diff --git a/frontend/components/auth/UnlockKeyringDialog.tsx b/frontend/components/auth/UnlockKeyringDialog.tsx index 424b582da..a834d65d9 100644 --- a/frontend/components/auth/UnlockKeyringDialog.tsx +++ b/frontend/components/auth/UnlockKeyringDialog.tsx @@ -75,7 +75,7 @@ export default function UnlockKeyringDialog(props: { organisationId: string }) { leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > - +
@@ -168,7 +168,11 @@ const TagsDialog = (props: {

- Update {secretName} tags + Update{' '} + + {secretName} + {' '} + tags

-
@@ -470,7 +478,10 @@ const DeleteConfirmDialog = (props: {

- Delete {secretName} + Delete{' '} + + {secretName} +

-
+
{sortedEnvSecrets.map((envS: EnvSecrets) => ( ))} From 24ac5478880ef93f60bb98ec5df9464812a80f4c Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 19:03:10 +0530 Subject: [PATCH 080/161] feat: add danger button variant --- frontend/components/common/Button.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/components/common/Button.tsx b/frontend/components/common/Button.tsx index 9900bfa7f..be7f4c512 100644 --- a/frontend/components/common/Button.tsx +++ b/frontend/components/common/Button.tsx @@ -34,6 +34,8 @@ const variantStyles: Record = { 'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-emerald-400/10 dark:text-emerald-400 dark:ring-1 dark:ring-inset dark:ring-emerald-400/20 dark:hover:bg-emerald-400/10 dark:hover:text-emerald-300 dark:hover:ring-emerald-300', warning: 'rounded-full bg-orange-700 py-1 px-3 text-white hover:bg-orange-600 dark:bg-orange-400/10 dark:text-orange-400 dark:ring-1 dark:ring-inset dark:ring-orange-400/20 dark:hover:bg-orange-400/10 dark:hover:text-orange-300 dark:hover:ring-orange-300', + danger: + 'rounded-full bg-red-700 py-1 px-3 text-white hover:bg-red-600 dark:bg-red-400/10 dark:text-red-400 dark:ring-1 dark:ring-inset dark:ring-red-400/20 dark:hover:bg-red-400/10 dark:hover:text-red-300 dark:hover:ring-red-300', secondary: 'rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300', filled: From ee0da19a4399c6e3e96e124b3aa14e9b779413bf Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 19:03:21 +0530 Subject: [PATCH 081/161] feat: use danger button variant for deletes --- frontend/components/environments/SecretRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index 47a63abbb..ead445330 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -495,7 +495,7 @@ const DeleteConfirmDialog = (props: { -
From f165e2722eb49dac588dfea3976e81acb72e72fd Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 31 Aug 2023 19:04:40 +0530 Subject: [PATCH 082/161] fix: use danger button variant for inline delete button --- frontend/components/environments/SecretRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index ead445330..b446939c4 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -443,7 +443,7 @@ const DeleteConfirmDialog = (props: { return ( <>
- From 0cae6ab1513cdc3a3c8c8e09f1440efa76838985 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 1 Sep 2023 13:07:46 +0530 Subject: [PATCH 084/161] fix: input buttons overlapping text --- .../[app]/secrets/environments/[environment]/page.tsx | 2 +- frontend/app/globals.css | 2 +- frontend/components/auth/UnlockKeyringDialog.tsx | 8 ++++---- frontend/components/environments/SecretRow.tsx | 11 ++++++----- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx index bed446e4d..67b6f8f2f 100644 --- a/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/environments/[environment]/page.tsx @@ -415,7 +415,7 @@ export default function Environment({
setSearchQuery(e.target.value)} /> diff --git a/frontend/app/globals.css b/frontend/app/globals.css index ca8f0b296..0c63347cf 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -116,7 +116,7 @@ a { @apply p-2 focus:outline-none } - input:not(.secrets) { + input:not(.custom) { @apply bg-zinc-100 dark:bg-zinc-800 dark:bg-opacity-60 rounded-md text-zinc-800 dark:text-white ring-1 ring-inset ring-zinc-200 focus:ring-1 focus:ring-emerald-500 focus:ring-inset; } diff --git a/frontend/components/auth/UnlockKeyringDialog.tsx b/frontend/components/auth/UnlockKeyringDialog.tsx index a834d65d9..d5e2d54f2 100644 --- a/frontend/components/auth/UnlockKeyringDialog.tsx +++ b/frontend/components/auth/UnlockKeyringDialog.tsx @@ -95,14 +95,14 @@ export default function UnlockKeyringDialog(props: { organisationId: string }) {

-
+
-
+
- {filteredSecrets.map((secret: SecretType, index: number) => ( + {filteredSecrets.map((secret, index: number) => (
{index + 1} - +
+
+ +
+

+ Service tokens +

+ {serviceTokensData?.serviceTokens.map((serviceToken: ServiceTokenType) => ( +
+ {serviceToken.name} | {serviceToken.createdAt} +
+ ))} + {serviceToken} +
+ +
- ))} - {userToken} -
-
diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index 47efc7540..4579ec8d9 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -4,7 +4,7 @@ import { SecretTagType, SecretType, } from '@/apollo/graphql' -import { FormEvent, Fragment, useEffect, useState } from 'react' +import { Fragment, useEffect, useState } from 'react' import { FaEyeSlash, FaEye, diff --git a/frontend/graphql/mutations/environments/createEnvironmentKey.gql b/frontend/graphql/mutations/environments/createEnvironmentKey.gql index 745ca3581..11801aba6 100644 --- a/frontend/graphql/mutations/environments/createEnvironmentKey.gql +++ b/frontend/graphql/mutations/environments/createEnvironmentKey.gql @@ -1,5 +1,5 @@ -mutation CreateEnvKey($envId: ID!, $ownerId: ID!,$wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) { - createEnvironmentKey(envId: $envId, userId: $ownerId, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt, identityKey: $identityKey) { +mutation CreateEnvKey($envId: ID!, $userId: ID,$wrappedSeed: String!, $wrappedSalt: String!, $identityKey: String!) { + createEnvironmentKey(envId: $envId, userId: $userId, wrappedSeed: $wrappedSeed, wrappedSalt: $wrappedSalt, identityKey: $identityKey) { environmentKey { id createdAt diff --git a/frontend/graphql/mutations/environments/createServiceToken.gql b/frontend/graphql/mutations/environments/createServiceToken.gql new file mode 100644 index 000000000..1a8020605 --- /dev/null +++ b/frontend/graphql/mutations/environments/createServiceToken.gql @@ -0,0 +1,9 @@ +mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt) { + createServiceToken(appId: $appId, environmentKeys: $environmentKeys, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare, name: $name, expiry: $expiry) { + serviceToken { + id + createdAt + expiresAt + } + } +} diff --git a/frontend/graphql/queries/secrets/getServiceTokens.gql b/frontend/graphql/queries/secrets/getServiceTokens.gql new file mode 100644 index 000000000..8634be426 --- /dev/null +++ b/frontend/graphql/queries/secrets/getServiceTokens.gql @@ -0,0 +1,12 @@ +query GetServiceTokens($appId: ID!) { + serviceTokens(appId: $appId) { + id + name + createdAt + expiresAt + keys { + id + + } + } +} diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index c3b2ab485..cdcc81683 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -13,6 +13,7 @@ import { encryptAsymmetric, getUserKxPrivateKey, getUserKxPublicKey, + randomKeyPair, } from './crypto' export type EnvKeyring = { @@ -75,9 +76,9 @@ export const newEnvWrapKey = async () => { /** * Encrypts an env seed with the given key * - * @param seed - Env seed as a hex string - * @param key - Encryption key as a hex string - * @returns {Promise} + * @param {string} seed - Env seed as a hex string + * @param {string} key - Encryption key as a hex string + * @returns {Promise} - hex encoded encrypted seed */ export const encryptedEnvSeed = async (seed: string, key: string) => { await _sodium.ready @@ -91,8 +92,8 @@ export const encryptedEnvSeed = async (seed: string, key: string) => { /** * Decrypts an env seed with the given key * - * @param encryptedSeed - Encrytped env seed as a hex string - * @param key - Decryption key as a hex string + * @param {string} encryptedSeed - Encrypted env seed as a hex string + * @param {string} key - Decryption key as a hex string * @returns {Promise} - hex encoded plaintext app seed */ export const decryptedAppSeed = async (encryptedSeed: string, key: string) => { @@ -124,6 +125,26 @@ export const envKeyring = async (envSeed: string): Promise => { return { publicKey: sodium.to_hex(publicKey), privateKey: sodium.to_hex(privateKey) } } +export const newServiceTokenKeys = async () => { + await _sodium.ready + const sodium = _sodium + + const { publicKey, privateKey } = await randomKeyPair() + + return { + publicKey: sodium.to_hex(publicKey), + privateKey: sodium.to_hex(privateKey), + } +} + +/** + * Generates an environment token. + * + * @param {EnvironmentType} environment - The environment for which the token is generated. + * @param {EnvironmentKeyType} key - The key associated with the environment. + * @param {{ publicKey: string; privateKey: string }} userKeyring - The user's keyring. + * @returns {Promise<{ pssEnv: string; mutationPayload: object }>} - An object containing the environment token and mutation payload. + */ export const generateEnvironmentToken = async ( environment: EnvironmentType, key: EnvironmentKeyType, @@ -164,6 +185,13 @@ export const generateEnvironmentToken = async ( } } +/** + * Generates a user token. + * + * @param {string} orgId - The organization ID. + * @param {{ publicKey: string; privateKey: string }} userKeyring - The user's keyring. + * @returns {Promise<{ pssUser: string; mutationPayload: object }>} - An object containing the user token and mutation payload. + */ export const generateUserToken = async ( orgId: string, userKeyring: { publicKey: string; privateKey: string } @@ -189,6 +217,13 @@ 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. + * @returns {Promise<{ user: OrganisationMemberType; wrappedSeed: string; wrappedSalt: string }>} - An object containing the wrapped environment secrets and user information. + */ const wrapEnvSecretsForUser = async ( envSecrets: { seed: string; salt: string }, user: OrganisationMemberType @@ -204,6 +239,35 @@ const wrapEnvSecretsForUser = async ( } } +/** + * Wraps environment secrets for a service token. + * + * @param {{ seed: string; salt: string }} envSecrets - The environment secrets to be wrapped. + * @param {string} publicKey - The public key of the service token. + * @returns {Promise<{ wrappedSeed: string; wrappedSalt: string }>} - An object containing the wrapped environment secrets. + */ +export const wrapEnvSecretsForServiceToken = async ( + envSecrets: { seed: string; salt: string }, + publicKey: string +) => { + //const servicePubKey = await getUserKxPublicKey(publicKey) + const wrappedSeed = await encryptAsymmetric(envSecrets.seed, publicKey) + const wrappedSalt = await encryptAsymmetric(envSecrets.salt, publicKey) + + return { + wrappedSeed, + wrappedSalt, + } +} + +/** + * Unwraps environment secrets for a user. + * + * @param {string} wrappedSeed - The wrapped environment seed. + * @param {string} wrappedSalt - The wrapped environment salt. + * @param {OrganisationKeyring} keyring - The keyring of the user. + * @returns {Promise<{ publicKey: string; privateKey: string; salt: string }>} - An object containing the unwrapped environment secrets. + */ export const unwrapEnvSecretsForUser = async ( wrappedSeed: string, wrappedSalt: string, @@ -220,12 +284,20 @@ export const unwrapEnvSecretsForUser = async ( const { publicKey, privateKey } = await envKeyring(seed) return { + seed, publicKey, privateKey, salt, } } +/** + * Decrypts environment secret names. + * + * @param {SecretType[]} encryptedSecrets - An array of encrypted secrets. + * @param {{ publicKey: string; privateKey: string }} envKeys - The environment keys for decryption. + * @returns {Promise} - An array of decrypted secrets. + */ export const decryptEnvSecretNames = async ( encryptedSecrets: SecretType[], envKeys: { publicKey: string; privateKey: string } @@ -245,6 +317,13 @@ export const decryptEnvSecretNames = async ( return decryptedSecrets } +/** + * Decrypts environment secrets. + * + * @param {SecretType[]} encryptedSecrets - An array of encrypted secrets. + * @param {{ publicKey: string; privateKey: string }} envKeys - The environment keys for decryption. + * @returns {Promise} - An array of decrypted secrets. + */ export const decryptEnvSecrets = async ( encryptedSecrets: SecretType[], envKeys: { publicKey: string; privateKey: string } @@ -268,6 +347,15 @@ export const decryptEnvSecrets = async ( return decryptedSecrets } +/** + * Creates a new environment payload. + * + * @param {string} appId - The ID of the application. + * @param {string} name - The name of the environment. + * @param {ApiEnvironmentEnvTypeChoices} envType - The type of environment. + * @param {OrganisationMemberType} user - The user for whom the environment is created. + * @returns {Promise} - An object containing the environment payload. + */ export const createNewEnvPayload = async ( appId: string, name: string, @@ -291,6 +379,13 @@ export const createNewEnvPayload = async ( } } +/** + * Compares two arrays for equality. + * + * @param {any[]} arr1 - The first array. + * @param {any[]} arr2 - The second array. + * @returns {boolean} - True if the arrays are equal, false otherwise. + */ export const arraysEqual = (arr1: any[], arr2: any[]) => { if (arr1.length !== arr2.length) { return false diff --git a/nginx/default.conf b/nginx/default.conf index fb1976f03..7cfb273df 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -65,6 +65,21 @@ server { proxy_cookie_path / "/; HttpOnly; SameSite=strict"; } + location /tokens/service/ { + rewrite ^/tokens/service/(.*) /tokens/service/$1 break; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://backend:8000; + proxy_redirect off; + + proxy_cookie_path / "/; HttpOnly; SameSite=strict"; + } + location / { include /etc/nginx/mime.types; From cafe2ce1817a47da44e97bc3eea934cc3cdf3a6e Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 7 Sep 2023 12:11:59 +0530 Subject: [PATCH 096/161] fix: allow service tokens to fetch secrets --- backend/api/views.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 94e263f88..25dcc391e 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -266,9 +266,7 @@ def get(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'Environment': - env, org_member = get_env_from_service_token(auth_token) - elif token_type == 'User': + if token_type == 'User' or token_type == 'Service': try: env_id = request.headers['environment'] @@ -305,9 +303,8 @@ def get(self, request): def post(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'Environment': - env, user = get_env_from_service_token(auth_token) - elif token_type == 'User': + + if token_type == 'User' or token_type == 'Service': try: env_id = request.headers['environment'] env = Environment.objects.get(id=env_id) @@ -350,9 +347,8 @@ def post(self, request): def put(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'Environment': - env, user = get_env_from_service_token(auth_token) - elif token_type == 'User': + + if token_type == 'User' or token_type == 'Service': try: env_id = request.headers['environment'] env = Environment.objects.get(id=env_id) @@ -397,9 +393,8 @@ def put(self, request): def delete(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'Environment': - env, user = get_env_from_service_token(auth_token) - elif token_type == 'User': + + if token_type == 'User' or token_type == 'Service': try: env_id = request.headers['environment'] user = get_org_member_from_user_token(auth_token) From 3d9fffee24d87ff8e8741ff743560a9dfde8e47c Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 7 Sep 2023 12:17:01 +0530 Subject: [PATCH 097/161] fix: only check user access for user tokens --- backend/api/views.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 25dcc391e..d9f619a82 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -266,11 +266,11 @@ def get(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'User' or token_type == 'Service': - try: - env_id = request.headers['environment'] + env_id = request.headers['environment'] + env = Environment.objects.get(id=env_id) - env = Environment.objects.get(id=env_id) + if token_type == 'User': + try: org_member = get_org_member_from_user_token(auth_token) if not user_can_access_environment(org_member.user.userId, env_id): @@ -304,10 +304,11 @@ def post(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'User' or token_type == 'Service': + env_id = request.headers['environment'] + env = Environment.objects.get(id=env_id) + + if token_type == 'User': try: - env_id = request.headers['environment'] - env = Environment.objects.get(id=env_id) user = get_org_member_from_user_token(auth_token) if not user_can_access_environment(user.user.userId, env_id): @@ -348,10 +349,11 @@ def put(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'User' or token_type == 'Service': + env_id = request.headers['environment'] + env = Environment.objects.get(id=env_id) + + if token_type == 'User': try: - env_id = request.headers['environment'] - env = Environment.objects.get(id=env_id) user = get_org_member_from_user_token(auth_token) if not user_can_access_environment(user.user.userId, env_id): @@ -394,9 +396,10 @@ def delete(self, request): auth_token = request.headers['authorization'] token_type = get_token_type(auth_token) - if token_type == 'User' or token_type == 'Service': + env_id = request.headers['environment'] + + if token_type == 'User': try: - env_id = request.headers['environment'] user = get_org_member_from_user_token(auth_token) if not user_can_access_environment(user.user.userId, env_id): From 63723292c757a0e856e44391b90e30c00a3701e2 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 7 Sep 2023 14:58:10 +0530 Subject: [PATCH 098/161] fix: handle secret events for service tokens --- backend/api/views.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/api/views.py b/backend/api/views.py index d9f619a82..4367bf9dd 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -315,6 +315,8 @@ def post(self, request): return HttpResponse(status=403) except: return HttpResponse(status=404) + else: + user = None if not env: return HttpResponse(status=404) @@ -361,6 +363,9 @@ def put(self, request): except: return HttpResponse(status=404) + else: + user = None + request_body = json.loads(request.body) for secret in request_body['secrets']: @@ -407,6 +412,9 @@ def delete(self, request): except: return HttpResponse(status=404) + else: + user = None + request_body = json.loads(request.body) secrets_to_delete = Secret.objects.filter( @@ -416,7 +424,7 @@ def delete(self, request): if not Secret.objects.filter(id=secret.id).exists(): return HttpResponse(status=404) - if not user_can_access_environment(user.user.userId, secret.environment.id): + if user is not None and not user_can_access_environment(user.user.userId, secret.environment.id): return HttpResponse(status=403) for secret in secrets_to_delete: From 4f53f4179a93dfe6bfef518faf5c99cc4966f09b Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 8 Sep 2023 20:37:38 +0530 Subject: [PATCH 099/161] refactor: tokens tab --- .../backend/graphene/mutations/environment.py | 51 +- backend/backend/schema.py | 8 +- frontend/apollo/gql.ts | 18 +- frontend/apollo/graphql.ts | 47 +- frontend/apollo/schema.graphql | 11 + frontend/app/[team]/apps/[app]/keys/page.tsx | 287 ------- frontend/app/[team]/apps/[app]/layout.tsx | 12 +- .../app/[team]/apps/[app]/secrets/page.tsx | 147 ---- .../app/[team]/apps/[app]/tokens/page.tsx | 323 ++++++++ .../components/apps/tokens/SecretTokens.tsx | 717 ++++++++++++++++++ .../environments/deleteServiceToken.gql | 5 + .../mutations/users/createUserToken.gql | 5 +- .../mutations/users/deleteUserToken.gql | 5 + frontend/utils/environments.ts | 5 +- 14 files changed, 1183 insertions(+), 458 deletions(-) delete mode 100644 frontend/app/[team]/apps/[app]/keys/page.tsx create mode 100644 frontend/app/[team]/apps/[app]/tokens/page.tsx create mode 100644 frontend/components/apps/tokens/SecretTokens.tsx create mode 100644 frontend/graphql/mutations/environments/deleteServiceToken.gql create mode 100644 frontend/graphql/mutations/users/deleteUserToken.gql diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index b2414d709..265cbae24 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -131,6 +131,7 @@ class Arguments: token = graphene.String(required=True) wrapped_key_share = graphene.String(required=True) + ok = graphene.Boolean() user_token = graphene.Field(UserTokenType) @classmethod @@ -144,7 +145,33 @@ def mutate(cls, root, info, org_id, name, identity_key, token, wrapped_key_share user_token = UserToken.objects.create( user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) - return CreateUserTokenMutation(user_token=user_token) + return CreateUserTokenMutation(user_token=user_token, ok=True) + + else: + raise GraphQLError( + "You don't have permission to perform this action") + + +class DeleteUserTokenMutation(graphene.Mutation): + class Arguments: + token_id = graphene.ID(required=True) + + ok = graphene.Boolean() + + @classmethod + def mutate(cls, root, info, token_id): + user = info.context.user + token = UserToken.objects.get(id=token_id) + org = token.user.organisation + + if user_is_org_member(user.userId, org.id): + token.deleted_at = timezone.now() + token.save() + + return DeleteUserTokenMutation(ok=True) + else: + raise GraphQLError( + "You don't have permission to perform this action") class CreateServiceTokenMutation(graphene.Mutation): @@ -185,6 +212,28 @@ def mutate(cls, root, info, app_id, environment_keys, identity_key, token, wrapp return CreateServiceTokenMutation(service_token=service_token) +class DeleteServiceTokenMutation(graphene.Mutation): + class Arguments: + token_id = graphene.ID(required=True) + + ok = graphene.Boolean() + + @classmethod + def mutate(cls, root, info, token_id): + user = info.context.user + token = ServiceToken.objects.get(id=token_id) + org = token.app.organisation + + if user_is_org_member(user.userId, org.id): + token.deleted_at = timezone.now() + token.save() + + return DeleteServiceTokenMutation(ok=True) + else: + raise GraphQLError( + "You don't have permission to perform this action") + + class CreateSecretFolderMutation(graphene.Mutation): class Arguments: id = graphene.ID(required=True) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index f3685cf85..8aa8a5e36 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,4 +1,4 @@ -from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, CreateServiceTokenMutation, CreateUserTokenMutation, DeleteSecretMutation, EditSecretMutation +from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, CreateServiceTokenMutation, CreateUserTokenMutation, DeleteSecretMutation, DeleteServiceTokenMutation, DeleteUserTokenMutation, EditSecretMutation from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation from .graphene.mutations.organisation import CreateOrganisationMutation @@ -142,14 +142,14 @@ def resolve_user_tokens(root, info, organisation_id): org_member = OrganisationMember.objects.get( user=info.context.user, organisation_id=organisation_id) - return UserToken.objects.filter(user=org_member) + return UserToken.objects.filter(user=org_member, deleted_at=None) def resolve_service_tokens(root, info, app_id): app = App.objects.get(id=app_id) if not user_is_org_member(info.context.user.userId, app.organisation.id): raise GraphQLError("You don't have access to this organisation") - return ServiceToken.objects.filter(app=app) + return ServiceToken.objects.filter(app=app, deleted_at=None) def resolve_logs(root, info, app_id, start=0, end=0): if not user_can_access_app(info.context.user.userId, app_id): @@ -273,7 +273,9 @@ class Mutation(graphene.ObjectType): create_environment_key = CreateEnvironmentKeyMutation.Field() create_environment_token = CreateEnvironmentTokenMutation.Field() create_user_token = CreateUserTokenMutation.Field() + delete_user_token = DeleteUserTokenMutation.Field() create_service_token = CreateServiceTokenMutation.Field() + delete_service_token = DeleteServiceTokenMutation.Field() create_secret_folder = CreateSecretFolderMutation.Field() create_secret_tag = CreateSecretTagMutation.Field() create_secret = CreateSecretMutation.Field() diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index d2eb831ce..69aff8021 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -21,12 +21,13 @@ const documents = { "mutation CreateEnvToken($envId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createEnvironmentToken(\n envId: $envId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n environmentToken {\n id\n createdAt\n }\n }\n}": types.CreateEnvTokenDocument, "mutation CreateNewSecret($newSecret: SecretInput!) {\n createSecret(secretData: $newSecret) {\n secret {\n id\n key\n value\n createdAt\n }\n }\n}": types.CreateNewSecretDocument, "mutation CreateNewSecretTag($orgId: ID!, $name: String!, $color: String!) {\n createSecretTag(orgId: $orgId, name: $name, color: $color) {\n tag {\n id\n }\n }\n}": types.CreateNewSecretTagDocument, - "mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt!) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}": types.CreateNewServiceTokenDocument, + "mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}": types.CreateNewServiceTokenDocument, "mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\n id\n }\n }\n}": types.DeleteSecretOpDocument, "mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}": types.UpdateSecretDocument, "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, "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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}": types.CreateNewUserTokenDocument, + "mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n ok\n }\n}": types.CreateNewUserTokenDocument, + "mutation RevokeUserToken($tokenId: ID!) {\n deleteUserToken(tokenId: $tokenId) {\n ok\n }\n}": types.RevokeUserTokenDocument, "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 }\n}": types.GetAppDetailDocument, "query GetAppLogCount($appId: ID!, $thisMonth: Boolean) {\n logsCount(appId: $appId, thisMonth: $thisMonth)\n}": types.GetAppLogCountDocument, @@ -41,6 +42,7 @@ const documents = { "query GetSecretNames($envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretNamesDocument, "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, + "query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n expiresAt\n keys {\n id\n }\n }\n}": types.GetServiceTokensDocument, "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetUserTokensDocument, }; @@ -93,7 +95,7 @@ export function graphql(source: "mutation CreateNewSecretTag($orgId: ID!, $name: /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt!) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}"): (typeof documents)["mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt!) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}"]; +export function graphql(source: "mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}"): (typeof documents)["mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -113,7 +115,11 @@ 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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n userToken {\n id\n createdAt\n }\n }\n}"]; +export function graphql(source: "mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n ok\n }\n}"): (typeof documents)["mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\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 RevokeUserToken($tokenId: ID!) {\n deleteUserToken(tokenId: $tokenId) {\n ok\n }\n}"): (typeof documents)["mutation RevokeUserToken($tokenId: ID!) {\n deleteUserToken(tokenId: $tokenId) {\n ok\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -170,6 +176,10 @@ 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!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}"): (typeof documents)["query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\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 GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n expiresAt\n keys {\n id\n }\n }\n}"): (typeof documents)["query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n expiresAt\n keys {\n id\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 b40c7809e..271bce9e3 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -134,6 +134,7 @@ export type CreateServiceTokenMutation = { export type CreateUserTokenMutation = { __typename?: 'CreateUserTokenMutation'; + ok?: Maybe; userToken?: Maybe; }; @@ -147,6 +148,16 @@ export type DeleteSecretMutation = { secret?: Maybe; }; +export type DeleteServiceTokenMutation = { + __typename?: 'DeleteServiceTokenMutation'; + ok?: Maybe; +}; + +export type DeleteUserTokenMutation = { + __typename?: 'DeleteUserTokenMutation'; + ok?: Maybe; +}; + export type EditSecretMutation = { __typename?: 'EditSecretMutation'; secret?: Maybe; @@ -234,6 +245,8 @@ export type Mutation = { createUserToken?: Maybe; deleteApp?: Maybe; deleteSecret?: Maybe; + deleteServiceToken?: Maybe; + deleteUserToken?: Maybe; editSecret?: Maybe; rotateAppKeys?: Maybe; }; @@ -331,6 +344,16 @@ export type MutationDeleteSecretArgs = { }; +export type MutationDeleteServiceTokenArgs = { + tokenId: Scalars['ID']; +}; + + +export type MutationDeleteUserTokenArgs = { + tokenId: Scalars['ID']; +}; + + export type MutationEditSecretArgs = { id: Scalars['ID']; secretData?: InputMaybe; @@ -641,7 +664,7 @@ export type CreateNewServiceTokenMutationVariables = Exact<{ token: Scalars['String']; wrappedKeyShare: Scalars['String']; name: Scalars['String']; - expiry: Scalars['BigInt']; + expiry?: InputMaybe; }>; @@ -689,7 +712,14 @@ export type CreateNewUserTokenMutationVariables = Exact<{ }>; -export type CreateNewUserTokenMutation = { __typename?: 'Mutation', createUserToken?: { __typename?: 'CreateUserTokenMutation', userToken?: { __typename?: 'UserTokenType', id: string, createdAt?: any | null } | null } | null }; +export type CreateNewUserTokenMutation = { __typename?: 'Mutation', createUserToken?: { __typename?: 'CreateUserTokenMutation', ok?: boolean | null } | null }; + +export type RevokeUserTokenMutationVariables = Exact<{ + tokenId: Scalars['ID']; +}>; + + +export type RevokeUserTokenMutation = { __typename?: 'Mutation', deleteUserToken?: { __typename?: 'DeleteUserTokenMutation', ok?: boolean | null } | null }; export type GetAppActivityChartQueryVariables = Exact<{ appId: Scalars['ID']; @@ -795,6 +825,13 @@ export type GetSecretsQueryVariables = Exact<{ export type GetSecretsQuery = { __typename?: 'Query', secrets?: Array<{ __typename?: 'SecretType', id: string, key: string, value: string, comment: string, createdAt?: any | null, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, history?: Array<{ __typename?: 'SecretEventType', id: string, key: string, value: string, version: number, comment: string, timestamp: any, eventType: ApiSecretEventEventTypeChoices, tags: Array<{ __typename?: 'SecretTagType', id: string, name: string, color: string }>, user?: { __typename?: 'OrganisationMemberType', email?: string | null, username?: string | null } | null } | null> | null } | null> | null, appEnvironments?: Array<{ __typename?: 'EnvironmentType', id: string, name: string, envType: ApiEnvironmentEnvTypeChoices, identityKey: string } | null> | null, environmentKeys?: Array<{ __typename?: 'EnvironmentKeyType', id: string, identityKey: string, wrappedSeed: string, wrappedSalt: string } | null> | null }; +export type GetServiceTokensQueryVariables = Exact<{ + appId: Scalars['ID']; +}>; + + +export type GetServiceTokensQuery = { __typename?: 'Query', serviceTokens?: Array<{ __typename?: 'ServiceTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, keys: Array<{ __typename?: 'EnvironmentKeyType', id: string }> } | null> | null }; + export type GetUserTokensQueryVariables = Exact<{ organisationId: Scalars['ID']; }>; @@ -811,12 +848,13 @@ export const CreateEnvKeyDocument = {"kind":"Document","definitions":[{"kind":"O export const CreateEnvTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEnvToken"},"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":"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"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEnvironmentToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"environmentToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecret"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"newSecret"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"newSecret"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"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":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewSecretTagDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecretTag"},"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":"color"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecretTag"},"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":"color"},"value":{"kind":"Variable","name":{"kind":"Name","value":"color"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tag"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateNewServiceTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewServiceToken"},"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":"environmentKeys"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentKeyInput"}}}},{"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":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentKeys"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environmentKeys"}}},{"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":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"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":"serviceToken"},"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"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateNewServiceTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewServiceToken"},"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":"environmentKeys"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentKeyInput"}}}},{"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":"name"}},"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":"createServiceToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentKeys"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environmentKeys"}}},{"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":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"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":"serviceToken"},"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"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteSecretOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSecretOp"},"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":"deleteSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSecret"},"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":"secretData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} 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 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"}}}}],"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +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"}}}}],"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"}}}],"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 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"}}]}}]}}]} as unknown as DocumentNode; export const GetAppLogCountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppLogCount"},"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":"thisMonth"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logsCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"thisMonth"},"value":{"kind":"Variable","name":{"kind":"Name","value":"thisMonth"}}}]}]}}]} as unknown as DocumentNode; @@ -831,4 +869,5 @@ export const GetEnvironmentTokensDocument = {"kind":"Document","definitions":[{" export const GetSecretNamesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretNames"},"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":"secrets"},"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":"key"}}]}},{"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"}}}}],"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"}}}],"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":"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":"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":"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":"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":"eventType"}}]}}]}},{"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":"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 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":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"keys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index abd84d267..f3ce4b7a8 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -250,7 +250,9 @@ type Mutation { createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation createUserToken(identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation + deleteUserToken(tokenId: ID!): DeleteUserTokenMutation createServiceToken(appId: ID!, environmentKeys: [EnvironmentKeyInput], expiry: BigInt, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateServiceTokenMutation + deleteServiceToken(tokenId: ID!): DeleteServiceTokenMutation createSecretFolder(envId: ID!, id: ID!, name: String!, parentFolderId: ID): CreateSecretFolderMutation createSecretTag(color: String!, name: String!, orgId: ID!): CreateSecretTagMutation createSecret(secretData: SecretInput): CreateSecretMutation @@ -296,9 +298,14 @@ type CreateEnvironmentTokenMutation { } type CreateUserTokenMutation { + ok: Boolean userToken: UserTokenType } +type DeleteUserTokenMutation { + ok: Boolean +} + type CreateServiceTokenMutation { serviceToken: ServiceTokenType } @@ -311,6 +318,10 @@ input EnvironmentKeyInput { wrappedSalt: String! } +type DeleteServiceTokenMutation { + ok: Boolean +} + type CreateSecretFolderMutation { folder: SecretFolderType } diff --git a/frontend/app/[team]/apps/[app]/keys/page.tsx b/frontend/app/[team]/apps/[app]/keys/page.tsx deleted file mode 100644 index 69d073681..000000000 --- a/frontend/app/[team]/apps/[app]/keys/page.tsx +++ /dev/null @@ -1,287 +0,0 @@ -'use client' - -import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' -import { GetAppDetail } from '@/graphql/queries/getAppDetail.gql' -import { RotateAppKey } from '@/graphql/mutations/rotateAppKeys.gql' -import { useLazyQuery, useQuery, useMutation } from '@apollo/client' -import { AppType, ChartDataPointType, TimeRange } from '@/apollo/graphql' -import { Fragment, useEffect, useState } from 'react' -import { Button } from '@/components/common/Button' -import { copyToClipBoard } from '@/utils/clipboard' -import { - FaCopy, - FaExclamationTriangle, - FaEye, - FaEyeSlash, - FaInfo, - FaMagic, - FaTimes, -} from 'react-icons/fa' -import { MdContentCopy, MdOutlineRotateLeft } from 'react-icons/md' -import { toast } from 'react-toastify' -import { Dialog, Transition } from '@headlessui/react' -import { cryptoUtils } from '@/utils/auth' -import { useSession } from 'next-auth/react' -import { getLocalKeyring } from '@/utils/localStorage' -import { splitSecret } from '@/utils/keyshares' -import { Alert } from '@/components/common/Alert' - -export default function App({ params }: { params: { team: string; app: string } }) { - const { data: orgsData } = useQuery(GetOrganisations) - const [getApp, { data }] = useLazyQuery(GetAppDetail) - - const app = data?.apps[0] as AppType - - const appId = `phApp:v${app?.appVersion}:${app?.identityKey}` - - const [appSecret, setAppSecret] = useState('') - - const { data: session } = useSession() - - const appSecretPlaceholder = '*'.repeat(295) - - useEffect(() => { - if (orgsData) { - const organisationId = orgsData.organisations[0].id - getApp({ - variables: { - organisationId, - appId: params.app, - }, - }) - } - }, [getApp, orgsData, params.app]) - - const handleCopy = (val: string) => { - copyToClipBoard(val) - toast.info('Copied') - } - - const RotateAppDialog = () => { - const [pw, setPw] = useState('') - const [showPw, setShowPw] = useState(false) - const [loading, setLoading] = useState(false) - const [isOpen, setIsOpen] = useState(false) - const [rotateAppKeys] = useMutation(RotateAppKey) - - const closeModal = () => { - setPw('') - setIsOpen(false) - } - - const handleGenerateNewAppKey = async () => { - const APP_VERSION = 1 - - return new Promise(async (resolve, reject) => { - setTimeout(async () => { - setLoading(true) - try { - const wrapKey = await cryptoUtils.newAppWrapKey() - const newAppToken = await cryptoUtils.newAppToken() - const deviceKey = await cryptoUtils.deviceVaultKey(pw, session?.user?.email!) - const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) - if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' - const decryptedKeyring = await cryptoUtils.decryptAccountKeyring( - encryptedKeyring!, - deviceKey - ) - if (!decryptedKeyring) throw 'Failed to decrypt keys' - - const appSeed = await cryptoUtils.decryptedAppSeed( - app.appSeed, - decryptedKeyring.symmetricKey - ) - - const appKeys = await cryptoUtils.appKeyring(appSeed) - const appKeyShares = await splitSecret(appKeys.privateKey) - const wrappedShare = await cryptoUtils.wrappedKeyShare(appKeyShares[1], wrapKey) - await rotateAppKeys({ - variables: { - id: app.id, - appToken: newAppToken, - wrappedKeyShare: wrappedShare, - }, - }) - - setAppSecret(`pss:v${APP_VERSION}:${newAppToken}:${appKeyShares[0]}:${wrapKey}`) - - setLoading(false) - resolve(true) - } catch (error) { - console.log(error) - setLoading(false) - reject() - } - }, 500) - }) - } - - const handleSubmit = async (event: { preventDefault: () => void }) => { - event.preventDefault() - toast - .promise(handleGenerateNewAppKey, { - pending: 'Generating app keys', - success: 'Success!', - error: 'Something went wrong! Please check your password and try again.', - }) - .then(() => closeModal()) - } - - return ( - <> - - - {}}> - -
- - -
-
- - - -

- Genereate new app secret -

- -
- - - Generate a new app secret for {app.name} - - - -
-
- -
- -
- Warning: This will revoke your current app keys. Your application - won't be able to decrypt data using the current keys. -
-
-
- - -
- -
- Your new keys will be available to use immediately. You will be able - to decrypt any existing data with your new keys. Please allow up to - 60 seconds for your old keys to be revoked. -
-
-
-
- -
- -
- setPw(e.target.value)} - type={showPw ? 'text' : 'password'} - minLength={16} - required - className="w-full " - /> - -
-
-
- - -
-
- -
-
-
-
-
-
- - ) - } - - return ( -
-
- {/*

keys

*/} - {app && ( -
-
-
- app id - -
- {appId} -
- -
-
- app secret -
- {appSecret && ( -
- -
{"Copy this value. You won't see it again!"}
-
- )} - {appSecret && ( - - )} -
- {!appSecret && } -
- {appSecret || appSecretPlaceholder} -
-
- )} -
-
- ) -} diff --git a/frontend/app/[team]/apps/[app]/layout.tsx b/frontend/app/[team]/apps/[app]/layout.tsx index 10fcafc1c..55c55c90b 100644 --- a/frontend/app/[team]/apps/[app]/layout.tsx +++ b/frontend/app/[team]/apps/[app]/layout.tsx @@ -42,8 +42,8 @@ export default function AppLayout({ if (app) { const currentUrl = path?.split('/')[4] if (currentUrl === '') return 0 - if (currentUrl === 'keys') return 1 - if (currentUrl === 'secrets') return 2 + if (currentUrl === 'secrets') return 1 + if (currentUrl === 'tokens') return 2 if (currentUrl === 'logs') return 3 if (currentUrl === 'settings') return 4 } @@ -57,14 +57,14 @@ export default function AppLayout({ name: 'Home', link: '', }, - { - name: 'Keys', - link: 'keys', - }, { name: 'Secrets', link: 'secrets', }, + { + name: 'Tokens', + link: 'tokens', + }, { name: 'Logs', link: 'logs', diff --git a/frontend/app/[team]/apps/[app]/secrets/page.tsx b/frontend/app/[team]/apps/[app]/secrets/page.tsx index 917e700eb..d1e79c31d 100644 --- a/frontend/app/[team]/apps/[app]/secrets/page.tsx +++ b/frontend/app/[team]/apps/[app]/secrets/page.tsx @@ -15,33 +15,22 @@ import { useCallback, useContext, useEffect, useState } from 'react' import { createNewEnvPayload, decryptEnvSecretNames, - generateUserToken, - newEnvToken, - newEnvWrapKey, - newServiceTokenKeys, unwrapEnvSecretsForUser, - wrapEnvSecretsForServiceToken, } from '@/utils/environments' import { Button } from '@/components/common/Button' import { ApiEnvironmentEnvTypeChoices, ApiOrganisationMemberRoleChoices, - EnvironmentKeyInput, EnvironmentType, OrganisationMemberType, SecretType, - ServiceTokenType, - UserTokenType, } from '@/apollo/graphql' -import { getUserKxPrivateKey, getUserKxPublicKey, randomKeyPair } from '@/utils/crypto' import _sodium from 'libsodium-wrappers-sumo' import { KeyringContext } from '@/contexts/keyringContext' import UnlockKeyringDialog from '@/components/auth/UnlockKeyringDialog' import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa' import Link from 'next/link' import { usePathname } from 'next/navigation' -import { splitSecret } from '@/utils/keyshares' -import { cryptoUtils } from '@/utils/auth' type EnvSecrets = { env: EnvironmentType @@ -187,107 +176,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin }) } - const handleCreateNewUserToken = async () => { - if (keyring) { - const userKxKeys = { - publicKey: await getUserKxPublicKey(keyring.publicKey), - privateKey: await getUserKxPrivateKey(keyring.privateKey), - } - - const { pssUser, mutationPayload } = await generateUserToken( - orgsData.organisations[0].id, - userKxKeys - ) - - await createUserToken({ - variables: mutationPayload, - refetchQueries: [ - { - query: GetUserTokens, - variables: { - organisationId: orgsData.organisations[0].id, - }, - }, - ], - }) - - setUserToken(pssUser) - } else { - console.log('keyring unavailable') - } - } - - const handleCreateNewServiceToken = async () => { - if (keyring) { - const appEnvironments = data.appEnvironments as EnvironmentType[] - - const token = await newEnvToken() - const wrapKey = await newEnvWrapKey() - - const tokenKeys = await newServiceTokenKeys() - const keyShares = await splitSecret(tokenKeys.privateKey) - const wrappedKeyShare = await cryptoUtils.wrappedKeyShare(keyShares[1], wrapKey) - - const pssService = `pss_service:v1:${token}:${tokenKeys.publicKey}:${keyShares[0]}:${wrapKey}` - - const envKeyPromises = appEnvironments.map(async (env: EnvironmentType) => { - const { data } = await getEnvKey({ - variables: { - envId: env.id, - }, - }) - - const { - wrappedSeed: userWrappedSeed, - wrappedSalt: userWrappedSalt, - identityKey, - } = data.environmentKeys[0] - - const { seed, salt } = await unwrapEnvSecretsForUser( - userWrappedSeed, - userWrappedSalt, - keyring! - ) - - const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForServiceToken( - { seed, salt }, - tokenKeys.publicKey - ) - - return { - envId: env.id, - identityKey, - wrappedSeed, - wrappedSalt, - } - }) - - const envKeyInputs = await Promise.all(envKeyPromises) - - await createServiceToken({ - variables: { - appId: params.app, - environmentKeys: envKeyInputs, - identityKey: tokenKeys.publicKey, - token, - wrappedKeyShare, - name: 'testServiceToken', - expiry: null, - }, - refetchQueries: [ - { - query: GetServiceTokens, - variables: { - appId: params.app, - }, - }, - ], - }) - - setServiceToken(pssService) - } - } - const EnvCard = (props: { envSecrets: EnvSecrets }) => { const { env, secrets } = props.envSecrets @@ -360,41 +248,6 @@ export default function Secrets({ params }: { params: { team: string; app: strin ))} -
-
-

- User tokens -

- {userTokensData?.userTokens.map((userToken: UserTokenType) => ( -
- {userToken.name} | {userToken.createdAt} -
- ))} - {userToken} -
- -
-
- -
-

- Service tokens -

- {serviceTokensData?.serviceTokens.map((serviceToken: ServiceTokenType) => ( -
- {serviceToken.name} | {serviceToken.createdAt} -
- ))} - {serviceToken} -
- -
-
-
)} diff --git a/frontend/app/[team]/apps/[app]/tokens/page.tsx b/frontend/app/[team]/apps/[app]/tokens/page.tsx new file mode 100644 index 000000000..0af7bf6ba --- /dev/null +++ b/frontend/app/[team]/apps/[app]/tokens/page.tsx @@ -0,0 +1,323 @@ +'use client' + +import { GetOrganisations } from '@/graphql/queries/getOrganisations.gql' +import { GetAppDetail } from '@/graphql/queries/getAppDetail.gql' +import { RotateAppKey } from '@/graphql/mutations/rotateAppKeys.gql' +import { useLazyQuery, useQuery, useMutation } from '@apollo/client' +import { AppType } from '@/apollo/graphql' +import { Fragment, useContext, useEffect, useState } from 'react' +import { Button } from '@/components/common/Button' +import { copyToClipBoard } from '@/utils/clipboard' +import { FaCopy, FaExclamationTriangle, FaInfo, FaTimes } from 'react-icons/fa' +import { MdContentCopy, MdOutlineRotateLeft } from 'react-icons/md' +import { toast } from 'react-toastify' +import { Dialog, Transition } from '@headlessui/react' +import { cryptoUtils } from '@/utils/auth' +import { splitSecret } from '@/utils/keyshares' +import { Alert } from '@/components/common/Alert' +import UnlockKeyringDialog from '@/components/auth/UnlockKeyringDialog' +import { KeyringContext } from '@/contexts/keyringContext' +import clsx from 'clsx' +import { SecretTokens } from '@/components/apps/tokens/SecretTokens' + +export default function Tokens({ params }: { params: { team: string; app: string } }) { + const { data: orgsData } = useQuery(GetOrganisations) + const [getApp, { data }] = useLazyQuery(GetAppDetail) + + const app = data?.apps[0] as AppType + + const [activePanel, setActivePanel] = useState<'secrets' | 'kms'>('secrets') + + const organisationId = orgsData?.organisations[0].id + + const { keyring } = useContext(KeyringContext) + + useEffect(() => { + if (orgsData) { + const organisationId = orgsData.organisations[0].id + getApp({ + variables: { + organisationId, + appId: params.app, + }, + }) + } + }, [getApp, orgsData, params.app]) + + const handleCopy = (val: string) => { + copyToClipBoard(val) + toast.info('Copied') + } + + const KmsPanel = () => { + const appId = `phApp:v${app?.appVersion}:${app?.identityKey}` + + const [appSecret, setAppSecret] = useState('') + + const appSecretPlaceholder = '*'.repeat(295) + + const RotateAppDialog = () => { + const [pw, setPw] = useState('') + const [showPw, setShowPw] = useState(false) + const [loading, setLoading] = useState(false) + const [isOpen, setIsOpen] = useState(false) + const [rotateAppKeys] = useMutation(RotateAppKey) + + const closeModal = () => { + setPw('') + setIsOpen(false) + } + + const handleGenerateNewAppKey = async () => { + const APP_VERSION = 1 + + return new Promise(async (resolve, reject) => { + setTimeout(async () => { + setLoading(true) + try { + const wrapKey = await cryptoUtils.newAppWrapKey() + const newAppToken = await cryptoUtils.newAppToken() + // const deviceKey = await cryptoUtils.deviceVaultKey(pw, session?.user?.email!) + // const encryptedKeyring = getLocalKeyring(orgsData.organisations[0].id) + // if (!encryptedKeyring) throw 'Error fetching local encrypted keys from browser' + // const decryptedKeyring = await cryptoUtils.decryptAccountKeyring( + // encryptedKeyring!, + // deviceKey + // ) + // if (!decryptedKeyring) throw 'Failed to decrypt keys' + + const appSeed = await cryptoUtils.decryptedAppSeed(app.appSeed, keyring!.symmetricKey) + + const appKeys = await cryptoUtils.appKeyring(appSeed) + const appKeyShares = await splitSecret(appKeys.privateKey) + const wrappedShare = await cryptoUtils.wrappedKeyShare(appKeyShares[1], wrapKey) + await rotateAppKeys({ + variables: { + id: app.id, + appToken: newAppToken, + wrappedKeyShare: wrappedShare, + }, + }) + + setAppSecret(`pss:v${APP_VERSION}:${newAppToken}:${appKeyShares[0]}:${wrapKey}`) + + setLoading(false) + resolve(true) + } catch (error) { + console.log(error) + setLoading(false) + reject() + } + }, 500) + }) + } + + const handleSubmit = async (event: { preventDefault: () => void }) => { + event.preventDefault() + toast + .promise(handleGenerateNewAppKey, { + pending: 'Generating app keys', + success: 'Success!', + error: 'Something went wrong! Please check your password and try again.', + }) + .then(() => closeModal()) + } + + return ( + <> + + + {}}> + +
+ + +
+
+ + + +

+ Genereate new app secret +

+ +
+ + + Generate a new app secret for {app.name} + + +
+
+
+ +
+ +
+ Warning: This will revoke your current app keys. Your application + won't be able to decrypt data using the current keys. +
+
+
+ + +
+ +
+ Your new keys will be available to use immediately. You will be + able to decrypt any existing data with your new keys. Please allow + up to 60 seconds for your old keys to be revoked. +
+
+
+
+ + {/*
+ +
+ setPw(e.target.value)} + type={showPw ? 'text' : 'password'} + minLength={16} + required + className="w-full " + /> + +
+
*/} +
+ + +
+
+
+
+
+
+
+
+
+ + ) + } + + return ( +
+
+
+ app id + +
+ {appId} +
+ +
+
+ app secret +
+ {appSecret && ( +
+ +
{"Copy this value. You won't see it again!"}
+
+ )} + {appSecret && ( + + )} +
+ {!appSecret && } +
+ {appSecret || appSecretPlaceholder} +
+
+ ) + } + + return ( +
+
+ {orgsData?.organisations && ( + + )} + {keyring !== null && ( +
+
+
setActivePanel('secrets')} + className={clsx( + 'p-4 cursor-pointer rounded-md transition ease', + activePanel === 'secrets' + ? 'bg-zinc-400 dark:bg-zinc-700 font-semibold' + : 'bg-zinc-200 hover:bg-zinc-400 dark:bg-zinc-800 hover:dark:bg-zinc-700' + )} + > + Secrets +
+
setActivePanel('kms')} + className={clsx( + 'p-4 cursor-pointer rounded-md transition ease', + activePanel === 'kms' + ? 'bg-zinc-400 dark:bg-zinc-700 font-semibold' + : 'bg-zinc-200 hover:bg-zinc-400 dark:bg-zinc-800 hover:dark:bg-zinc-700' + )} + > + KMS +
+
+
+ {app && activePanel === 'secrets' && ( + + )} + {app && activePanel === 'kms' && } +
+
+ )} +
+
+ ) +} diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx new file mode 100644 index 000000000..f1075f8a0 --- /dev/null +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -0,0 +1,717 @@ +import { CreateNewUserToken } from '@/graphql/mutations/users/createUserToken.gql' +import { RevokeUserToken } from '@/graphql/mutations/users/deleteUserToken.gql' +import { RevokeServiceToken } from '@/graphql/mutations/environments/deleteServiceToken.gql' +import { CreateNewServiceToken } from '@/graphql/mutations/environments/createServiceToken.gql' +import { GetUserTokens } from '@/graphql/queries/users/getUserTokens.gql' +import { GetServiceTokens } from '@/graphql/queries/secrets/getServiceTokens.gql' +import { GetEnvironmentKey } from '@/graphql/queries/secrets/getEnvironmentKey.gql' +import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql' +import { + generateUserToken, + newEnvToken, + newEnvWrapKey, + newServiceTokenKeys, + unwrapEnvSecretsForUser, + wrapEnvSecretsForServiceToken, +} from '@/utils/environments' +import { EnvironmentType, ServiceTokenType, UserTokenType } from '@/apollo/graphql' +import { cryptoUtils } from '@/utils/auth' +import { getUserKxPublicKey, getUserKxPrivateKey } from '@/utils/crypto' +import { splitSecret } from '@/utils/keyshares' +import { useLazyQuery, useMutation, useQuery } from '@apollo/client' +import { useState, useEffect, useContext, Fragment } from 'react' +import { KeyringContext } from '@/contexts/keyringContext' +import { Button } from '@/components/common/Button' +import { + FaCheckSquare, + FaChevronDown, + FaExclamationTriangle, + FaKey, + FaPlus, + FaSquare, + FaTimes, + FaTrashAlt, + FaUserLock, + FaUserSecret, +} from 'react-icons/fa' +import { relativeTimeFromDates } from '@/utils/time' +import { Dialog, Listbox, Transition } from '@headlessui/react' +import { copyToClipBoard } from '@/utils/clipboard' +import { MdContentCopy } from 'react-icons/md' +import { toast } from 'react-toastify' +import clsx from 'clsx' + +const handleCopy = (val: string) => { + copyToClipBoard(val) + toast.info('Copied', { + autoClose: 2000, + }) +} + +const CreateUserTokenDialog = (props: { organisationId: string }) => { + const { organisationId } = props + + const { keyring } = useContext(KeyringContext) + + const [isOpen, setIsOpen] = useState(false) + const [name, setName] = useState('') + const [userToken, setUserToken] = useState('') + const [createUserToken] = useMutation(CreateNewUserToken) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const handleCreateNewUserToken = async () => { + if (name.length === 0) { + toast.error('You must enter a name for the token') + return false + } + + if (keyring) { + const userKxKeys = { + publicKey: await getUserKxPublicKey(keyring.publicKey), + privateKey: await getUserKxPrivateKey(keyring.privateKey), + } + + const { pssUser, mutationPayload } = await generateUserToken(organisationId, userKxKeys, name) + + await createUserToken({ + variables: mutationPayload, + refetchQueries: [ + { + query: GetUserTokens, + variables: { + organisationId, + }, + }, + ], + }) + + setUserToken(pssUser) + } else { + console.log('keyring unavailable') + } + } + + return ( + <> +
+ +
+ + + {}}> + +
+ + +
+
+ + + +

+ Create a new User token +

+ + +
+ + {userToken ? ( +
+
+
+ + user token + +
+ {userToken && ( +
+ +
+ {"Copy this value. You won't see it again!"} +
+
+ )} + {userToken && ( + + )} +
+
+ {userToken} +
+
+ ) : ( +
+
+ + setName(e.target.value)} /> +
+
+ + +
+
+ )} +
+
+
+
+
+
+ + ) +} + +const CreateServiceTokenDialog = (props: { organisationId: string; appId: string }) => { + const { organisationId, appId } = props + + const { keyring } = useContext(KeyringContext) + + const [isOpen, setIsOpen] = useState(false) + const [name, setName] = useState('') + const [envScope, setEnvScope] = useState>>([]) + const [serviceToken, setServiceToken] = useState('') + + const { data } = useQuery(GetAppEnvironments, { + variables: { + appId, + }, + }) + const [getEnvKey] = useLazyQuery(GetEnvironmentKey) + const [createServiceToken] = useMutation(CreateNewServiceToken) + + const reset = () => { + setName('') + setEnvScope([]) + } + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const envOptions = + data?.appEnvironments.map((env: EnvironmentType) => { + const { id, name } = env + + return { + id, + name, + } + }) ?? [] + + const handleCreateNewServiceToken = async () => { + if (name.length === 0) { + toast.error('You must enter a name for the token') + return false + } + + if (envScope.length === 0) { + toast.error('The token must be scoped to atleast one environment') + return false + } + + if (keyring) { + const appEnvironments = data.appEnvironments as EnvironmentType[] + + const token = await newEnvToken() + const wrapKey = await newEnvWrapKey() + + const tokenKeys = await newServiceTokenKeys() + const keyShares = await splitSecret(tokenKeys.privateKey) + const wrappedKeyShare = await cryptoUtils.wrappedKeyShare(keyShares[1], wrapKey) + + const pssService = `pss_service:v1:${token}:${tokenKeys.publicKey}:${keyShares[0]}:${wrapKey}` + + 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, + }, + }) + + const { + wrappedSeed: userWrappedSeed, + wrappedSalt: userWrappedSalt, + identityKey, + } = data.environmentKeys[0] + + const { seed, salt } = await unwrapEnvSecretsForUser( + userWrappedSeed, + userWrappedSalt, + keyring! + ) + + const { wrappedSeed, wrappedSalt } = await wrapEnvSecretsForServiceToken( + { seed, salt }, + tokenKeys.publicKey + ) + + return { + envId: env.id, + identityKey, + wrappedSeed, + wrappedSalt, + } + }) + + const envKeyInputs = await Promise.all(envKeyPromises) + + await createServiceToken({ + variables: { + appId, + environmentKeys: envKeyInputs, + identityKey: tokenKeys.publicKey, + token, + wrappedKeyShare, + name, + expiry: null, + }, + refetchQueries: [ + { + query: GetServiceTokens, + variables: { + appId, + }, + }, + ], + }) + + setServiceToken(pssService) + } + } + + return ( + <> +
+ +
+ + + {}}> + +
+ + +
+
+ + + +

+ Create a new Service token +

+ + +
+ + {serviceToken ? ( +
+
+
+ + service token + +
+ {serviceToken && ( +
+ +
+ {"Copy this value. You won't see it again!"} +
+
+ )} + {serviceToken && ( + + )} +
+
+ {serviceToken} +
+
+ ) : ( +
+
+ + setName(e.target.value)} /> +
+ +
+ + {({ open }) => ( + <> + + + + +
+ {envScope + .map((env: Partial) => env.name) + .join(' + ')} + +
+
+ + +
+ {envOptions.map((env: Partial) => ( + + {({ active, selected }) => ( +
+ {selected ? ( + + ) : ( + + )} + {env.name} +
+ )} +
+ ))} +
+
+
+ + )} +
+
+
+ + +
+
+ )} +
+
+
+
+
+
+ + ) +} + +export const SecretTokens = (props: { organisationId: string; appId: string }) => { + const { organisationId, appId } = props + + const { keyring } = useContext(KeyringContext) + + const [getUserTokens, { data: userTokensData }] = useLazyQuery(GetUserTokens) + const [getServiceTokens, { data: serviceTokensData }] = useLazyQuery(GetServiceTokens) + + const [deleteUserToken] = useMutation(RevokeUserToken) + const [deleteServiceToken] = useMutation(RevokeServiceToken) + + const [createServiceToken] = useMutation(CreateNewServiceToken) + + const [serviceToken, setServiceToken] = useState('') + + const handleDeleteUserToken = async (tokenId: string) => { + await deleteUserToken({ + variables: { tokenId }, + refetchQueries: [ + { + query: GetUserTokens, + variables: { + organisationId, + }, + }, + ], + }) + } + + const handleDeleteServiceToken = async (tokenId: string) => { + await deleteServiceToken({ + variables: { tokenId }, + refetchQueries: [ + { + query: GetServiceTokens, + variables: { + organisationId, + appId, + }, + }, + ], + }) + } + + useEffect(() => { + if (organisationId && appId) { + getUserTokens({ + variables: { + organisationId, + }, + }) + getServiceTokens({ + variables: { + appId, + }, + }) + } + }, [appId, getServiceTokens, getUserTokens, organisationId]) + + const DeleteConfirmDialog = (props: { + token: UserTokenType | ServiceTokenType + onDelete: Function + }) => { + const { token, onDelete } = props + + const [isOpen, setIsOpen] = useState(false) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Delete{' '} + + {token.name} + +

+ + +
+ +
+

+ Are you sure you want to delete this token? +

+
+ + +
+
+
+
+
+
+
+
+ + ) + } + + const UserToken = (props: { token: UserTokenType }) => { + const { token } = props + + return ( +
+
+ +
+
{token.name}
+
+ Created {relativeTimeFromDates(new Date(token.createdAt))} +
+
+
+ +
+ ) + } + + const ServiceToken = (props: { token: ServiceTokenType }) => { + const { token } = props + + return ( +
+
+ +
+
{token.name}
+
+ Created {relativeTimeFromDates(new Date(token.createdAt))} +
+
+
+ +
+ ) + } + + return ( +
+
+
+

User tokens

+

+ Tokens used to authenticate with the CLI from personal devices. Used for development and + manual configuration. +

+
+
+ {userTokensData?.userTokens.map((userToken: UserTokenType) => ( + + ))} +
+ + +
+ +
+ +
+
+

Service tokens

+

+ Tokens used to authenticate with the CLI from automated machines. Used for CI and + production environments. +

+
+
+ {serviceTokensData?.serviceTokens.map((serviceToken: ServiceTokenType) => ( + + ))} +
+ + +
+
+ ) +} diff --git a/frontend/graphql/mutations/environments/deleteServiceToken.gql b/frontend/graphql/mutations/environments/deleteServiceToken.gql new file mode 100644 index 000000000..ed6d747a4 --- /dev/null +++ b/frontend/graphql/mutations/environments/deleteServiceToken.gql @@ -0,0 +1,5 @@ +mutation RevokeServiceToken($tokenId: ID!) { + deleteServiceToken(tokenId: $tokenId) { + ok + } +} diff --git a/frontend/graphql/mutations/users/createUserToken.gql b/frontend/graphql/mutations/users/createUserToken.gql index 09dd22563..83925dca5 100644 --- a/frontend/graphql/mutations/users/createUserToken.gql +++ b/frontend/graphql/mutations/users/createUserToken.gql @@ -1,8 +1,5 @@ mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) { createUserToken(orgId: $orgId, name: $name, identityKey: $identityKey, token: $token, wrappedKeyShare: $wrappedKeyShare) { - userToken { - id - createdAt - } + ok } } diff --git a/frontend/graphql/mutations/users/deleteUserToken.gql b/frontend/graphql/mutations/users/deleteUserToken.gql new file mode 100644 index 000000000..9586dc80a --- /dev/null +++ b/frontend/graphql/mutations/users/deleteUserToken.gql @@ -0,0 +1,5 @@ +mutation RevokeUserToken($tokenId: ID!) { + deleteUserToken(tokenId: $tokenId) { + ok + } +} diff --git a/frontend/utils/environments.ts b/frontend/utils/environments.ts index cdcc81683..c9d8f01f1 100644 --- a/frontend/utils/environments.ts +++ b/frontend/utils/environments.ts @@ -194,7 +194,8 @@ export const generateEnvironmentToken = async ( */ export const generateUserToken = async ( orgId: string, - userKeyring: { publicKey: string; privateKey: string } + userKeyring: { publicKey: string; privateKey: string }, + name: string ) => { const wrapKey = await newEnvWrapKey() const token = await newEnvToken() @@ -205,7 +206,7 @@ export const generateUserToken = async ( const pssUser = `pss_user:v1:${token}:${userKeyring.publicKey}:${keyShares[0]}:${wrapKey}` const mutationPayload = { orgId, - name: 'testUserToken', + name, identityKey: userKeyring.publicKey, token, wrappedKeyShare, From 238cea706fbe5d6a17aeede310a1ab4b395b08d6 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Fri, 8 Sep 2023 20:42:52 +0530 Subject: [PATCH 100/161] fix: reset dialog state when closed --- frontend/components/apps/tokens/SecretTokens.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx index f1075f8a0..203baf140 100644 --- a/frontend/components/apps/tokens/SecretTokens.tsx +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -58,7 +58,13 @@ const CreateUserTokenDialog = (props: { organisationId: string }) => { const [userToken, setUserToken] = useState('') const [createUserToken] = useMutation(CreateNewUserToken) + const reset = () => { + setName('') + setUserToken('') + } + const closeModal = () => { + reset() setIsOpen(false) } @@ -222,9 +228,11 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string const reset = () => { setName('') setEnvScope([]) + setServiceToken('') } const closeModal = () => { + reset() setIsOpen(false) } From bca4adb670a5f2c1fae01b0f7fe8f7df081b2496 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 9 Sep 2023 12:04:30 +0530 Subject: [PATCH 101/161] fix: history log for service tokens --- frontend/components/environments/SecretRow.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/components/environments/SecretRow.tsx b/frontend/components/environments/SecretRow.tsx index 4579ec8d9..3ac520d69 100644 --- a/frontend/components/environments/SecretRow.tsx +++ b/frontend/components/environments/SecretRow.tsx @@ -17,6 +17,7 @@ import { FaTags, FaCheckSquare, FaSquare, + FaKey, } from 'react-icons/fa' import { Button } from '../common/Button' import { Dialog, Transition } from '@headlessui/react' @@ -392,8 +393,8 @@ const HistoryDialog = (props: { secret: SecretType }) => { {' '} by
- - {historyItem!.user!.username} + {historyItem!.user ? : } + {historyItem!.user?.username || 'Service token'}
{index > 0 && ( From fe8ce3e2e792ffca4426c914a7bbac8f3c2bb026 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 9 Sep 2023 13:18:06 +0530 Subject: [PATCH 102/161] fix: return 403 for revoked tokens --- backend/api/utils.py | 13 ++++++++++++- backend/api/views.py | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/backend/api/utils.py b/backend/api/utils.py index 7c6f64638..d3e0acd77 100644 --- a/backend/api/utils.py +++ b/backend/api/utils.py @@ -1,4 +1,4 @@ -from api.models import EnvironmentToken, UserToken +from api.models import EnvironmentToken, ServiceToken, UserToken def get_client_ip(request): @@ -38,3 +38,14 @@ def get_org_member_from_user_token(auth_token): return user_token.user except Exception as ex: return False + + +def token_is_expired(auth_token): + token_type = get_token_type(auth_token) + if token_type == 'User': + user_token = UserToken.objects.get(token=auth_token.split(" ")[2]) + return user_token.deleted_at != None + else: + service_token = ServiceToken.objects.get( + token=auth_token.split(" ")[2]) + return service_token.deleted_at != None diff --git a/backend/api/views.py b/backend/api/views.py index 4367bf9dd..19919cbce 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -11,7 +11,7 @@ from rest_framework.permissions import AllowAny from rest_framework.response import Response from django.http import JsonResponse, HttpResponse -from api.utils import get_client_ip, get_env_from_service_token, get_org_member_from_user_token, get_token_type +from api.utils import get_client_ip, get_env_from_service_token, get_org_member_from_user_token, get_token_type, token_is_expired from logs.models import KMSDBLog from .models import App, Environment, EnvironmentKey, EnvironmentToken, Secret, SecretEvent, SecretTag, ServiceToken, UserToken import jwt @@ -226,6 +226,8 @@ def user_token_kms(request): token = auth_token.split(' ')[2] user_token = UserToken.objects.get(token=token) + if user_token.deleted_at != None: + return HttpResponse(status=403) serializer = UserTokenSerializer(user_token) @@ -244,6 +246,8 @@ def service_token_kms(request): token = auth_token.split(' ')[2] service_token = ServiceToken.objects.get(token=token) + if service_token.deleted_at != None: + return HttpResponse(status=403) serializer = ServiceTokenSerializer(service_token) @@ -264,6 +268,10 @@ def dispatch(self, request, *args): def get(self, request): auth_token = request.headers['authorization'] + + if token_is_expired(auth_token): + return HttpResponse(status=403) + token_type = get_token_type(auth_token) env_id = request.headers['environment'] @@ -302,6 +310,10 @@ def get(self, request): def post(self, request): auth_token = request.headers['authorization'] + + if token_is_expired(auth_token): + return HttpResponse(status=403) + token_type = get_token_type(auth_token) env_id = request.headers['environment'] @@ -349,6 +361,10 @@ def post(self, request): def put(self, request): auth_token = request.headers['authorization'] + + if token_is_expired(auth_token): + return HttpResponse(status=403) + token_type = get_token_type(auth_token) env_id = request.headers['environment'] @@ -399,6 +415,10 @@ def put(self, request): def delete(self, request): auth_token = request.headers['authorization'] + + if token_is_expired(auth_token): + return HttpResponse(status=403) + token_type = get_token_type(auth_token) env_id = request.headers['environment'] From 429d3a285ebf751ec1e4c546bbb19bc7a73b3f11 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Sat, 9 Sep 2023 14:59:55 +0530 Subject: [PATCH 103/161] feat: token expiry --- .../migrations/0030_usertoken_expires_at.py | 18 +++ backend/api/models.py | 1 + backend/api/utils.py | 13 +- .../backend/graphene/mutations/environment.py | 12 +- backend/backend/graphene/types.py | 4 +- frontend/apollo/gql.ts | 5 + frontend/apollo/graphql.ts | 11 ++ frontend/apollo/schema.graphql | 4 +- .../components/apps/tokens/SecretTokens.tsx | 147 ++++++++++++++++-- .../mutations/users/createUserToken.gql | 4 +- .../graphql/queries/users/getUserTokens.gql | 1 + frontend/utils/environments.ts | 4 +- frontend/utils/time.ts | 12 ++ 13 files changed, 210 insertions(+), 26 deletions(-) create mode 100644 backend/api/migrations/0030_usertoken_expires_at.py diff --git a/backend/api/migrations/0030_usertoken_expires_at.py b/backend/api/migrations/0030_usertoken_expires_at.py new file mode 100644 index 000000000..96558b80e --- /dev/null +++ b/backend/api/migrations/0030_usertoken_expires_at.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.3 on 2023-09-09 09:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0029_servicetoken'), + ] + + operations = [ + migrations.AddField( + model_name='usertoken', + name='expires_at', + field=models.DateTimeField(null=True), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 58169c49e..a6866c10e 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -235,6 +235,7 @@ class UserToken(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(blank=True, null=True) + expires_at = models.DateTimeField(null=True) class SecretFolder(models.Model): diff --git a/backend/api/utils.py b/backend/api/utils.py index d3e0acd77..15c5987a1 100644 --- a/backend/api/utils.py +++ b/backend/api/utils.py @@ -1,4 +1,5 @@ from api.models import EnvironmentToken, ServiceToken, UserToken +from django.utils import timezone def get_client_ip(request): @@ -41,11 +42,11 @@ def get_org_member_from_user_token(auth_token): def token_is_expired(auth_token): - token_type = get_token_type(auth_token) + prefix, token_type, token_value = auth_token.split(" ") + if token_type == 'User': - user_token = UserToken.objects.get(token=auth_token.split(" ")[2]) - return user_token.deleted_at != None + token = UserToken.objects.get(token=token_value) else: - service_token = ServiceToken.objects.get( - token=auth_token.split(" ")[2]) - return service_token.deleted_at != None + token = ServiceToken.objects.get(token=token_value) + + return token.deleted_at is not None or token.expires_at < timezone.now() diff --git a/backend/backend/graphene/mutations/environment.py b/backend/backend/graphene/mutations/environment.py index 265cbae24..beccc80db 100644 --- a/backend/backend/graphene/mutations/environment.py +++ b/backend/backend/graphene/mutations/environment.py @@ -130,20 +130,26 @@ class Arguments: identity_key = graphene.String(required=True) token = graphene.String(required=True) wrapped_key_share = graphene.String(required=True) + expiry = graphene.BigInt(required=False) ok = graphene.Boolean() user_token = graphene.Field(UserTokenType) @classmethod - def mutate(cls, root, info, org_id, name, identity_key, token, wrapped_key_share): + def mutate(cls, root, info, org_id, name, identity_key, token, wrapped_key_share, expiry): user = info.context.user if user_is_org_member(user.userId, org_id): org_member = OrganisationMember.objects.get( organisation_id=org_id, user_id=user.userId) + if expiry is not None: + expires_at = datetime.fromtimestamp(expiry / 1000) + else: + expires_at = None + user_token = UserToken.objects.create( - user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share) + user=org_member, name=name, identity_key=identity_key, token=token, wrapped_key_share=wrapped_key_share, expires_at=expires_at) return CreateUserTokenMutation(user_token=user_token, ok=True) @@ -200,7 +206,7 @@ def mutate(cls, root, info, app_id, environment_keys, identity_key, token, wrapp environment_id=key.env_id, identity_key=key.identity_key, wrapped_seed=key.wrapped_seed, wrapped_salt=key.wrapped_salt) for key in environment_keys]) if expiry is not None: - expires_at = datetime.fromtimestamp(expiry) + expires_at = datetime.fromtimestamp(expiry / 1000) else: expires_at = None diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 471a6819a..7a6f3d12e 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -62,14 +62,14 @@ class UserTokenType(DjangoObjectType): class Meta: model = UserToken fields = ('id', 'name', 'identity_key', 'token', - 'wrapped_key_share', 'created_at', 'updated_at') + 'wrapped_key_share', 'created_at', 'updated_at', 'expires_at') class ServiceTokenType(DjangoObjectType): class Meta: model = ServiceToken fields = ('id', 'keys', 'identity_key', - 'token', 'wrapped_key_share', 'name', 'created_by', 'created_at', 'expires_at') + 'token', 'wrapped_key_share', 'name', 'created_by', 'created_at', 'updated_at', 'expires_at') class SecretFolderType(DjangoObjectType): diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 69aff8021..8c02b6f78 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -23,6 +23,7 @@ const documents = { "mutation CreateNewSecretTag($orgId: ID!, $name: String!, $color: String!) {\n createSecretTag(orgId: $orgId, name: $name, color: $color) {\n tag {\n id\n }\n }\n}": types.CreateNewSecretTagDocument, "mutation CreateNewServiceToken($appId: ID!, $environmentKeys: [EnvironmentKeyInput], $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $name: String!, $expiry: BigInt) {\n createServiceToken(\n appId: $appId\n environmentKeys: $environmentKeys\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n name: $name\n expiry: $expiry\n ) {\n serviceToken {\n id\n createdAt\n expiresAt\n }\n }\n}": types.CreateNewServiceTokenDocument, "mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\n id\n }\n }\n}": types.DeleteSecretOpDocument, + "mutation RevokeServiceToken($tokenId: ID!) {\n deleteServiceToken(tokenId: $tokenId) {\n ok\n }\n}": types.RevokeServiceTokenDocument, "mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}": types.UpdateSecretDocument, "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, "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, @@ -100,6 +101,10 @@ export function graphql(source: "mutation CreateNewServiceToken($appId: ID!, $en * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\n id\n }\n }\n}"): (typeof documents)["mutation DeleteSecretOp($id: ID!) {\n deleteSecret(id: $id) {\n secret {\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 RevokeServiceToken($tokenId: ID!) {\n deleteServiceToken(tokenId: $tokenId) {\n ok\n }\n}"): (typeof documents)["mutation RevokeServiceToken($tokenId: ID!) {\n deleteServiceToken(tokenId: $tokenId) {\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 271bce9e3..8b10a1aac 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -326,6 +326,7 @@ export type MutationCreateServiceTokenArgs = { export type MutationCreateUserTokenArgs = { + expiry?: InputMaybe; identityKey: Scalars['String']; name: Scalars['String']; orgId: Scalars['ID']; @@ -558,6 +559,7 @@ export type ServiceTokenType = { keys: Array; name: Scalars['String']; token: Scalars['String']; + updatedAt: Scalars['DateTime']; wrappedKeyShare: Scalars['String']; }; @@ -574,6 +576,7 @@ export enum TimeRange { export type UserTokenType = { __typename?: 'UserTokenType'; createdAt?: Maybe; + expiresAt?: Maybe; id: Scalars['String']; identityKey: Scalars['String']; name: Scalars['String']; @@ -677,6 +680,13 @@ export type DeleteSecretOpMutationVariables = Exact<{ export type DeleteSecretOpMutation = { __typename?: 'Mutation', deleteSecret?: { __typename?: 'DeleteSecretMutation', secret?: { __typename?: 'SecretType', id: string } | null } | null }; +export type RevokeServiceTokenMutationVariables = Exact<{ + tokenId: Scalars['ID']; +}>; + + +export type RevokeServiceTokenMutation = { __typename?: 'Mutation', deleteServiceToken?: { __typename?: 'DeleteServiceTokenMutation', ok?: boolean | null } | null }; + export type UpdateSecretMutationVariables = Exact<{ id: Scalars['ID']; secretData: SecretInput; @@ -850,6 +860,7 @@ export const CreateNewSecretDocument = {"kind":"Document","definitions":[{"kind" export const CreateNewSecretTagDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewSecretTag"},"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":"color"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSecretTag"},"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":"color"},"value":{"kind":"Variable","name":{"kind":"Name","value":"color"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tag"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewServiceTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewServiceToken"},"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":"environmentKeys"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentKeyInput"}}}},{"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":"name"}},"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":"createServiceToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentKeys"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environmentKeys"}}},{"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":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"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":"serviceToken"},"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"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteSecretOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSecretOp"},"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":"deleteSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; +export const RevokeServiceTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeServiceToken"},"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":"deleteServiceToken"},"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 UpdateSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSecret"},"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":"secretData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} 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; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index f3ce4b7a8..5ab8b83d6 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -227,6 +227,7 @@ type UserTokenType { wrappedKeyShare: String! createdAt: DateTime updatedAt: DateTime! + expiresAt: DateTime } type ServiceTokenType { @@ -238,6 +239,7 @@ type ServiceTokenType { name: String! createdBy: OrganisationMemberType createdAt: DateTime + updatedAt: DateTime! expiresAt: DateTime } @@ -249,7 +251,7 @@ type Mutation { createEnvironment(environmentData: EnvironmentInput!): CreateEnvironmentMutation createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation - createUserToken(identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation + createUserToken(expiry: BigInt, identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation deleteUserToken(tokenId: ID!): DeleteUserTokenMutation createServiceToken(appId: ID!, environmentKeys: [EnvironmentKeyInput], expiry: BigInt, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateServiceTokenMutation deleteServiceToken(tokenId: ID!): DeleteServiceTokenMutation diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx index 203baf140..92b4a16b6 100644 --- a/frontend/components/apps/tokens/SecretTokens.tsx +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -25,6 +25,8 @@ import { Button } from '@/components/common/Button' import { FaCheckSquare, FaChevronDown, + FaCircle, + FaDotCircle, FaExclamationTriangle, FaKey, FaPlus, @@ -32,15 +34,19 @@ import { FaTimes, FaTrashAlt, FaUserLock, - FaUserSecret, } from 'react-icons/fa' -import { relativeTimeFromDates } from '@/utils/time' -import { Dialog, Listbox, Transition } from '@headlessui/react' +import { getUnixTimestampInFuture, relativeTimeFromDates } from '@/utils/time' +import { Dialog, Listbox, RadioGroup, Transition } from '@headlessui/react' import { copyToClipBoard } from '@/utils/clipboard' import { MdContentCopy } from 'react-icons/md' import { toast } from 'react-toastify' import clsx from 'clsx' +interface ExpiryOptionT { + name: string + value: number | null +} + const handleCopy = (val: string) => { copyToClipBoard(val) toast.info('Copied', { @@ -48,6 +54,34 @@ const handleCopy = (val: string) => { }) } +const tokenExpiryOptions: ExpiryOptionT[] = [ + { + name: 'Never', + value: null, + }, + { + name: '7 days', + value: getUnixTimestampInFuture(7), + }, + { + name: '30 days', + value: getUnixTimestampInFuture(30), + }, + { + name: '60 days', + value: getUnixTimestampInFuture(60), + }, + { + name: '90 days', + value: getUnixTimestampInFuture(90), + }, +] + +const humanReadableExpiry = (expiry: ExpiryOptionT) => + expiry.value === null + ? 'This token will never expire.' + : `This token will expire on ${new Date(expiry.value).toLocaleDateString()}.` + const CreateUserTokenDialog = (props: { organisationId: string }) => { const { organisationId } = props @@ -55,6 +89,8 @@ const CreateUserTokenDialog = (props: { organisationId: string }) => { const [isOpen, setIsOpen] = useState(false) const [name, setName] = useState('') + const [expiry, setExpiry] = useState(tokenExpiryOptions[0]) + const [userToken, setUserToken] = useState('') const [createUserToken] = useMutation(CreateNewUserToken) @@ -84,7 +120,12 @@ const CreateUserTokenDialog = (props: { organisationId: string }) => { privateKey: await getUserKxPrivateKey(keyring.privateKey), } - const { pssUser, mutationPayload } = await generateUserToken(organisationId, userKxKeys, name) + const { pssUser, mutationPayload } = await generateUserToken( + organisationId, + userKxKeys, + name, + expiry.value + ) await createUserToken({ variables: mutationPayload, @@ -187,6 +228,42 @@ const CreateUserTokenDialog = (props: { organisationId: string }) => { setName(e.target.value)} /> + +
+ + + + +
+ {tokenExpiryOptions.map((option) => ( + + {({ active, checked }) => ( +
+ {checked ? ( + + ) : ( + + )} + {option.name} +
+ )} +
+ ))} +
+
+ + {humanReadableExpiry(expiry)} + +
+
-
- + )} @@ -301,6 +308,7 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string const [name, setName] = useState('') const [envScope, setEnvScope] = useState>>([]) const [expiry, setExpiry] = useState(tokenExpiryOptions[0]) + const [showEnvHint, setShowEnvHint] = useState(false) const [serviceToken, setServiceToken] = useState('') @@ -316,6 +324,7 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string setName('') setEnvScope([]) setServiceToken('') + setShowEnvHint(false) } const closeModal = () => { @@ -337,14 +346,11 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string } }) ?? [] - const handleCreateNewServiceToken = async () => { - if (name.length === 0) { - toast.error('You must enter a name for the token') - return false - } + const handleCreateNewServiceToken = async (event: { preventDefault: () => void }) => { + event.preventDefault() if (envScope.length === 0) { - toast.error('The token must be scoped to atleast one environment') + setShowEnvHint(true) return false } @@ -468,7 +474,7 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string {serviceToken ? (
-
+
service token @@ -489,11 +495,11 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string )}
- {serviceToken} + {serviceToken}
) : ( -
+
- setName(e.target.value)} /> + setName(e.target.value)} + />
-
- +
+ {envScope.length === 0 && showEnvHint && ( + + Select an environment scope + + )} + {({ open }) => ( <> @@ -516,14 +538,16 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string Environment scope - +
- {envScope - .map((env: Partial) => env.name) - .join(' + ')} + + {envScope + .map((env: Partial) => env.name) + .join(' + ')} + @@ -553,7 +577,9 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string ) : ( )} - {env.name} + + {env.name} +
)} @@ -573,7 +599,7 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string Expiry -
+
{tokenExpiryOptions.map((option) => ( {({ active, checked }) => ( @@ -602,14 +628,14 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string
- -
-
+ )} @@ -768,15 +794,22 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) = ) } - const UserToken = (props: { token: UserTokenType }) => { - const { token } = props + const CreatedToken = (props: { + token: ServiceTokenType | UserTokenType + deleteHandler: Function + }) => { + const { token, deleteHandler } = props const isExpired = token.expiresAt === null ? false : new Date(token.expiresAt) < new Date() return ( -
+
- + {token.__typename === 'UserTokenType' ? ( + + ) : ( + + )}
{token.name}
@@ -789,33 +822,9 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) =
- -
- ) - } - - const ServiceToken = (props: { token: ServiceTokenType }) => { - const { token } = props - - const isExpired = token.expiresAt === null ? false : new Date(token.expiresAt) < new Date() - - return ( -
-
- -
-
{token.name}
-
-
Created {relativeTimeFromDates(new Date(token.createdAt))}
- -
- {isExpired ? 'Expired' : 'Expires'}{' '} - {token.expiresAt ? relativeTimeFromDates(new Date(token.expiresAt)) : 'never'} -
-
-
+
+
-
) } @@ -830,9 +839,13 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) = manual configuration.

-
+
{userTokensData?.userTokens.map((userToken: UserTokenType) => ( - + ))}
@@ -849,9 +862,13 @@ export const SecretTokens = (props: { organisationId: string; appId: string }) = production environments.

-
+
{serviceTokensData?.serviceTokens.map((serviceToken: ServiceTokenType) => ( - + ))}
From 3451af1d70eeecb622508ca820c6da6e081a9a42 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 12 Sep 2023 11:33:11 +0530 Subject: [PATCH 112/161] fix: calculate token expiry when creating tokens --- .../components/apps/tokens/SecretTokens.tsx | 26 +++++++++---------- frontend/utils/time.ts | 11 +++++++- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx index b47ca44f7..66245290a 100644 --- a/frontend/components/apps/tokens/SecretTokens.tsx +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -44,7 +44,7 @@ import clsx from 'clsx' interface ExpiryOptionT { name: string - value: number | null + getExpiry: () => number | null } const handleCopy = (val: string) => { @@ -57,38 +57,38 @@ const handleCopy = (val: string) => { const tokenExpiryOptions: ExpiryOptionT[] = [ { name: 'Never', - value: null, + getExpiry: () => null, }, { name: '2 min', - value: getUnixTimeStampinFuture(0, 0, 2), + getExpiry: () => getUnixTimeStampinFuture(0, 0, 2), }, { name: '7 days', - value: getUnixTimeStampinFuture(7), + getExpiry: () => getUnixTimeStampinFuture(7), }, { name: '30 days', - value: getUnixTimeStampinFuture(30), + getExpiry: () => getUnixTimeStampinFuture(30), }, { name: '60 days', - value: getUnixTimeStampinFuture(60), + getExpiry: () => getUnixTimeStampinFuture(60), }, { name: '90 days', - value: getUnixTimeStampinFuture(90), + getExpiry: () => getUnixTimeStampinFuture(90), }, ] const compareExpiryOptions = (a: ExpiryOptionT, b: ExpiryOptionT) => { - return a.value === b.value + return a.getExpiry() === b.getExpiry() } -const humanReadableExpiry = (expiry: ExpiryOptionT) => - expiry.value === null +const humanReadableExpiry = (expiryOption: ExpiryOptionT) => + expiryOption.getExpiry() === null ? 'This token will never expire.' - : `This token will expire on ${new Date(expiry.value).toLocaleDateString()}.` + : `This token will expire on ${new Date(expiryOption.getExpiry()!).toLocaleDateString()}.` const CreateUserTokenDialog = (props: { organisationId: string }) => { const { organisationId } = props @@ -134,7 +134,7 @@ const CreateUserTokenDialog = (props: { organisationId: string }) => { organisationId, userKxKeys, name, - expiry.value + expiry.getExpiry() ) await createUserToken({ @@ -410,7 +410,7 @@ const CreateServiceTokenDialog = (props: { organisationId: string; appId: string token, wrappedKeyShare, name, - expiry: expiry.value, + expiry: expiry.getExpiry(), }, refetchQueries: [ { diff --git a/frontend/utils/time.ts b/frontend/utils/time.ts index be1d9bbbc..586c6fe5f 100644 --- a/frontend/utils/time.ts +++ b/frontend/utils/time.ts @@ -45,5 +45,14 @@ export const getUnixTimeStampinFuture = ( hours: number = 0, minutes: number = 0 ): number => { - return Date.now() + days * 86400000 + hours * 3600000 + minutes * 60000 + const millisecondsInADay = 86400000 + const millisecondsInAnHour = 3600000 + const millisecondsInAMinute = 60000 + + return ( + Date.now() + + days * millisecondsInADay + + hours * millisecondsInAnHour + + minutes * millisecondsInAMinute + ) } From 7d234d97dbee3a1568aca8e064cbc361e5f76d4f Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 12 Sep 2023 12:41:55 +0530 Subject: [PATCH 113/161] fix: remove 2min expiry option --- frontend/components/apps/tokens/SecretTokens.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/components/apps/tokens/SecretTokens.tsx b/frontend/components/apps/tokens/SecretTokens.tsx index 66245290a..fb83e66a3 100644 --- a/frontend/components/apps/tokens/SecretTokens.tsx +++ b/frontend/components/apps/tokens/SecretTokens.tsx @@ -59,10 +59,6 @@ const tokenExpiryOptions: ExpiryOptionT[] = [ name: 'Never', getExpiry: () => null, }, - { - name: '2 min', - getExpiry: () => getUnixTimeStampinFuture(0, 0, 2), - }, { name: '7 days', getExpiry: () => getUnixTimeStampinFuture(7), From 7c887970b8bb4855c242e27710ee9c86324761a8 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 12 Sep 2023 19:08:21 +0530 Subject: [PATCH 114/161] feat: add invite model, schema, types and operations --- .../0031_organisationmemberinvite.py | 28 +++++++ .../0032_organisationmemberinvite_apps.py | 18 +++++ backend/api/models.py | 14 ++++ .../graphene/mutations/organisation.py | 76 ++++++++++++++----- backend/backend/graphene/types.py | 9 ++- backend/backend/schema.py | 20 ++++- 6 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 backend/api/migrations/0031_organisationmemberinvite.py create mode 100644 backend/api/migrations/0032_organisationmemberinvite_apps.py diff --git a/backend/api/migrations/0031_organisationmemberinvite.py b/backend/api/migrations/0031_organisationmemberinvite.py new file mode 100644 index 000000000..98b901bb7 --- /dev/null +++ b/backend/api/migrations/0031_organisationmemberinvite.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.3 on 2023-09-12 08:18 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0030_usertoken_expires_at'), + ] + + operations = [ + migrations.CreateModel( + name='OrganisationMemberInvite', + fields=[ + ('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('invitee_email', models.EmailField(max_length=254)), + ('valid', models.BooleanField(default=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('expires_at', models.DateTimeField()), + ('invited_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember')), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invites', to='api.organisation')), + ], + ), + ] diff --git a/backend/api/migrations/0032_organisationmemberinvite_apps.py b/backend/api/migrations/0032_organisationmemberinvite_apps.py new file mode 100644 index 000000000..68f7a410d --- /dev/null +++ b/backend/api/migrations/0032_organisationmemberinvite_apps.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.3 on 2023-09-12 13:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0031_organisationmemberinvite'), + ] + + operations = [ + migrations.AddField( + model_name='organisationmemberinvite', + name='apps', + field=models.ManyToManyField(to='api.app'), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index a6866c10e..168d119c4 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -152,6 +152,20 @@ def __str__(self): return self.name +class OrganisationMemberInvite(models.Model): + id = models.TextField(default=uuid4, primary_key=True, editable=False) + organisation = models.ForeignKey( + Organisation, related_name='invites', on_delete=models.CASCADE) + apps = models.ManyToManyField(App) + invited_by = models.ForeignKey( + OrganisationMember, on_delete=models.CASCADE) + invitee_email = models.EmailField() + valid = models.BooleanField(default=True) + created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + expires_at = models.DateTimeField() + + class Environment(models.Model): DEVELOPMENT = "dev" diff --git a/backend/backend/graphene/mutations/organisation.py b/backend/backend/graphene/mutations/organisation.py index d2c1465f1..07260ea0f 100644 --- a/backend/backend/graphene/mutations/organisation.py +++ b/backend/backend/graphene/mutations/organisation.py @@ -1,8 +1,10 @@ from backend.graphene.utils.permissions import user_is_admin import graphene from graphql import GraphQLError -from api.models import Organisation, CustomUser, OrganisationMember -from backend.graphene.types import OrganisationMemberType, OrganisationType +from api.models import App, Organisation, CustomUser, OrganisationMember, OrganisationMemberInvite +from backend.graphene.types import OrganisationMemberInviteType, OrganisationMemberType, OrganisationType +from datetime import datetime, timedelta + class CreateOrganisationMutation(graphene.Mutation): class Arguments: @@ -23,10 +25,47 @@ def mutate(cls, root, info, id, name, identity_key): owner = CustomUser.objects.get(userId=info.context.user.userId) org = Organisation.objects.create( id=id, name=name, identity_key=identity_key) - OrganisationMember.objects.create(user=owner, organisation=org, role=OrganisationMember.OWNER, identity_key=identity_key) + OrganisationMember.objects.create( + user=owner, organisation=org, role=OrganisationMember.OWNER, identity_key=identity_key) return CreateOrganisationMutation(organisation=org) - + + +class InviteOrganisationMemberMutation(graphene.Mutation): + class Arguments: + org_id = graphene.ID(required=True) + email = graphene.String(required=True) + apps = graphene.List(graphene.String) + + invite = graphene.Field(OrganisationMemberInviteType) + + @classmethod + def mutate(cls, root, info, org_id, email, apps): + if user_is_admin(info.context.user, org_id): + user_already_exists = OrganisationMember.objects.filter( + organisation_id=org_id, user__email=email).exists() + if user_already_exists: + raise GraphQLError( + "This user is already a member if your organisation") + + invited_by = OrganisationMember.objects.get( + user=info.context.user, organisation_id=org_id) + + expiry = datetime.now() + timedelta(days=3) + + app_scope = App.objects.filter(id__in=apps) + + invite = OrganisationMemberInvite.objects.create( + organisation_id=org_id, invited_by=invited_by, invitee_email=email, expires_at=expiry) + + invite.apps.set(app_scope) + + return InviteOrganisationMemberMutation(invite=invite) + else: + raise GraphQLError( + "You don't have permission to perform this action") + + class CreateOrganisationMemberMutation(graphene.Mutation): class Arguments: org_id = graphene.ID(required=True) @@ -40,14 +79,17 @@ class Arguments: @classmethod def mutate(cls, root, info, org_id, user_id, role, identity_key, wrapped_keyring): if user_is_admin(info.context.user, org_id): - org = Organisation.objects.get(id=org_id) + org = Organisation.objects.get(id=org_id) + + org_member = OrganisationMember.objects.create( + user_id=user_id, organisation=org, role=role, identity_key=identity_key, wrapped_keyring=wrapped_keyring) + + return CreateOrganisationMemberMutation(org_member=org_member) + else: + raise GraphQLError( + "You don't have permission to perform this action") - org_member = OrganisationMember.objects.create(user_id=user_id, organisation=org, role=role, identity_key=identity_key, wrapped_keyring=wrapped_keyring) - return CreateOrganisationMemberMutation(org_member=org_member) - else: - raise GraphQLError("You don't have permission to perform this action") - class UpdateOrganisationMemberRole(graphene.Mutation): class Arguments: org_id = graphene.ID(required=True) @@ -60,14 +102,14 @@ class Arguments: def mutate(cls, root, info, org_id, user_id, role): if user_is_admin(info.context.user, org_id): if role == OrganisationMember.OWNER: - raise GraphQLError('You cannot set this user as the organisation owner') - org_member = OrganisationMember.objects.get(organisation__id=org_id, user__id=user_id) + raise GraphQLError( + 'You cannot set this user as the organisation owner') + org_member = OrganisationMember.objects.get( + organisation__id=org_id, user__id=user_id) org_member.role = role org_member.save() return UpdateOrganisationMemberRole(org_member=org_member) - else: - raise GraphQLError("You don't have permission to perform this action") - - - \ No newline at end of file + else: + raise GraphQLError( + "You don't have permission to perform this action") diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 7a6f3d12e..10631f6e1 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -2,7 +2,7 @@ from enum import Enum from graphene import ObjectType, relay from graphene_django import DjangoObjectType -from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretFolder, SecretTag, ServiceToken, UserToken +from api.models import CustomUser, Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, OrganisationMemberInvite, Secret, SecretEvent, SecretFolder, SecretTag, ServiceToken, UserToken from logs.dynamodb_models import KMSLog @@ -30,6 +30,13 @@ def resolve_username(self, info): return org_member.user.username +class OrganisationMemberInviteType(DjangoObjectType): + class Meta: + model = OrganisationMemberInvite + fields = ('id', 'invited_by', 'invitee_email', 'valid', + 'created_at', 'updated_at', 'expires_at') + + class AppType(DjangoObjectType): class Meta: model = App diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 8aa8a5e36..a23596a5d 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,11 +1,11 @@ from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, CreateServiceTokenMutation, CreateUserTokenMutation, DeleteSecretMutation, DeleteServiceTokenMutation, DeleteUserTokenMutation, EditSecretMutation -from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_org_member +from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_admin, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation -from .graphene.mutations.organisation import CreateOrganisationMutation -from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, KMSLogType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, ServiceTokenType, TimeRange, UserTokenType +from .graphene.mutations.organisation import CreateOrganisationMutation, InviteOrganisationMemberMutation +from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, KMSLogType, OrganisationMemberInviteType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, ServiceTokenType, TimeRange, UserTokenType import graphene from graphql import GraphQLError -from api.models import Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, Secret, SecretEvent, SecretTag, ServiceToken, UserToken +from api.models import Environment, EnvironmentKey, EnvironmentToken, Organisation, App, OrganisationMember, OrganisationMemberInvite, Secret, SecretEvent, SecretTag, ServiceToken, UserToken from logs.queries import get_app_log_count, get_app_log_count_range, get_app_logs from datetime import datetime, timedelta from django.conf import settings @@ -21,6 +21,8 @@ class Query(graphene.ObjectType): ), user_id=graphene.ID(), role=graphene.List(graphene.String)) organisation_admins_and_self = graphene.List( OrganisationMemberType, organisation_id=graphene.ID()) + organisation_invites = graphene.List( + OrganisationMemberInviteType, org_id=graphene.ID()) apps = graphene.List( AppType, organisation_id=graphene.ID(), app_id=graphene.ID()) logs = graphene.List(KMSLogType, app_id=graphene.ID(), @@ -72,6 +74,15 @@ def resolve_organisation_admins_and_self(root, info, organisation_id): return members + def resolve_organisation_invites(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") + + invites = OrganisationMemberInvite.objects.filter( + organisation_id=org_id, valid=True) + + return invites + def resolve_apps(root, info, organisation_id, app_id): filter = { 'organisation_id': organisation_id, @@ -266,6 +277,7 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): class Mutation(graphene.ObjectType): create_organisation = CreateOrganisationMutation.Field() + invite_organisation_member = InviteOrganisationMemberMutation.Field() create_app = CreateAppMutation.Field() rotate_app_keys = RotateAppKeysMutation.Field() delete_app = DeleteAppMutation.Field() From 7cc25e170821564cc2f17a705b06392f13a94d66 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 12 Sep 2023 19:09:15 +0530 Subject: [PATCH 115/161] chore: regenerate schema and types --- frontend/apollo/gql.ts | 18 ++++++++--- frontend/apollo/graphql.ts | 55 ++++++++++++++++++++++++++++++++-- frontend/apollo/schema.graphql | 16 ++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 8c02b6f78..377ae59b1 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -26,8 +26,9 @@ const documents = { "mutation RevokeServiceToken($tokenId: ID!) {\n deleteServiceToken(tokenId: $tokenId) {\n ok\n }\n}": types.RevokeServiceTokenDocument, "mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}": types.UpdateSecretDocument, "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, + "mutation InviteMember($orgId: ID!, $email: String!, $apps: [String]) {\n inviteOrganisationMember(orgId: $orgId, email: $email, apps: $apps) {\n invite {\n id\n }\n }\n}": types.InviteMemberDocument, "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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n ok\n }\n}": types.CreateNewUserTokenDocument, + "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 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 }\n}": types.GetAppDetailDocument, @@ -35,6 +36,7 @@ const documents = { "query GetAppLogs($appId: ID!, $start: BigInt, $end: BigInt) {\n logs(appId: $appId, start: $start, end: $end) {\n id\n timestamp\n phaseNode\n eventType\n ipAddress\n country\n city\n phSize\n }\n logsCount(appId: $appId)\n}": types.GetAppLogsDocument, "query GetApps($organisationId: ID!, $appId: ID!) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n }\n}": types.GetAppsDocument, "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n }\n}": types.GetOrganisationsDocument, + "query GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n }\n inviteeEmail\n }\n}": types.GetInvitesDocument, "query GetOrganisationAdminsAndSelf($organisationId: ID!) {\n organisationAdminsAndSelf(organisationId: $organisationId) {\n id\n role\n identityKey\n }\n}": types.GetOrganisationAdminsAndSelfDocument, "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}": types.GetOrganisationMembersDocument, "query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId, environmentId: null) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}": types.GetAppEnvironmentsDocument, @@ -44,7 +46,7 @@ const documents = { "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, "query GetSecrets($appId: ID!, $envId: ID!) {\n secrets(envId: $envId) {\n id\n key\n value\n tags {\n id\n name\n color\n }\n comment\n createdAt\n history {\n id\n key\n value\n tags {\n id\n name\n color\n }\n version\n comment\n timestamp\n user {\n email\n username\n }\n eventType\n }\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n }\n environmentKeys(environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n}": types.GetSecretsDocument, "query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n expiresAt\n keys {\n id\n }\n }\n}": types.GetServiceTokensDocument, - "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}": types.GetUserTokensDocument, + "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n expiresAt\n }\n}": types.GetUserTokensDocument, }; /** @@ -113,6 +115,10 @@ export function graphql(source: "mutation UpdateSecret($id: ID!, $secretData: Se * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"): (typeof documents)["mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\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: "mutation InviteMember($orgId: ID!, $email: String!, $apps: [String]) {\n inviteOrganisationMember(orgId: $orgId, email: $email, apps: $apps) {\n invite {\n id\n }\n }\n}"): (typeof documents)["mutation InviteMember($orgId: ID!, $email: String!, $apps: [String]) {\n inviteOrganisationMember(orgId: $orgId, email: $email, apps: $apps) {\n invite {\n id\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -120,7 +126,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 CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n ok\n }\n}"): (typeof documents)["mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n ) {\n ok\n }\n}"]; +export function graphql(source: "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}"): (typeof 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}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -149,6 +155,10 @@ export function graphql(source: "query GetApps($organisationId: ID!, $appId: 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 GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\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 GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n }\n inviteeEmail\n }\n}"): (typeof documents)["query GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n }\n inviteeEmail\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -188,7 +198,7 @@ export function graphql(source: "query GetServiceTokens($appId: ID!) {\n servic /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}"): (typeof documents)["query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n }\n}"]; +export function graphql(source: "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n expiresAt\n }\n}"): (typeof documents)["query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n expiresAt\n }\n}"]; export function graphql(source: string) { return (documents as any)[source] ?? {}; diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 8b10a1aac..97611143c 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -213,6 +213,11 @@ export type EnvironmentType = { wrappedSeed: Scalars['String']; }; +export type InviteOrganisationMemberMutation = { + __typename?: 'InviteOrganisationMemberMutation'; + invite?: Maybe; +}; + export type KmsLogType = Node & { __typename?: 'KMSLogType'; appId?: Maybe; @@ -248,6 +253,7 @@ export type Mutation = { deleteServiceToken?: Maybe; deleteUserToken?: Maybe; editSecret?: Maybe; + inviteOrganisationMember?: Maybe; rotateAppKeys?: Maybe; }; @@ -361,6 +367,13 @@ export type MutationEditSecretArgs = { }; +export type MutationInviteOrganisationMemberArgs = { + apps?: InputMaybe>>; + email: Scalars['String']; + orgId: Scalars['ID']; +}; + + export type MutationRotateAppKeysArgs = { appToken: Scalars['String']; id: Scalars['ID']; @@ -373,6 +386,17 @@ export type Node = { id: Scalars['ID']; }; +export type OrganisationMemberInviteType = { + __typename?: 'OrganisationMemberInviteType'; + createdAt?: Maybe; + expiresAt: Scalars['DateTime']; + id: Scalars['String']; + invitedBy: OrganisationMemberType; + inviteeEmail: Scalars['String']; + updatedAt: Scalars['DateTime']; + valid: Scalars['Boolean']; +}; + export type OrganisationMemberType = { __typename?: 'OrganisationMemberType'; createdAt?: Maybe; @@ -404,6 +428,7 @@ export type Query = { logs?: Maybe>>; logsCount?: Maybe; organisationAdminsAndSelf?: Maybe>>; + organisationInvites?: Maybe>>; organisationMembers?: Maybe>>; organisations?: Maybe>>; secretHistory?: Maybe>>; @@ -460,6 +485,11 @@ export type QueryOrganisationAdminsAndSelfArgs = { }; +export type QueryOrganisationInvitesArgs = { + orgId?: InputMaybe; +}; + + export type QueryOrganisationMembersArgs = { organisationId?: InputMaybe; role?: InputMaybe>>; @@ -704,6 +734,15 @@ export type InitAppEnvironmentsMutationVariables = Exact<{ export type InitAppEnvironmentsMutation = { __typename?: 'Mutation', devEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null, stagingEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null, prodEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null }; +export type InviteMemberMutationVariables = Exact<{ + orgId: Scalars['ID']; + email: Scalars['String']; + apps?: InputMaybe> | InputMaybe>; +}>; + + +export type InviteMemberMutation = { __typename?: 'Mutation', inviteOrganisationMember?: { __typename?: 'InviteOrganisationMemberMutation', invite?: { __typename?: 'OrganisationMemberInviteType', id: string } | null } | null }; + export type RotateAppKeyMutationVariables = Exact<{ id: Scalars['ID']; appToken: Scalars['String']; @@ -719,6 +758,7 @@ export type CreateNewUserTokenMutationVariables = Exact<{ identityKey: Scalars['String']; token: Scalars['String']; wrappedKeyShare: Scalars['String']; + expiry?: InputMaybe; }>; @@ -777,6 +817,13 @@ 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 } | null> | null }; +export type GetInvitesQueryVariables = Exact<{ + orgId: Scalars['ID']; +}>; + + +export type GetInvitesQuery = { __typename?: 'Query', organisationInvites?: Array<{ __typename?: 'OrganisationMemberInviteType', id: string, createdAt?: any | null, expiresAt: any, inviteeEmail: string, invitedBy: { __typename?: 'OrganisationMemberType', email?: string | null } } | null> | null }; + export type GetOrganisationAdminsAndSelfQueryVariables = Exact<{ organisationId: Scalars['ID']; }>; @@ -847,7 +894,7 @@ export type GetUserTokensQueryVariables = Exact<{ }>; -export type GetUserTokensQuery = { __typename?: 'Query', userTokens?: Array<{ __typename?: 'UserTokenType', id: string, name: string, wrappedKeyShare: string, createdAt?: any | null } | null> | null }; +export type GetUserTokensQuery = { __typename?: 'Query', userTokens?: Array<{ __typename?: 'UserTokenType', id: string, name: string, wrappedKeyShare: string, createdAt?: any | null, expiresAt?: any | null } | null> | null }; export const CreateApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateApplication"},"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":"organisationId"}},"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":"appToken"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appSeed"}},"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":"appVersion"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"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":"appToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appToken"}}},{"kind":"Argument","name":{"kind":"Name","value":"appSeed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appSeed"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"appVersion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appVersion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -863,8 +910,9 @@ export const DeleteSecretOpDocument = {"kind":"Document","definitions":[{"kind": export const RevokeServiceTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeServiceToken"},"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":"deleteServiceToken"},"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 UpdateSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSecret"},"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":"secretData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; +export const InviteMemberDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InviteMember"},"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":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apps"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inviteOrganisationMember"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}},{"kind":"Argument","name":{"kind":"Name","value":"apps"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apps"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invite"},"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 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"}}}}],"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; +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 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"}}]}}]}}]} as unknown as DocumentNode; @@ -872,6 +920,7 @@ export const GetAppLogCountDocument = {"kind":"Document","definitions":[{"kind": export const GetAppLogsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppLogs"},"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":"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":"logsCount"},"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":"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"}}]}}]}}]} 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"}}]}}]}}]} 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":"inviteeEmail"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationAdminsAndSelfDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationAdminsAndSelf"},"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":"organisationAdminsAndSelf"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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"}}}}],"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"}}],"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"}}]}}]}}]} as unknown as DocumentNode; @@ -881,4 +930,4 @@ export const GetSecretNamesDocument = {"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"}}}}],"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"}}}],"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":"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":"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":"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":"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":"eventType"}}]}}]}},{"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":"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 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":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"keys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"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":"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 5ab8b83d6..9a2f54eb8 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -2,6 +2,7 @@ type Query { organisations: [OrganisationType] organisationMembers(organisationId: ID, userId: ID, role: [String]): [OrganisationMemberType] organisationAdminsAndSelf(organisationId: ID): [OrganisationMemberType] + organisationInvites(orgId: ID): [OrganisationMemberInviteType] apps(organisationId: ID, appId: ID): [AppType] logs(appId: ID, start: BigInt, end: BigInt): [KMSLogType] logsCount(appId: ID, thisMonth: Boolean): Int @@ -66,6 +67,16 @@ enum ApiOrganisationMemberRoleChoices { DEV } +type OrganisationMemberInviteType { + id: String! + invitedBy: OrganisationMemberType! + inviteeEmail: String! + valid: Boolean! + createdAt: DateTime + updatedAt: DateTime! + expiresAt: DateTime! +} + type AppType { id: String! name: String! @@ -245,6 +256,7 @@ type ServiceTokenType { type Mutation { createOrganisation(id: ID!, identityKey: String!, name: String!): CreateOrganisationMutation + inviteOrganisationMember(apps: [String], email: String!, orgId: ID!): InviteOrganisationMemberMutation createApp(appSeed: String!, appToken: String!, appVersion: Int!, id: ID!, identityKey: String!, name: String!, organisationId: ID!, wrappedKeyShare: String!): CreateAppMutation rotateAppKeys(appToken: String!, id: ID!, wrappedKeyShare: String!): RotateAppKeysMutation deleteApp(id: ID!): DeleteAppMutation @@ -266,6 +278,10 @@ type CreateOrganisationMutation { organisation: OrganisationType } +type InviteOrganisationMemberMutation { + invite: OrganisationMemberInviteType +} + type CreateAppMutation { app: AppType } From 9d9ee77c1bb8afb8fc8c843de52251397963704f Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 12 Sep 2023 19:09:25 +0530 Subject: [PATCH 116/161] feat: add invites query and mutation --- .../mutations/organisation/inviteNewMember.gql | 7 +++++++ frontend/graphql/queries/organisation/getInvites.gql | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 frontend/graphql/mutations/organisation/inviteNewMember.gql create mode 100644 frontend/graphql/queries/organisation/getInvites.gql diff --git a/frontend/graphql/mutations/organisation/inviteNewMember.gql b/frontend/graphql/mutations/organisation/inviteNewMember.gql new file mode 100644 index 000000000..d0bdbe07e --- /dev/null +++ b/frontend/graphql/mutations/organisation/inviteNewMember.gql @@ -0,0 +1,7 @@ +mutation InviteMember($orgId: ID!, $email: String!, $apps: [String]) { + inviteOrganisationMember(orgId: $orgId, email: $email, apps: $apps) { + invite { + id + } + } +} diff --git a/frontend/graphql/queries/organisation/getInvites.gql b/frontend/graphql/queries/organisation/getInvites.gql new file mode 100644 index 000000000..46e8b5322 --- /dev/null +++ b/frontend/graphql/queries/organisation/getInvites.gql @@ -0,0 +1,11 @@ +query GetInvites($orgId: ID!) { + organisationInvites(orgId: $orgId) { + id + createdAt + expiresAt + invitedBy { + email + } + inviteeEmail + } +} From 61b69836e76b3c072b24e541cbdd6c1577b055cb Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Tue, 12 Sep 2023 19:16:17 +0530 Subject: [PATCH 117/161] feat: add organisation context --- frontend/app/[team]/layout.tsx | 25 +++++++------ frontend/contexts/organisationContext.tsx | 44 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 frontend/contexts/organisationContext.tsx diff --git a/frontend/app/[team]/layout.tsx b/frontend/app/[team]/layout.tsx index 4d73dcc0b..018a15df3 100644 --- a/frontend/app/[team]/layout.tsx +++ b/frontend/app/[team]/layout.tsx @@ -4,6 +4,7 @@ import '@/app/globals.css' import { HeroPattern } from '@/components/common/HeroPattern' import { NavBar } from '@/components/layout/Navbar' import Sidebar from '@/components/layout/Sidebar' +import { OrganisationProvider } from '@/contexts/organisationContext' import clsx from 'clsx' import { usePathname } from 'next/navigation' @@ -19,16 +20,18 @@ export default function RootLayout({ const showNav = !path?.split('/').includes('newdevice') return ( -
- - {showNav && } - {showNav && } -
{children}
-
+ +
+ + {showNav && } + {showNav && } +
{children}
+
+
) } diff --git a/frontend/contexts/organisationContext.tsx b/frontend/contexts/organisationContext.tsx new file mode 100644 index 000000000..b71bbe004 --- /dev/null +++ b/frontend/contexts/organisationContext.tsx @@ -0,0 +1,44 @@ +import { OrganisationType } from '@/apollo/graphql' +import { createContext, useEffect, useState } from 'react' +import GetOrganisations from '@/graphql/queries/getOrganisations.gql' +import { useQuery } from '@apollo/client' + +interface OrganisationContextValue { + activeOrganisation: OrganisationType | null + organisations: OrganisationType[] + setOrganisation: (organisation: OrganisationType) => void +} + +export const organisationContext = createContext({ + activeOrganisation: null, + organisations: [], + setOrganisation: () => {}, +}) + +interface OrganisationProviderProps { + children: React.ReactNode +} + +export const OrganisationProvider: React.FC = ({ children }) => { + const { data: orgsData } = useQuery(GetOrganisations) + + const [organisation, setOrganisation] = useState(null) + + useEffect(() => { + if (organisation === null && orgsData) { + setOrganisation(orgsData.organisations[0]) + } + }, [organisation, orgsData]) + + return ( + + {children} + + ) +} From 69a4fa1c39470d546b3e57b5000c7384ed42f688 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Wed, 13 Sep 2023 15:09:12 +0530 Subject: [PATCH 118/161] feat: increase free app limit to 5 --- backend/ee/feature_flags.py | 9 +++++---- frontend/components/apps/NewAppDialog.tsx | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/ee/feature_flags.py b/backend/ee/feature_flags.py index df8d37284..c1ea1107d 100644 --- a/backend/ee/feature_flags.py +++ b/backend/ee/feature_flags.py @@ -11,13 +11,14 @@ def allow_new_app(organisation): Returns: bool: Whether or not to allow creating an app for the given org """ - FREE_APP_LIMIT = 1 + FREE_APP_LIMIT = 5 PRO_APP_LIMIT = 10 - current_app_count = App.objects.filter(organisation=organisation, is_deleted=False).count() - + current_app_count = App.objects.filter( + organisation=organisation, is_deleted=False).count() + if organisation.plan == Organisation.FREE_PLAN and current_app_count >= FREE_APP_LIMIT: return False elif organisation.plan == Organisation.PRO_PLAN and current_app_count >= PRO_APP_LIMIT: return False - return True \ No newline at end of file + return True diff --git a/frontend/components/apps/NewAppDialog.tsx b/frontend/components/apps/NewAppDialog.tsx index 104e05840..ec70a7e21 100644 --- a/frontend/components/apps/NewAppDialog.tsx +++ b/frontend/components/apps/NewAppDialog.tsx @@ -18,7 +18,7 @@ import { import { splitSecret } from '@/utils/keyshares' import { UpgradeRequestForm } from '../forms/UpgradeRequestForm' -const FREE_APP_LIMIT = 1 +const FREE_APP_LIMIT = 5 const PRO_APP_LIMIT = 10 export default function NewAppDialog(props: { From 7bc5426865e970aec528879d4f494fdb3cc71dd3 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 14 Sep 2023 20:18:57 +0530 Subject: [PATCH 119/161] feat: add role to invite model --- .../0033_organisationmemberinvite_role.py | 18 +++++++++++++ backend/api/models.py | 5 ++++ .../graphene/mutations/organisation.py | 25 +++++++++++++++++-- backend/backend/graphene/types.py | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 backend/api/migrations/0033_organisationmemberinvite_role.py diff --git a/backend/api/migrations/0033_organisationmemberinvite_role.py b/backend/api/migrations/0033_organisationmemberinvite_role.py new file mode 100644 index 000000000..31b73a6a6 --- /dev/null +++ b/backend/api/migrations/0033_organisationmemberinvite_role.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.3 on 2023-09-14 08:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0032_organisationmemberinvite_apps'), + ] + + operations = [ + migrations.AddField( + model_name='organisationmemberinvite', + name='role', + field=models.CharField(choices=[('owner', 'Owner'), ('admin', 'Admin'), ('dev', 'Developer')], default='dev', max_length=5), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index 168d119c4..2ffb8c4c9 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -157,6 +157,11 @@ class OrganisationMemberInvite(models.Model): organisation = models.ForeignKey( Organisation, related_name='invites', on_delete=models.CASCADE) apps = models.ManyToManyField(App) + role = models.CharField( + max_length=5, + choices=OrganisationMember.USER_ROLES, + default=OrganisationMember.DEVELOPER, + ) invited_by = models.ForeignKey( OrganisationMember, on_delete=models.CASCADE) invitee_email = models.EmailField() diff --git a/backend/backend/graphene/mutations/organisation.py b/backend/backend/graphene/mutations/organisation.py index 07260ea0f..af8ca6187 100644 --- a/backend/backend/graphene/mutations/organisation.py +++ b/backend/backend/graphene/mutations/organisation.py @@ -36,11 +36,12 @@ class Arguments: org_id = graphene.ID(required=True) email = graphene.String(required=True) apps = graphene.List(graphene.String) + role = graphene.String() invite = graphene.Field(OrganisationMemberInviteType) @classmethod - def mutate(cls, root, info, org_id, email, apps): + def mutate(cls, root, info, org_id, email, apps, role): if user_is_admin(info.context.user, org_id): user_already_exists = OrganisationMember.objects.filter( organisation_id=org_id, user__email=email).exists() @@ -56,7 +57,7 @@ def mutate(cls, root, info, org_id, email, apps): app_scope = App.objects.filter(id__in=apps) invite = OrganisationMemberInvite.objects.create( - organisation_id=org_id, invited_by=invited_by, invitee_email=email, expires_at=expiry) + organisation_id=org_id, invited_by=invited_by, role=role, invitee_email=email, expires_at=expiry) invite.apps.set(app_scope) @@ -66,6 +67,26 @@ def mutate(cls, root, info, org_id, email, apps): "You don't have permission to perform this action") +class DeleteInviteMutation(graphene.Mutation): + class Arguments: + invite_id = graphene.ID(required=True) + + ok = graphene.Boolean() + + @classmethod + def mutate(cls, rooot, info, invite_id): + invite = OrganisationMemberInvite.objects.get(id=invite_id) + + if user_is_admin(info.context.user, invite.organisation.id): + invite.delete() + + return DeleteInviteMutation(ok=True) + + else: + raise GraphQLError( + "You don't have permission to perform this action") + + class CreateOrganisationMemberMutation(graphene.Mutation): class Arguments: org_id = graphene.ID(required=True) diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 10631f6e1..32f877eee 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -33,7 +33,7 @@ def resolve_username(self, info): class OrganisationMemberInviteType(DjangoObjectType): class Meta: model = OrganisationMemberInvite - fields = ('id', 'invited_by', 'invitee_email', 'valid', + fields = ('id', 'invited_by', 'invitee_email', 'valid', 'organisation', 'apps', 'role', 'created_at', 'updated_at', 'expires_at') From e466a8c9bfd15bf66480e212c7fd9603481e122c Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 14 Sep 2023 20:19:07 +0530 Subject: [PATCH 120/161] feat: add invite validation query --- backend/backend/schema.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/backend/backend/schema.py b/backend/backend/schema.py index a23596a5d..55e981f5c 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -1,7 +1,7 @@ from .graphene.mutations.environment import CreateEnvironmentKeyMutation, CreateEnvironmentMutation, CreateEnvironmentTokenMutation, CreateSecretFolderMutation, CreateSecretMutation, CreateSecretTagMutation, CreateServiceTokenMutation, CreateUserTokenMutation, DeleteSecretMutation, DeleteServiceTokenMutation, DeleteUserTokenMutation, EditSecretMutation from .graphene.utils.permissions import user_can_access_app, user_can_access_environment, user_is_admin, user_is_org_member from .graphene.mutations.app import CreateAppMutation, DeleteAppMutation, RotateAppKeysMutation -from .graphene.mutations.organisation import CreateOrganisationMutation, InviteOrganisationMemberMutation +from .graphene.mutations.organisation import CreateOrganisationMutation, DeleteInviteMutation, InviteOrganisationMemberMutation from .graphene.types import AppType, ChartDataPointType, EnvironmentKeyType, EnvironmentTokenType, EnvironmentType, KMSLogType, OrganisationMemberInviteType, OrganisationMemberType, OrganisationType, SecretEventType, SecretTagType, SecretType, ServiceTokenType, TimeRange, UserTokenType import graphene from graphql import GraphQLError @@ -11,6 +11,7 @@ from django.conf import settings from logs.models import KMSDBLog from itertools import chain +from django.utils import timezone CLOUD_HOSTED = settings.APP_HOST == 'cloud' @@ -23,6 +24,8 @@ class Query(graphene.ObjectType): OrganisationMemberType, organisation_id=graphene.ID()) organisation_invites = graphene.List( OrganisationMemberInviteType, org_id=graphene.ID()) + validate_invite = graphene.Field( + OrganisationMemberInviteType, invite_id=graphene.ID()) apps = graphene.List( AppType, organisation_id=graphene.ID(), app_id=graphene.ID()) logs = graphene.List(KMSLogType, app_id=graphene.ID(), @@ -54,9 +57,15 @@ def resolve_organisation_members(root, info, organisation_id, role, user_id=None if not user_is_org_member(info.context.user.userId, organisation_id): raise GraphQLError("You don't have access to this organisation") - roles = [user_role.lower() for user_role in role] + filter = { + "organisation_id": organisation_id + } + + if role: + roles = [user_role.lower() for user_role in role] + filter["roles__in"] = roles - return OrganisationMember.objects.filter(organisation_id=organisation_id, role__in=roles) + return OrganisationMember.objects.filter(**filter) def resolve_organisation_admins_and_self(root, info, organisation_id): if not user_is_org_member(info.context.user.userId, organisation_id): @@ -83,6 +92,21 @@ def resolve_organisation_invites(root, info, org_id): return invites + def resolve_validate_invite(root, info, invite_id): + try: + invite = OrganisationMemberInvite.objects.get( + id=invite_id, valid=True) + except: + raise GraphQLError("This invite is invalid") + + if invite.expires_at < timezone.now(): + raise GraphQLError("This invite has expired") + + if invite.invitee_email == info.context.user.email: + return invite + else: + raise GraphQLError("This invite is for another user") + def resolve_apps(root, info, organisation_id, app_id): filter = { 'organisation_id': organisation_id, @@ -278,6 +302,7 @@ def resolve_app_activity_chart(root, info, app_id, period=TimeRange.DAY): class Mutation(graphene.ObjectType): create_organisation = CreateOrganisationMutation.Field() invite_organisation_member = InviteOrganisationMemberMutation.Field() + delete_invitation = DeleteInviteMutation.Field() create_app = CreateAppMutation.Field() rotate_app_keys = RotateAppKeysMutation.Field() delete_app = DeleteAppMutation.Field() From d74c9a469c74e79bdc06be6ca78bc7fbde1e08df Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 14 Sep 2023 20:19:31 +0530 Subject: [PATCH 121/161] chore: regenerate schema and types --- frontend/apollo/gql.ts | 14 ++++++++-- frontend/apollo/graphql.ts | 51 ++++++++++++++++++++++++++++++++-- frontend/apollo/schema.graphql | 23 ++++++++++++++- 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index 377ae59b1..53a2ce6ef 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -26,6 +26,7 @@ const documents = { "mutation RevokeServiceToken($tokenId: ID!) {\n deleteServiceToken(tokenId: $tokenId) {\n ok\n }\n}": types.RevokeServiceTokenDocument, "mutation UpdateSecret($id: ID!, $secretData: SecretInput!) {\n editSecret(id: $id, secretData: $secretData) {\n secret {\n id\n updatedAt\n }\n }\n}": types.UpdateSecretDocument, "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}": types.InitAppEnvironmentsDocument, + "mutation DeleteInvite($inviteId: ID!) {\n deleteInvitation(inviteId: $inviteId) {\n ok\n }\n}": types.DeleteInviteDocument, "mutation InviteMember($orgId: ID!, $email: String!, $apps: [String]) {\n inviteOrganisationMember(orgId: $orgId, email: $email, apps: $apps) {\n invite {\n id\n }\n }\n}": types.InviteMemberDocument, "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 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, @@ -38,7 +39,8 @@ const documents = { "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n }\n}": types.GetOrganisationsDocument, "query GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n }\n inviteeEmail\n }\n}": types.GetInvitesDocument, "query GetOrganisationAdminsAndSelf($organisationId: ID!) {\n organisationAdminsAndSelf(organisationId: $organisationId) {\n id\n role\n identityKey\n }\n}": types.GetOrganisationAdminsAndSelfDocument, - "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}": types.GetOrganisationMembersDocument, + "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n id\n role\n identityKey\n email\n username\n createdAt\n }\n}": types.GetOrganisationMembersDocument, + "query VerifyInvite($inviteId: ID!) {\n validateInvite(inviteId: $inviteId) {\n id\n inviteeEmail\n apps {\n id\n name\n }\n organisation {\n id\n }\n }\n}": types.VerifyInviteDocument, "query GetAppEnvironments($appId: ID!) {\n appEnvironments(appId: $appId, environmentId: null) {\n id\n name\n envType\n identityKey\n wrappedSeed\n wrappedSalt\n createdAt\n }\n}": types.GetAppEnvironmentsDocument, "query GetEnvironmentKey($envId: ID!) {\n environmentKeys(environmentId: $envId) {\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, @@ -115,6 +117,10 @@ export function graphql(source: "mutation UpdateSecret($id: ID!, $secretData: Se * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n}"): (typeof documents)["mutation InitAppEnvironments($devEnv: EnvironmentInput!, $stagingEnv: EnvironmentInput!, $prodEnv: EnvironmentInput!) {\n devEnvironment: createEnvironment(environmentData: $devEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n stagingEnvironment: createEnvironment(environmentData: $stagingEnv) {\n environment {\n id\n name\n createdAt\n identityKey\n }\n }\n prodEnvironment: createEnvironment(environmentData: $prodEnv) {\n environment {\n id\n name\n createdAt\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: "mutation DeleteInvite($inviteId: ID!) {\n deleteInvitation(inviteId: $inviteId) {\n ok\n }\n}"): (typeof documents)["mutation DeleteInvite($inviteId: ID!) {\n deleteInvitation(inviteId: $inviteId) {\n ok\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -166,7 +172,11 @@ export function graphql(source: "query GetOrganisationAdminsAndSelf($organisatio /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}"): (typeof documents)["query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n role\n identityKey\n }\n}"]; +export function graphql(source: "query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n id\n role\n identityKey\n email\n username\n createdAt\n }\n}"): (typeof documents)["query GetOrganisationMembers($organisationId: ID!, $role: [String]) {\n organisationMembers(organisationId: $organisationId, role: $role) {\n id\n role\n identityKey\n email\n username\n createdAt\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 VerifyInvite($inviteId: ID!) {\n validateInvite(inviteId: $inviteId) {\n id\n inviteeEmail\n apps {\n id\n name\n }\n organisation {\n id\n }\n }\n}"): (typeof documents)["query VerifyInvite($inviteId: ID!) {\n validateInvite(inviteId: $inviteId) {\n id\n inviteeEmail\n apps {\n id\n name\n }\n organisation {\n id\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 97611143c..ffa9914db 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -36,6 +36,16 @@ export enum ApiEnvironmentEnvTypeChoices { Staging = 'STAGING' } +/** An enumeration. */ +export enum ApiOrganisationMemberInviteRoleChoices { + /** Admin */ + Admin = 'ADMIN', + /** Developer */ + Dev = 'DEV', + /** Owner */ + Owner = 'OWNER' +} + /** An enumeration. */ export enum ApiOrganisationMemberRoleChoices { /** Admin */ @@ -143,6 +153,11 @@ export type DeleteAppMutation = { app?: Maybe; }; +export type DeleteInviteMutation = { + __typename?: 'DeleteInviteMutation'; + ok?: Maybe; +}; + export type DeleteSecretMutation = { __typename?: 'DeleteSecretMutation'; secret?: Maybe; @@ -249,6 +264,7 @@ export type Mutation = { createServiceToken?: Maybe; createUserToken?: Maybe; deleteApp?: Maybe; + deleteInvitation?: Maybe; deleteSecret?: Maybe; deleteServiceToken?: Maybe; deleteUserToken?: Maybe; @@ -346,6 +362,11 @@ export type MutationDeleteAppArgs = { }; +export type MutationDeleteInvitationArgs = { + inviteId: Scalars['ID']; +}; + + export type MutationDeleteSecretArgs = { id: Scalars['ID']; }; @@ -371,6 +392,7 @@ export type MutationInviteOrganisationMemberArgs = { apps?: InputMaybe>>; email: Scalars['String']; orgId: Scalars['ID']; + role?: InputMaybe; }; @@ -388,11 +410,14 @@ export type Node = { export type OrganisationMemberInviteType = { __typename?: 'OrganisationMemberInviteType'; + apps: Array; createdAt?: Maybe; expiresAt: Scalars['DateTime']; id: Scalars['String']; invitedBy: OrganisationMemberType; inviteeEmail: Scalars['String']; + organisation: OrganisationType; + role: ApiOrganisationMemberInviteRoleChoices; updatedAt: Scalars['DateTime']; valid: Scalars['Boolean']; }; @@ -436,6 +461,7 @@ export type Query = { secrets?: Maybe>>; serviceTokens?: Maybe>>; userTokens?: Maybe>>; + validateInvite?: Maybe; }; @@ -521,6 +547,11 @@ export type QueryUserTokensArgs = { organisationId?: InputMaybe; }; + +export type QueryValidateInviteArgs = { + inviteId?: InputMaybe; +}; + export type RotateAppKeysMutation = { __typename?: 'RotateAppKeysMutation'; app?: Maybe; @@ -734,6 +765,13 @@ export type InitAppEnvironmentsMutationVariables = Exact<{ export type InitAppEnvironmentsMutation = { __typename?: 'Mutation', devEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null, stagingEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null, prodEnvironment?: { __typename?: 'CreateEnvironmentMutation', environment?: { __typename?: 'EnvironmentType', id: string, name: string, createdAt?: any | null, identityKey: string } | null } | null }; +export type DeleteInviteMutationVariables = Exact<{ + inviteId: Scalars['ID']; +}>; + + +export type DeleteInviteMutation = { __typename?: 'Mutation', deleteInvitation?: { __typename?: 'DeleteInviteMutation', ok?: boolean | null } | null }; + export type InviteMemberMutationVariables = Exact<{ orgId: Scalars['ID']; email: Scalars['String']; @@ -837,7 +875,14 @@ export type GetOrganisationMembersQueryVariables = Exact<{ }>; -export type GetOrganisationMembersQuery = { __typename?: 'Query', organisationMembers?: Array<{ __typename?: 'OrganisationMemberType', role: ApiOrganisationMemberRoleChoices, identityKey?: string | null } | null> | null }; +export type GetOrganisationMembersQuery = { __typename?: 'Query', organisationMembers?: Array<{ __typename?: 'OrganisationMemberType', id: string, role: ApiOrganisationMemberRoleChoices, identityKey?: string | null, email?: string | null, username?: string | null, createdAt?: any | null } | null> | null }; + +export type VerifyInviteQueryVariables = Exact<{ + inviteId: Scalars['ID']; +}>; + + +export type VerifyInviteQuery = { __typename?: 'Query', validateInvite?: { __typename?: 'OrganisationMemberInviteType', id: string, inviteeEmail: string, apps: Array<{ __typename?: 'AppType', id: string, name: string }>, organisation: { __typename?: 'OrganisationType', id: string } } | null }; export type GetAppEnvironmentsQueryVariables = Exact<{ appId: Scalars['ID']; @@ -910,6 +955,7 @@ export const DeleteSecretOpDocument = {"kind":"Document","definitions":[{"kind": export const RevokeServiceTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeServiceToken"},"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":"deleteServiceToken"},"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 UpdateSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSecret"},"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":"secretData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SecretInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const InitAppEnvironmentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InitAppEnvironments"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EnvironmentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"devEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"devEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"stagingEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stagingEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"prodEnvironment"},"name":{"kind":"Name","value":"createEnvironment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environmentData"},"value":{"kind":"Variable","name":{"kind":"Name","value":"prodEnv"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"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":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteInvite"},"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":"deleteInvitation"},"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":"ok"}}]}}]}}]} as unknown as DocumentNode; export const InviteMemberDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InviteMember"},"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":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apps"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inviteOrganisationMember"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}},{"kind":"Argument","name":{"kind":"Name","value":"apps"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apps"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invite"},"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 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; @@ -922,7 +968,8 @@ export const GetAppsDocument = {"kind":"Document","definitions":[{"kind":"Operat 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"}}]}}]}}]} 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":"inviteeEmail"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationAdminsAndSelfDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationAdminsAndSelf"},"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":"organisationAdminsAndSelf"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]} 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"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} 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":"inviteeEmail"}},{"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":"organisation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} 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"}}}}],"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"}}],"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"}}]}}]}}]} 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"}}}}],"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"}}}],"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; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 9a2f54eb8..f408f4702 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -3,6 +3,7 @@ type Query { organisationMembers(organisationId: ID, userId: ID, role: [String]): [OrganisationMemberType] organisationAdminsAndSelf(organisationId: ID): [OrganisationMemberType] organisationInvites(orgId: ID): [OrganisationMemberInviteType] + validateInvite(inviteId: ID): OrganisationMemberInviteType apps(organisationId: ID, appId: ID): [AppType] logs(appId: ID, start: BigInt, end: BigInt): [KMSLogType] logsCount(appId: ID, thisMonth: Boolean): Int @@ -69,6 +70,9 @@ enum ApiOrganisationMemberRoleChoices { type OrganisationMemberInviteType { id: String! + organisation: OrganisationType! + apps: [AppType!]! + role: ApiOrganisationMemberInviteRoleChoices! invitedBy: OrganisationMemberType! inviteeEmail: String! valid: Boolean! @@ -88,6 +92,18 @@ type AppType { createdAt: DateTime } +"""An enumeration.""" +enum ApiOrganisationMemberInviteRoleChoices { + """Owner""" + OWNER + + """Admin""" + ADMIN + + """Developer""" + DEV +} + type KMSLogType implements Node { id: ID! timestamp: BigInt @@ -256,7 +272,8 @@ type ServiceTokenType { type Mutation { createOrganisation(id: ID!, identityKey: String!, name: String!): CreateOrganisationMutation - inviteOrganisationMember(apps: [String], email: String!, orgId: ID!): InviteOrganisationMemberMutation + inviteOrganisationMember(apps: [String], email: String!, orgId: ID!, role: String): InviteOrganisationMemberMutation + deleteInvitation(inviteId: ID!): DeleteInviteMutation createApp(appSeed: String!, appToken: String!, appVersion: Int!, id: ID!, identityKey: String!, name: String!, organisationId: ID!, wrappedKeyShare: String!): CreateAppMutation rotateAppKeys(appToken: String!, id: ID!, wrappedKeyShare: String!): RotateAppKeysMutation deleteApp(id: ID!): DeleteAppMutation @@ -282,6 +299,10 @@ type InviteOrganisationMemberMutation { invite: OrganisationMemberInviteType } +type DeleteInviteMutation { + ok: Boolean +} + type CreateAppMutation { app: AppType } From 2537295751b46e165142df085b27494c9e486462 Mon Sep 17 00:00:00 2001 From: rohan-chaturvedi Date: Thu, 14 Sep 2023 20:21:22 +0530 Subject: [PATCH 122/161] feat: members page --- frontend/app/[team]/members/page.tsx | 525 ++++++++++++++++++ frontend/components/layout/Sidebar.tsx | 20 +- .../mutations/organisation/deleteInvite.gql | 5 + .../organisation/inviteNewMember.gql | 4 +- .../organisation/getOrganisationMembers.gql | 4 + frontend/utils/auth.ts | 19 +- 6 files changed, 564 insertions(+), 13 deletions(-) create mode 100644 frontend/app/[team]/members/page.tsx create mode 100644 frontend/graphql/mutations/organisation/deleteInvite.gql diff --git a/frontend/app/[team]/members/page.tsx b/frontend/app/[team]/members/page.tsx new file mode 100644 index 000000000..72371341f --- /dev/null +++ b/frontend/app/[team]/members/page.tsx @@ -0,0 +1,525 @@ +'use client' + +import GetOrganisationMembers from '@/graphql/queries/organisation/getOrganisationMembers.gql' +import GetInvites from '@/graphql/queries/organisation/getInvites.gql' +import GetApps from '@/graphql/queries/getApps.gql' +import InviteMember from '@/graphql/mutations/organisation/inviteNewMember.gql' +import DeleteInvite from '@/graphql/mutations/organisation/deleteInvite.gql' +import { useLazyQuery, useMutation, useQuery } from '@apollo/client' +import { Fragment, useContext, useEffect, useState } from 'react' +import { + OrganisationMemberInviteType, + OrganisationMemberType, + AppType, + ApiOrganisationMemberRoleChoices, +} from '@/apollo/graphql' +import { Button } from '@/components/common/Button' +import { organisationContext } from '@/contexts/organisationContext' +import { relativeTimeFromDates } from '@/utils/time' +import { Dialog, Disclosure, RadioGroup, Transition } from '@headlessui/react' +import { + FaCheckSquare, + FaChevronDown, + FaCircle, + FaCopy, + FaDotCircle, + FaPlus, + FaSquare, + FaTimes, + FaTrashAlt, +} from 'react-icons/fa' +import clsx from 'clsx' +import { cryptoUtils } from '@/utils/auth' +import { copyToClipBoard } from '@/utils/clipboard' +import { toast } from 'react-toastify' + +const handleCopy = (val: string) => { + copyToClipBoard(val) + toast.info('Copied', { autoClose: 2000 }) +} + +const InviteDialog = (props: { organisationId: string }) => { + const { organisationId } = props + + const { data: invitesData, loading: invitesLoading } = useQuery(GetInvites, { + variables: { orgId: organisationId }, + }) + const { data: appsData, loading: appsLoading } = useQuery(GetApps, { + variables: { organisationId, appId: '' }, + }) + const [createInvite, { data }] = useMutation(InviteMember) + const [deleteInvite] = useMutation(DeleteInvite) + + const [isOpen, setIsOpen] = useState(false) + + const [email, setEmail] = useState('') + const [role, setRole] = useState('Dev') + const [apps, setApps] = useState[]>([]) + + const [inviteLink, setInviteLink] = useState('') + + const roleOptions = Object.keys(ApiOrganisationMemberRoleChoices).filter( + (option) => option !== 'Owner' + ) + + const isLoading = invitesLoading || appsLoading + + const reset = () => { + setEmail('') + setRole('Dev') + setApps([]) + setInviteLink('') + } + + const closeModal = () => { + reset() + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + const handleClose = () => { + closeModal() + } + + const handleInvite = async (event: { preventDefault: () => void }) => { + event.preventDefault() + await createInvite({ + variables: { + email, + orgId: organisationId, + apps: apps.map((app) => app.id), + role, + }, + refetchQueries: [ + { + query: GetInvites, + variables: { + orgId: organisationId, + }, + }, + ], + }) + + setInviteLink(cryptoUtils.getInviteLink(data?.inviteOrganisationMember.invite.id)) + } + + const handleDeleteInvite = async (inviteId: string) => { + await deleteInvite({ + variables: { + inviteId, + }, + refetchQueries: [ + { + query: GetInvites, + variables: { + orgId: organisationId, + }, + }, + ], + }) + } + + const AppSelector = (props: { app: AppType }) => { + const { id: appId, name: appName } = props.app + + const isSelected = apps.map((app) => app.name).includes(appName) + + const handleAppClick = () => { + if (isSelected) { + setApps(apps.filter((app) => app.name !== appName)) + } else setApps([...apps, ...[{ id: appId, name: appName }]]) + } + + return ( +
+ {isSelected ? ( + + ) : ( + + )} +
+ {appName} +
+
+ ) + } + + const sortedInvites: OrganisationMemberInviteType[] = + invitesData?.organisationInvites + ?.slice() // Create a shallow copy of the array to avoid modifying the original + .sort((a: OrganisationMemberInviteType, b: OrganisationMemberInviteType) => { + // Compare the createdAt timestamps in descending order + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + }) || [] + + const DeleteInviteConfirmDialog = (props: { inviteId: string }) => { + const { inviteId } = props + + const [isOpen, setIsOpen] = useState(false) + + const closeModal = () => { + setIsOpen(false) + } + + const openModal = () => { + setIsOpen(true) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Delete Invite +

+ + +
+ +
+

+ Are you sure you want to delete this invite? +

+
+ + +
+
+
+
+
+
+
+
+ + ) + } + + return ( + <> +
+ +
+ + + + +
+ + +
+
+ + + +

+ Invite a new member +

+ + +
+ + {!isLoading && ( +
+
+ {!inviteLink && ( +
+
+
+ + setEmail(e.target.value)} + /> +
+
+ + + + +
+ {roleOptions.map((option) => ( + + {({ active, checked }) => ( +
+ {checked ? ( + + ) : ( + + )} + {option} +
+ )} +
+ ))} +
+
+
+
+ +
+ + {appsData.apps.map((appOption: AppType) => ( + + ))} +
+ +
+ + +
+
+ )} + {inviteLink && ( +
+
+

Invite sent

+

+ An invite link has been sent by email to {email}. You can also share + the link below to invite the user to your organisation. +

+
+
+
+ {inviteLink} +
+ +
+
+ )} +
+ +
+ + {({ open }) => ( + <> + +
+ + Invite history +
+
+ + +
+ + + + + + + + + + + + {sortedInvites.map( + (invite: OrganisationMemberInviteType) => ( + + + + + + + + ) + )} + +
+ Email + + Invited by + + Invited + + Expires +
+ {invite.inviteeEmail} + + {invite.invitedBy.email} + + {relativeTimeFromDates(new Date(invite.createdAt))} + + {relativeTimeFromDates(new Date(invite.expiresAt))} + + +
+
+
+
+ + )} +
+
+
+ )} +
+
+
+
+
+
+ + ) +} + +export default function Members({ params }: { params: { team: string } }) { + const [getMembers, { data: membersData }] = useLazyQuery(GetOrganisationMembers) + + const { activeOrganisation: organisation } = useContext(organisationContext) + + useEffect(() => { + if (organisation) { + getMembers({ + variables: { + organisationId: organisation.id, + role: null, + }, + }) + } + }, [organisation]) + + return ( +
+

{params.team} Members

+ +
+
+ {organisation && } +
+ + + + + + + + + + + + {membersData?.organisationMembers.map((member: OrganisationMemberType) => ( + + + + + + + ))} + +
+ Email + + Username + + Role + + Joined +
{member.email}{member.username}{member.role} + {relativeTimeFromDates(new Date(member.createdAt))} +
+
+
+ ) +} diff --git a/frontend/components/layout/Sidebar.tsx b/frontend/components/layout/Sidebar.tsx index 749dd5b3f..a4b12779e 100644 --- a/frontend/components/layout/Sidebar.tsx +++ b/frontend/components/layout/Sidebar.tsx @@ -4,7 +4,7 @@ import Link from 'next/link' import UserMenu from '../UserMenu' import { usePathname } from 'next/navigation' import clsx from 'clsx' -import { FaCog, FaCubes, FaHome } from 'react-icons/fa' +import { FaCog, FaCubes, FaHome, FaUsersCog } from 'react-icons/fa' export type SidebarLinkT = { name: string @@ -47,6 +47,12 @@ const Sidebar = () => { icon: , active: usePathname() === `/${team}/apps`, }, + { + name: 'Members', + href: `/${team}/members`, + icon: , + active: usePathname() === `/${team}/apps`, + }, { name: 'Settings', href: `/${team}/settings`, @@ -58,7 +64,7 @@ const Sidebar = () => { return (