diff --git a/backend/api/auth.py b/backend/api/auth.py index dfa5c7c83..7055e6fbc 100644 --- a/backend/api/auth.py +++ b/backend/api/auth.py @@ -17,6 +17,18 @@ logger = logging.getLogger(__name__) +class ServiceAccountUser: + """Mock ServiceAccount user""" + + def __init__(self, service_account): + self.userId = service_account.id + self.id = service_account.id + self.is_authenticated = True + self.is_active = True + self.username = service_account.name + self.service_account = service_account + + class PhaseTokenAuthentication(authentication.BaseAuthentication): def authenticate(self, request): @@ -122,8 +134,14 @@ def authenticate(self, request): try: service_token = get_service_token(auth_token) - service_account = get_service_account_from_token(auth_token) - user = service_token.created_by.user + service_account = get_service_account_from_token(auth_token) + + creator = getattr(service_token, "created_by", None) + if creator: + user = creator.user + else: + user = ServiceAccountUser(service_account) + auth["service_account"] = service_account auth["service_account_token"] = service_token diff --git a/backend/api/identity_providers.py b/backend/api/identity_providers.py new file mode 100644 index 000000000..5a62bdca3 --- /dev/null +++ b/backend/api/identity_providers.py @@ -0,0 +1,57 @@ +class IdentityProviders: + """ + Configuration for supported identity providers. + Similar to Providers in services.py but specifically for identity authentication. + """ + + AWS_IAM = { + "id": "aws_iam", + "name": "AWS IAM", + "description": "Use AWS STS GetCallerIdentity to authenticate.", + "icon_id": "aws", # Maps to ProviderIcon component + "supported": True, + } + + # Future identity providers can be added here: + # GitHub OIDC + # "id": "github_oidc", + # "name": "GitHub OIDC", + # "description": "Use GitHub OIDC for authentication.", + # "icon_id": "github", + # "supported": False, + # } + # + # Kubernetes OIDC + # "id": "kubernetes_oidc", + # "name": "Kubernetes OIDC", + # "description": "Use Kubernetes OIDC for authentication.", + # "icon_id": "kubernetes", + # "supported": False, + # } + + @classmethod + def get_all_providers(cls): + """Get all identity providers, including unsupported ones for future roadmap display.""" + return [ + provider + for provider in cls.__dict__.values() + if isinstance(provider, dict) + ] + + @classmethod + def get_supported_providers(cls): + """Get only currently supported identity providers.""" + return [ + provider + for provider in cls.__dict__.values() + if isinstance(provider, dict) and provider.get("supported", False) + ] + + @classmethod + def get_provider_config(cls, provider_id): + """Get configuration for a specific provider by ID.""" + for provider in cls.__dict__.values(): + if isinstance(provider, dict) and provider["id"] == provider_id: + return provider + raise ValueError(f"Identity provider '{provider_id}' not found") + diff --git a/backend/api/migrations/0110_serviceaccounttoken_created_by_service_account.py b/backend/api/migrations/0110_serviceaccounttoken_created_by_service_account.py new file mode 100644 index 000000000..36cf7c9f5 --- /dev/null +++ b/backend/api/migrations/0110_serviceaccounttoken_created_by_service_account.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.22 on 2025-08-22 08:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0109_alter_dynamicsecret_key_map"), + ] + + operations = [ + migrations.AddField( + model_name="serviceaccounttoken", + name="created_by_service_account", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="created_tokens", + to="api.serviceaccount", + ), + ), + ] diff --git a/backend/api/migrations/0111_identity_serviceaccount_identities.py b/backend/api/migrations/0111_identity_serviceaccount_identities.py new file mode 100644 index 000000000..2a570bbfa --- /dev/null +++ b/backend/api/migrations/0111_identity_serviceaccount_identities.py @@ -0,0 +1,56 @@ +# Generated by Django 4.2.22 on 2025-08-25 13:24 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0110_serviceaccounttoken_created_by_service_account"), + ] + + operations = [ + migrations.CreateModel( + name="Identity", + fields=[ + ( + "id", + models.TextField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("provider", models.CharField(max_length=64)), + ("name", models.CharField(max_length=100)), + ("description", models.TextField(blank=True, null=True)), + ("config", models.JSONField(default=dict)), + ( + "token_name_pattern", + models.CharField(blank=True, max_length=128, null=True), + ), + ("default_ttl_seconds", models.IntegerField(default=3600)), + ("max_ttl_seconds", models.IntegerField(default=86400)), + ("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)), + ( + "organisation", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="api.organisation", + ), + ), + ], + ), + migrations.AddField( + model_name="serviceaccount", + name="identities", + field=models.ManyToManyField( + blank=True, related_name="service_accounts", to="api.identity" + ), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index e26cc1740..0e3c657ba 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -279,6 +279,9 @@ class ServiceAccount(models.Model): network_policies = models.ManyToManyField( NetworkAccessPolicy, blank=True, related_name="service_accounts" ) + identities = models.ManyToManyField( + "Identity", blank=True, related_name="service_accounts" + ) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(null=True, blank=True) @@ -556,11 +559,37 @@ class ServiceAccountToken(models.Model): created_by = models.ForeignKey( OrganisationMember, on_delete=models.CASCADE, blank=True, null=True ) + created_by_service_account = models.ForeignKey( + ServiceAccount, + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="created_tokens", + ) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(blank=True, null=True) expires_at = models.DateTimeField(null=True) + def clean(self): + # Ensure only one of created_by or created_by_service_account is set + # Service accounts can create tokens for themselves and others + if not (self.created_by or self.created_by_service_account): + raise ValidationError( + "Must set either created_by (organisation member) or created_by_service_account" + ) + if self.created_by and self.created_by_service_account: + raise ValidationError( + "Only one of created_by or created_by_service_account may be set" + ) + + def save(self, *args, **kwargs): + self.full_clean() # This calls clean() and field validation + super().save(*args, **kwargs) + + def get_creator_account(self): + return self.created_by or self.created_by_service_account + def delete(self, *args, **kwargs): """ Soft delete the object by setting the 'deleted_at' field. @@ -878,6 +907,50 @@ class SecretEvent(models.Model): user_agent = models.TextField(null=True, blank=True) +class Identity(models.Model): + """ + Third-party identity configuration. + + Scope: Organisation level; can be attached to multiple ServiceAccounts. + """ + + id = models.TextField(default=uuid4, primary_key=True, editable=False) + organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) + provider = models.CharField(max_length=64) + name = models.CharField(max_length=100) + description = models.TextField(blank=True, null=True) + + # Provider-specific configuration + # Example for aws_iam: + # { + # "trustedPrincipals": "arn:..., arn:...", + # "signatureTtlSeconds": 60, + # "stsEndpoint": "https://sts.amazonaws.com" + # } + config = models.JSONField(default=dict) + + # Token configuration + token_name_pattern = models.CharField(max_length=128, blank=True, null=True) + default_ttl_seconds = models.IntegerField(default=3600) + max_ttl_seconds = models.IntegerField(default=86400) + + created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(blank=True, null=True) + + def get_trusted_list(self): + try: + principals = self.config.get("trustedPrincipals", []) + if isinstance(principals, list): + return [ + p.strip() for p in principals if isinstance(p, str) and p.strip() + ] + # Fallback for legacy comma-separated string format + return [p.strip() for p in str(principals).split(",") if p.strip()] + except Exception: + return [] + + class PersonalSecret(models.Model): id = models.TextField(default=uuid4, primary_key=True, editable=False) secret = models.ForeignKey(Secret, on_delete=models.CASCADE) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 2bc1aae87..6aee0c861 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -17,6 +17,7 @@ Secret, ServiceAccount, ServiceToken, + ServiceAccountToken, UserToken, PersonalSecret, ) @@ -307,7 +308,7 @@ class ServiceAccountTokenSerializer(serializers.ModelSerializer): ) class Meta: - model = UserToken + model = ServiceAccountToken fields = [ "wrapped_key_share", "account_id", diff --git a/backend/api/utils/access/roles.py b/backend/api/utils/access/roles.py index 6eb243191..138d0e50d 100644 --- a/backend/api/utils/access/roles.py +++ b/backend/api/utils/access/roles.py @@ -12,6 +12,7 @@ "MemberPersonalAccessTokens": ["create", "read", "update", "delete"], "ServiceAccounts": ["create", "read", "update", "delete"], "ServiceAccountTokens": ["create", "read", "update", "delete"], + "ExternalIdentities": ["create", "read", "update", "delete"], "Roles": ["create", "read", "update", "delete"], "IntegrationCredentials": ["create", "read", "update", "delete"], "NetworkAccessPolicies": ["create", "read", "update", "delete"], @@ -43,6 +44,7 @@ "MemberPersonalAccessTokens": ["create", "read", "update", "delete"], "ServiceAccounts": ["create", "read", "update", "delete"], "ServiceAccountTokens": ["create", "read", "update", "delete"], + "ExternalIdentities": ["create", "read", "update", "delete"], "Roles": ["create", "read", "update", "delete"], "IntegrationCredentials": ["create", "read", "update", "delete"], "NetworkAccessPolicies": ["create", "read", "update", "delete"], @@ -73,6 +75,7 @@ "Members": ["create", "read", "update", "delete"], "ServiceAccounts": ["create", "read", "update", "delete"], "ServiceAccountTokens": ["create", "read", "update", "delete"], + "ExternalIdentities": ["create", "read", "update", "delete"], "Roles": ["create", "read", "update", "delete"], "IntegrationCredentials": ["create", "read", "update", "delete"], "NetworkAccessPolicies": ["create", "read", "update", "delete"], @@ -103,6 +106,7 @@ "Members": ["read"], "ServiceAccounts": [], "ServiceAccountTokens": [], + "ExternalIdentities": [], "Roles": ["read"], "IntegrationCredentials": [ "create", @@ -137,6 +141,7 @@ "Members": ["read"], "ServiceAccounts": ["read"], "ServiceAccountTokens": ["read"], + "ExternalIdentities": ["read"], "Roles": ["read"], "IntegrationCredentials": ["read"], "NetworkAccessPolicies": ["read"], diff --git a/backend/api/utils/crypto.py b/backend/api/utils/crypto.py index f59757375..3ecfd86d5 100644 --- a/backend/api/utils/crypto.py +++ b/backend/api/utils/crypto.py @@ -1,6 +1,7 @@ import re from nacl.hash import blake2b from nacl.utils import random +from nacl.utils import random as random_bytes from base64 import b64encode, b64decode from django.conf import settings from nacl.bindings import ( @@ -12,6 +13,8 @@ crypto_kx_server_session_keys, crypto_secretbox_NONCEBYTES, crypto_generichash, + crypto_sign_ed25519_pk_to_curve25519, + crypto_sign_ed25519_sk_to_curve25519, ) from nacl.encoding import RawEncoder from typing import Tuple @@ -230,3 +233,94 @@ def validate_encrypted_string(encrypted_string): return bool(match) return True + + +def xor_bytes(first: bytes, second: bytes) -> bytes: + """ + XOR two byte strings of equal length. + + Args: + first: First byte string. + second: Second byte string. Must be the same length as `first`. + + Returns: + A new byte string containing the XOR of the inputs. + + Raises: + ValueError: If the inputs are not the same length. + """ + if len(first) != len(second): + raise ValueError("xor_bytes inputs must be the same length") + return bytes(x ^ y for x, y in zip(first, second)) + + +def split_secret_hex(secret_hex: str) -> Tuple[str, str]: + """ + Split a secret (hex string) into two additive/XOR shares. + + This uses a one-time pad approach where a uniformly random share is + generated and the second share is computed as share2 = random ^ secret. + + Args: + secret_hex: Secret as a hex string. + + Returns: + Tuple of two hex strings (share_a, share_b). + """ + secret_bytes = bytes.fromhex(secret_hex) + random_share = random_bytes(len(secret_bytes)) + last_share = xor_bytes(random_share, secret_bytes) + return random_share.hex(), last_share.hex() + + +def random_hex(nbytes: int = 32) -> str: + """ + Generate a random hex string. + + Args: + nbytes: Number of random bytes to generate. Defaults to 32. + + Returns: + Hex string of length 2 * nbytes. + """ + + return random_bytes(nbytes).hex() + + +def ed25519_to_kx(pub_hex: str, priv_hex: str) -> Tuple[str, str]: + """ + Convert an Ed25519 keypair to Curve25519 (key exchange) form. + + This is useful when the stored signing keys are in Ed25519 form but we + require Curve25519 keys for symmetric key agreement operations. + + Args: + pub_hex: Ed25519 public key in hex. + priv_hex: Ed25519 private key in hex. + + Returns: + Tuple of hex strings (kx_public_hex, kx_private_hex). + """ + + pub_kx = crypto_sign_ed25519_pk_to_curve25519(bytes.fromhex(pub_hex)).hex() + priv_kx = crypto_sign_ed25519_sk_to_curve25519(bytes.fromhex(priv_hex)).hex() + return pub_kx, priv_kx + + +def wrap_share_hex(share_hex: str, wrap_key_hex: str) -> str: + """ + Wrap (encrypt) a hex-encoded share using a symmetric key, returning hex. + + This reuses the module's low-level XChaCha20-Poly1305 helper `encrypt_raw` + to avoid duplicate cryptographic code paths. + + Args: + share_hex: The plaintext share to encrypt, as a hex string. + wrap_key_hex: Symmetric key as a hex string (32-byte key recommended). + + Returns: + A hex string representing ciphertext||nonce. + """ + key_bytes = bytes.fromhex(wrap_key_hex) + ct_plus_nonce = encrypt_raw(share_hex, key_bytes) + return bytes(ct_plus_nonce).hex() diff --git a/backend/api/utils/identity/aws.py b/backend/api/utils/identity/aws.py new file mode 100644 index 000000000..3290715a3 --- /dev/null +++ b/backend/api/utils/identity/aws.py @@ -0,0 +1,33 @@ +import botocore.loaders + + +def list_sts_endpoints(): + """ + Return a list of STS endpoints from botocore's endpoint metadata. + + Output: List[dict(regionCode, regionName, endpoint)] + """ + loader = botocore.loaders.create_loader() + endpoints_data = loader.load_data("endpoints") + partitions = endpoints_data.get("partitions", []) + results = [] + for part in partitions: + services = part.get("services", {}) + if "sts" not in services: + continue + service = services["sts"] + endpoints = service.get("endpoints", {}) + for region_code, meta in endpoints.items(): + hostname = meta.get("hostname") + if not hostname: + hostname = f"sts.{region_code}.{part.get('dnsSuffix', 'amazonaws.com')}" + region_name = region_code + results.append( + { + "regionCode": region_code, + "regionName": region_name, + "endpoint": f"https://{hostname}", + } + ) + + return results diff --git a/backend/api/utils/identity/common.py b/backend/api/utils/identity/common.py new file mode 100644 index 000000000..ab2cca1e8 --- /dev/null +++ b/backend/api/utils/identity/common.py @@ -0,0 +1,77 @@ +import json + +from django.utils import timezone + + +def resolve_service_account(account_id): + from api.models import ServiceAccount + + try: + service_account = ServiceAccount.objects.get(id=account_id) + except ServiceAccount.DoesNotExist: + return None + return service_account + + +def resolve_attached_identity(service_account, provider: str): + """Return the first non-deleted identity for the service account and provider.""" + return service_account.identities.filter(provider=provider, deleted_at=None).first() + + +def mint_service_account_token(service_account, identity, requested_ttl: int, token_name_fallback: str): + """ + Create a ServiceAccountToken for the given service account using the server + keyring, honoring the identity's TTL rules. Returns a dict suitable for + JSON response containing token strings and TTLs. + """ + from api.utils.crypto import ( + get_server_keypair, + decrypt_asymmetric, + split_secret_hex, + wrap_share_hex, + random_hex, + ed25519_to_kx, + ) + from api.models import ServiceAccountToken + + now = timezone.now() + + # Load and unwrap server-managed keyring + pk, sk = get_server_keypair() + keyring_json = decrypt_asymmetric( + service_account.server_wrapped_keyring, sk.hex(), pk.hex() + ) + keyring = json.loads(keyring_json) + kx_pub, kx_priv = ed25519_to_kx(keyring["publicKey"], keyring["privateKey"]) + + # Compute TTL consistent with limits + req_ttl = int(requested_ttl or identity.default_ttl_seconds) + ttl = min(req_ttl, identity.max_ttl_seconds) + expires_at = now + timezone.timedelta(seconds=ttl) + + # Create token material + wrap_key = random_hex(32) + token_value = random_hex(32) + share_a, share_b = split_secret_hex(kx_priv) + wrapped_share_b = wrap_share_hex(share_b, wrap_key) + + ServiceAccountToken.objects.create( + service_account=service_account, + name=(identity.token_name_pattern or token_name_fallback), + identity_key=kx_pub, + token=token_value, + wrapped_key_share=wrapped_share_b, + created_by_service_account=service_account, + expires_at=expires_at, + ) + + full_token = f"pss_service:v2:{token_value}:{kx_pub}:{share_a}:{wrap_key}" + bearer = f"ServiceAccount {token_value}" + + return { + "tokenType": "ServiceAccount", + "token": full_token, + "bearerToken": bearer, + "TTL": ttl, + "maxTTL": identity.max_ttl_seconds, + } diff --git a/backend/api/views/identities/__init__.py b/backend/api/views/identities/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/api/views/identities/aws/__init__.py b/backend/api/views/identities/aws/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/api/views/identities/aws/iam.py b/backend/api/views/identities/aws/iam.py new file mode 100644 index 000000000..630241736 --- /dev/null +++ b/backend/api/views/identities/aws/iam.py @@ -0,0 +1,187 @@ +import base64 +import json +from io import StringIO +from urllib.parse import urlparse +import fnmatch +import requests +from defusedxml.ElementTree import parse +from django.http import JsonResponse +from django.utils import timezone +from rest_framework.decorators import api_view, permission_classes +from rest_framework.permissions import AllowAny + +from api.utils.identity.common import ( + resolve_service_account, + resolve_attached_identity, + mint_service_account_token, +) + + +@api_view(["POST"]) +@permission_classes([AllowAny]) +def aws_iam_auth(request): + """Accepts SigV4-signed STS GetCallerIdentity request and issues a ServiceAccount token if trusted.""" + try: + payload = json.loads(request.body) + except Exception: + return JsonResponse({"error": "Invalid JSON"}, status=400) + + account = (payload or {}).get("account", {}) + aws_iam = (payload or {}).get("awsIam", {}) + token_req = (payload or {}).get("tokenRequest", {}) + + account_type = (account.get("type") or "service").lower() + account_id = account.get("id") + if account_type != "service" or not account_id: + return JsonResponse( + {"error": "Only service account authentication supported"}, status=400 + ) + + # Decode signed request + try: + method = aws_iam.get("httpRequestMethod") or "POST" + url = base64.b64decode(aws_iam["httpRequestUrl"]).decode("utf-8") + headers_json = base64.b64decode(aws_iam["httpRequestHeaders"]).decode("utf-8") + body = base64.b64decode(aws_iam.get("httpRequestBody") or b"").decode("utf-8") + headers = json.loads(headers_json) + except Exception: + return JsonResponse({"error": "Invalid awsIam request encoding"}, status=400) + + # Verify signature freshness using X-Amz-Date + amz_date = headers.get("X-Amz-Date") or headers.get("x-amz-date") + if not amz_date: + return JsonResponse({"error": "Missing X-Amz-Date header"}, status=400) + try: + from datetime import datetime, timezone as dt_timezone + + dt = datetime.strptime(amz_date.replace("Z", ""), "%Y%m%dT%H%M%S").replace( + tzinfo=dt_timezone.utc + ) + except Exception: + return JsonResponse({"error": "Invalid X-Amz-Date"}, status=400) + + # Resolve account and identity + service_account = resolve_service_account(account_id) + if service_account is None: + return JsonResponse({"error": "Service account not found"}, status=404) + if not service_account.server_wrapped_keyring: + return JsonResponse( + {"error": "Server-side key management must be enabled"}, status=403 + ) + + identity = resolve_attached_identity(service_account, "aws_iam") + if not identity: + return JsonResponse( + {"error": "No AWS IAM identity attached to this account"}, status=404 + ) + + try: + max_skew = int(identity.config.get("signatureTtlSeconds", 60)) + except Exception: + max_skew = 60 + now = timezone.now() + if abs((now - dt).total_seconds()) > max_skew: + return JsonResponse({"error": "Signature expired"}, status=401) + + # Enforce that the signed request targets the identity's configured STS endpoint + try: + configured = identity.config.get("stsEndpoint") + if not configured.startswith("http"): + configured = f"https://{configured}" + request_url = url + if not request_url.startswith("http"): + request_url = f"https://{request_url}" + + cfg_host = urlparse(configured).netloc.lower() + req_host = urlparse(request_url).netloc.lower() + header_host = (headers.get("Host") or headers.get("host") or "").lower() + + if req_host != cfg_host or (header_host and header_host != cfg_host): + return JsonResponse( + { + "error": "STS endpoint mismatch. Please sign the request for the configured STS endpoint.", + "expectedEndpoint": configured, + "receivedEndpoint": request_url, + }, + status=400, + ) + except Exception: + return JsonResponse({"error": "Invalid STS endpoint configuration"}, status=400) + + # Forward the signed request to AWS STS + try: + resp = requests.request( + method, configured, headers=headers, data=body, timeout=10 + ) + except requests.exceptions.Timeout: + return JsonResponse({"error": "AWS STS request timed out"}, status=504) + except requests.exceptions.ConnectionError: + return JsonResponse({"error": "Unable to connect to AWS STS"}, status=502) + except Exception: + return JsonResponse({"error": "Failed to contact AWS STS"}, status=502) + + if resp.status_code != 200: + return JsonResponse( + {"error": "AWS STS validation failed", "status": resp.status_code}, + status=401, + ) + + arn = None + try: + xml_root = parse(StringIO(resp.text)) + + # AWS STS namespace + ns = {"aws": "https://sts.amazonaws.com/doc/2011-06-15/"} + + # Find the ARN element in the GetCallerIdentityResult + arn_element = xml_root.find(".//aws:GetCallerIdentityResult/aws:Arn", ns) + if arn_element is not None and arn_element.text: + arn = arn_element.text.strip() + except Exception: + pass + + if arn is None: + return JsonResponse({"error": "Unable to parse AWS identity"}, status=500) + + # Trust check + trusted = identity.get_trusted_list() + if any(p == "*" for p in trusted): + return JsonResponse( + {"error": "Invalid trusted principal pattern '*' is not allowed"}, + status=400, + ) + + def arn_matches_patterns(value: str, patterns: list[str]) -> bool: + for pattern in patterns: + if not pattern or pattern == "*": + continue + if fnmatch.fnmatch(value, pattern): + return True + return False + + if not arn_matches_patterns(arn, trusted): + return JsonResponse({"error": "Untrusted principal"}, status=403) + + # Validate requested TTL + requested_ttl = int(token_req.get("ttl") or identity.default_ttl_seconds) + max_ttl = identity.max_ttl_seconds + + if requested_ttl > max_ttl: + return JsonResponse( + { + "error": f"Requested TTL ({requested_ttl}s) exceeds maximum allowed TTL ({max_ttl}s)" + }, + status=400, + ) + + try: + auth = mint_service_account_token( + service_account, + identity, + requested_ttl, + token_name_fallback="aws-iam", + ) + except Exception: + return JsonResponse({"error": "Failed to mint token"}, status=500) + + return JsonResponse({"authentication": auth}) diff --git a/backend/backend/graphene/mutations/access.py b/backend/backend/graphene/mutations/access.py index ada6f4ee5..62176e60c 100644 --- a/backend/backend/graphene/mutations/access.py +++ b/backend/backend/graphene/mutations/access.py @@ -6,7 +6,9 @@ ServiceAccount, ) from api.utils.access.permissions import user_has_permission -from backend.graphene.types import NetworkAccessPolicyType, RoleType +from backend.graphene.types import NetworkAccessPolicyType, RoleType, IdentityType +from api.models import Identity +from django.utils import timezone import graphene from graphql import GraphQLError @@ -221,6 +223,178 @@ def mutate(cls, root, info, id): return DeleteNetworkAccessPolicyMutation(ok=True) +class CreateIdentityMutation(graphene.Mutation): + class Arguments: + organisation_id = graphene.ID(required=True) + provider = graphene.String(required=True) + name = graphene.String(required=True) + description = graphene.String(required=False) + trusted_principals = graphene.String(required=True) + signature_ttl_seconds = graphene.Int(required=False) + sts_endpoint = graphene.String(required=False) + token_name_pattern = graphene.String(required=False) + default_ttl_seconds = graphene.Int(required=True) + max_ttl_seconds = graphene.Int(required=True) + + identity = graphene.Field(IdentityType) + + @classmethod + def mutate( + cls, + root, + info, + organisation_id, + provider, + name, + trusted_principals, + default_ttl_seconds, + max_ttl_seconds, + description=None, + signature_ttl_seconds=60, + sts_endpoint="https://sts.amazonaws.com", + token_name_pattern=None, + ): + user = info.context.user + org = Organisation.objects.get(id=organisation_id) + + if not user_has_permission(user, "create", "ExternalIdentities", org): + raise GraphQLError( + "You don't have the permissions required to create identities in this organisation" + ) + + if default_ttl_seconds is not None and max_ttl_seconds is not None: + if int(default_ttl_seconds) > int(max_ttl_seconds): + raise GraphQLError( + "Default token expiry must be less than or equal to Maximum token expiry" + ) + + # Store provider-specific configuration in a generic config field + # Convert comma-separated trusted_principals to list for consistency + trusted_list = [p.strip() for p in trusted_principals.split(",") if p.strip()] + config = { + "trustedPrincipals": trusted_list, + "signatureTtlSeconds": signature_ttl_seconds, + "stsEndpoint": sts_endpoint, + } + + identity = Identity.objects.create( + organisation=org, + provider=provider, + name=name, + description=description, + config=config, + token_name_pattern=token_name_pattern, + default_ttl_seconds=default_ttl_seconds, + max_ttl_seconds=max_ttl_seconds, + ) + + return CreateIdentityMutation(identity=identity) + + +class UpdateIdentityMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + name = graphene.String(required=False) + description = graphene.String(required=False) + trusted_principals = graphene.String(required=False) + signature_ttl_seconds = graphene.Int(required=False) + sts_endpoint = graphene.String(required=False) + token_name_pattern = graphene.String(required=False) + default_ttl_seconds = graphene.Int(required=False) + max_ttl_seconds = graphene.Int(required=False) + + identity = graphene.Field(IdentityType) + + @classmethod + def mutate( + cls, + root, + info, + id, + name=None, + description=None, + trusted_principals=None, + signature_ttl_seconds=None, + sts_endpoint=None, + token_name_pattern=None, + default_ttl_seconds=None, + max_ttl_seconds=None, + ): + user = info.context.user + identity = Identity.objects.get(id=id, deleted_at=None) + + org = identity.organisation + if not user_has_permission(user, "update", "ExternalIdentities", org): + raise GraphQLError( + "You don't have the permissions required to update identities in this organisation" + ) + + # Update basic fields using dictionary unpacking + basic_updates = { + k: v + for k, v in { + "name": name, + "description": description, + "token_name_pattern": token_name_pattern, + "default_ttl_seconds": default_ttl_seconds, + "max_ttl_seconds": max_ttl_seconds, + }.items() + if v is not None + } + for field, value in basic_updates.items(): + setattr(identity, field, value) + + # Update provider-specific config atomically + config_updates = {} + if trusted_principals is not None: + config_updates["trustedPrincipals"] = [ + p.strip() for p in trusted_principals.split(",") if p.strip() + ] + if signature_ttl_seconds is not None: + config_updates["signatureTtlSeconds"] = signature_ttl_seconds + if sts_endpoint is not None: + config_updates["stsEndpoint"] = sts_endpoint + + if config_updates: + identity.config = {**(identity.config or {}), **config_updates} + + if ( + identity.default_ttl_seconds is not None + and identity.max_ttl_seconds is not None + ): + if int(identity.default_ttl_seconds) > int(identity.max_ttl_seconds): + raise GraphQLError( + "Default token expiry must be less than or equal to Maximum token expiry" + ) + + identity.save() + + return UpdateIdentityMutation(identity=identity) + + +class DeleteIdentityMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + + ok = graphene.Boolean() + + @classmethod + def mutate(cls, root, info, id): + user = info.context.user + identity = Identity.objects.get(id=id, deleted_at=None) + + org = identity.organisation + if not user_has_permission(user, "delete", "ExternalIdentities", org): + raise GraphQLError( + "You don't have the permissions required to delete identities in this organisation" + ) + + identity.deleted_at = timezone.now() + identity.save() + + return DeleteIdentityMutation(ok=True) + + class AccountTypeEnum(graphene.Enum): USER = "user" SERVICE = "service" diff --git a/backend/backend/graphene/mutations/service_accounts.py b/backend/backend/graphene/mutations/service_accounts.py index b8768e955..2b99c476b 100644 --- a/backend/backend/graphene/mutations/service_accounts.py +++ b/backend/backend/graphene/mutations/service_accounts.py @@ -7,6 +7,7 @@ ServiceAccount, ServiceAccountHandler, ServiceAccountToken, + Identity, ) from api.utils.access.permissions import user_has_permission, user_is_org_member from backend.graphene.types import ServiceAccountTokenType, ServiceAccountType @@ -82,7 +83,7 @@ def mutate( return CreateServiceAccountMutation(service_account=service_account) -class EnableServiceAccountThirdPartyAuthMutation(graphene.Mutation): +class EnableServiceAccountServerSideKeyManagementMutation(graphene.Mutation): class Arguments: service_account_id = graphene.ID() server_wrapped_keyring = graphene.String() @@ -113,7 +114,35 @@ def mutate( service_account.server_wrapped_recovery = server_wrapped_recovery service_account.save() - return EnableServiceAccountThirdPartyAuthMutation( + return EnableServiceAccountServerSideKeyManagementMutation( + service_account=service_account + ) + + +class EnableServiceAccountClientSideKeyManagementMutation(graphene.Mutation): + class Arguments: + service_account_id = graphene.ID() + + service_account = graphene.Field(ServiceAccountType) + + @classmethod + def mutate(cls, root, info, service_account_id): + user = info.context.user + service_account = ServiceAccount.objects.get(id=service_account_id) + + if not user_has_permission( + user, "update", "ServiceAccounts", service_account.organisation + ): + raise GraphQLError( + "You don't have the permissions required to update Service Accounts in this organisation" + ) + + # Delete server-wrapped keys to disable server-side key management + service_account.server_wrapped_keyring = None + service_account.server_wrapped_recovery = None + service_account.save() + + return EnableServiceAccountClientSideKeyManagementMutation( service_account=service_account ) @@ -123,11 +152,12 @@ class Arguments: service_account_id = graphene.ID() name = graphene.String() role_id = graphene.ID() + identity_ids = graphene.List(graphene.NonNull(graphene.ID), required=False) service_account = graphene.Field(ServiceAccountType) @classmethod - def mutate(cls, root, info, service_account_id, name, role_id): + def mutate(cls, root, info, service_account_id, name, role_id, identity_ids=None): user = info.context.user service_account = ServiceAccount.objects.get(id=service_account_id) @@ -141,6 +171,13 @@ def mutate(cls, root, info, service_account_id, name, role_id): role = Role.objects.get(id=role_id) service_account.name = name service_account.role = role + if identity_ids is not None: + identities = Identity.objects.filter( + id__in=identity_ids, + organisation=service_account.organisation, + deleted_at=None, + ) + service_account.identities.set(identities) service_account.save() return UpdateServiceAccountMutation(service_account=service_account) diff --git a/backend/backend/graphene/queries/access.py b/backend/backend/graphene/queries/access.py index d7ba3740d..f553bee3a 100644 --- a/backend/backend/graphene/queries/access.py +++ b/backend/backend/graphene/queries/access.py @@ -1,5 +1,11 @@ from api.utils.access.permissions import user_has_permission, user_is_org_member -from api.models import NetworkAccessPolicy, Organisation, OrganisationMember, Role +from api.models import ( + NetworkAccessPolicy, + Organisation, + OrganisationMember, + Role, + Identity, +) from graphql import GraphQLError from django.db import transaction from api.utils.access.roles import default_roles @@ -97,3 +103,20 @@ def resolve_client_ip(root, info): else: ip = request.META.get("REMOTE_ADDR") return ip + + +def resolve_identities(root, info, organisation_id): + if not user_is_org_member(info.context.user.userId, organisation_id): + raise GraphQLError("You don't have access to this organisation") + + if user_has_permission( + info.context.user.userId, + "read", + "ExternalIdentities", + Organisation.objects.get(id=organisation_id), + ): + return Identity.objects.filter(organisation_id=organisation_id, deleted_at=None) + else: + raise GraphQLError( + "You don't have permission to read identities in this Organisation" + ) diff --git a/backend/backend/graphene/queries/identity.py b/backend/backend/graphene/queries/identity.py new file mode 100644 index 000000000..20490f36b --- /dev/null +++ b/backend/backend/graphene/queries/identity.py @@ -0,0 +1,20 @@ +from graphql import GraphQLError +from api.identity_providers import IdentityProviders +from backend.graphene.types import IdentityProviderType + + +def resolve_aws_sts_endpoints(root, info): + """Return STS endpoints dynamically using botocore's endpoint resolver data.""" + try: + from api.utils.identity.aws import list_sts_endpoints + return list_sts_endpoints() + except Exception as ex: + raise GraphQLError(f"Failed to load AWS STS endpoints: {ex}") + + +def resolve_identity_providers(root, info): + """Get all supported identity providers.""" + return [ + IdentityProviderType(**provider) + for provider in IdentityProviders.get_supported_providers() + ] diff --git a/backend/backend/graphene/types.py b/backend/backend/graphene/types.py index 9482d166c..61cfc8ef1 100644 --- a/backend/backend/graphene/types.py +++ b/backend/backend/graphene/types.py @@ -33,12 +33,12 @@ SecretEvent, SecretFolder, SecretTag, - ServerEnvironmentKey, ServiceAccount, ServiceAccountHandler, ServiceAccountToken, ServiceToken, UserToken, + Identity, ) from logs.dynamodb_models import KMSLog from django.utils import timezone @@ -330,6 +330,14 @@ class ProviderType(graphene.ObjectType): auth_scheme = graphene.String() +class IdentityProviderType(graphene.ObjectType): + id = graphene.String(required=True) + name = graphene.String(required=True) + description = graphene.String(required=True) + icon_id = graphene.String(required=True) + supported = graphene.Boolean(required=True) + + class ServiceType(ObjectType): id = graphene.String() name = graphene.String() @@ -692,11 +700,12 @@ def resolve_environments(self, info): class ServiceAccountType(DjangoObjectType): - third_party_auth_enabled = graphene.Boolean() + server_side_key_management_enabled = graphene.Boolean() handlers = graphene.List(ServiceAccountHandlerType) tokens = graphene.List(ServiceAccountTokenType) app_memberships = graphene.List(graphene.NonNull(AppMembershipType)) network_policies = graphene.List(graphene.NonNull(lambda: NetworkAccessPolicyType)) + identities = graphene.List(graphene.NonNull(lambda: IdentityType)) class Meta: model = ServiceAccount @@ -710,7 +719,7 @@ class Meta: "deleted_at", ) - def resolve_third_party_auth_enabled(self, info): + def resolve_server_side_key_management_enabled(self, info): return ( self.server_wrapped_keyring is not None and self.server_wrapped_recovery is not None @@ -756,6 +765,9 @@ def resolve_network_policies(self, info): return list(chain(account_policies, global_policies)) + def resolve_identities(self, info): + return self.identities.filter(deleted_at=None) + class EnvironmentKeyType(DjangoObjectType): class Meta: @@ -1010,3 +1022,40 @@ class AWSValidationResultType(graphene.ObjectType): method = graphene.String() error = graphene.String() assumed_role_arn = graphene.String() + + +class AwsIamConfigType(graphene.ObjectType): + trusted_principals = graphene.List(graphene.String) + signature_ttl_seconds = graphene.Int() + sts_endpoint = graphene.String() + + +class IdentityConfigUnion(graphene.Union): + class Meta: + types = (AwsIamConfigType,) + + +class IdentityType(DjangoObjectType): + config = graphene.Field(IdentityConfigUnion) + + class Meta: + model = Identity + fields = "__all__" + + def resolve_config(self, info): + """Map provider-specific config into typed objects""" + provider = (self.provider or '').lower() + cfg = self.config or {} + + if provider == 'aws_iam': + try: + ttl = int(cfg.get('signatureTtlSeconds', 60)) + except Exception: + ttl = 60 + return AwsIamConfigType( + trusted_principals=self.get_trusted_list(), + signature_ttl_seconds=ttl, + sts_endpoint=cfg.get('stsEndpoint'), + ) + + return None diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 1496c7eef..e5d0c8457 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -28,7 +28,8 @@ CreateServiceAccountTokenMutation, DeleteServiceAccountMutation, DeleteServiceAccountTokenMutation, - EnableServiceAccountThirdPartyAuthMutation, + EnableServiceAccountClientSideKeyManagementMutation, + EnableServiceAccountServerSideKeyManagementMutation, UpdateServiceAccountHandlersMutation, UpdateServiceAccountMutation, ) @@ -42,6 +43,9 @@ CreateNetworkAccessPolicyMutation, DeleteCustomRoleMutation, DeleteNetworkAccessPolicyMutation, + CreateIdentityMutation, + UpdateIdentityMutation, + DeleteIdentityMutation, UpdateAccountNetworkAccessPolicies, UpdateCustomRoleMutation, UpdateNetworkAccessPolicyMutation, @@ -85,11 +89,13 @@ resolve_validate_aws_assume_role_auth, resolve_validate_aws_assume_role_credentials, ) +from .graphene.queries.identity import resolve_aws_sts_endpoints, resolve_identity_providers from .graphene.queries.access import ( resolve_roles, resolve_organisation_global_access_users, resolve_network_access_policies, resolve_client_ip, + resolve_identities, ) from .graphene.queries.service_accounts import ( resolve_service_accounts, @@ -183,6 +189,7 @@ PhaseLicenseType, ProviderCredentialsType, ProviderType, + IdentityProviderType, RoleType, SecretEventType, SecretFolderType, @@ -196,6 +203,7 @@ TimeRange, UserTokenType, AWSValidationResultType, + IdentityType, ) import graphene from graphql import GraphQLError @@ -235,6 +243,7 @@ class Query(graphene.ObjectType): network_access_policies = graphene.List( NetworkAccessPolicyType, organisation_id=graphene.ID() ) + identities = graphene.List(IdentityType, organisation_id=graphene.ID()) organisation_name_available = graphene.Boolean(name=graphene.String()) @@ -342,6 +351,8 @@ class Query(graphene.ObjectType): providers = graphene.List(ProviderType) services = graphene.List(ServiceType) + aws_sts_endpoints = graphene.List(graphene.JSONString) + identity_providers = graphene.List(IdentityProviderType) saved_credentials = graphene.List(ProviderCredentialsType, org_id=graphene.ID()) @@ -481,6 +492,12 @@ def resolve_organisations(root, info): resolve_roles = resolve_roles resolve_network_access_policies = resolve_network_access_policies + # Identities + resolve_identities = resolve_identities + resolve_aws_sts_endpoints = resolve_aws_sts_endpoints + resolve_identity_providers = resolve_identity_providers + + resolve_organisation_plan = resolve_organisation_plan def resolve_organisation_name_available(root, info, name): @@ -963,10 +980,18 @@ class Mutation(graphene.ObjectType): delete_network_access_policy = DeleteNetworkAccessPolicyMutation.Field() update_account_network_access_policies = UpdateAccountNetworkAccessPolicies.Field() + # Identities + create_identity = CreateIdentityMutation.Field() + update_identity = UpdateIdentityMutation.Field() + delete_identity = DeleteIdentityMutation.Field() + # Service Accounts create_service_account = CreateServiceAccountMutation.Field() - enable_service_account_third_party_auth = ( - EnableServiceAccountThirdPartyAuthMutation.Field() + enable_service_account_server_side_key_management = ( + EnableServiceAccountServerSideKeyManagementMutation.Field() + ) + enable_service_account_client_side_key_management = ( + EnableServiceAccountClientSideKeyManagementMutation.Field() ) update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field() update_service_account = UpdateServiceAccountMutation.Field() diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 26f2c9087..ad0d5cf0e 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -12,38 +12,59 @@ secrets_tokens, root_endpoint, ) +from api.views.identities.aws.iam import aws_iam_auth from api.views.kms import kms CLOUD_HOSTED = settings.APP_HOST == "cloud" +# Core API and System URLs urlpatterns = [ - path("public/", root_endpoint), + # System health and info + path("health/", health_check), + path( + "493c5048-99f9-4eac-ad0d-98c3740b491f/health", health_check + ), # Legacy health check - TODO: Remove + # Authentication and user management path("accounts/", include("allauth.urls")), path("auth/", include("dj_rest_auth.urls")), path("social/login/", include("api.urls")), path("logout/", csrf_exempt(logout_view)), + # GraphQL API path("graphql/", csrf_exempt(PrivateGraphQLView.as_view(graphiql=True))), - path("health/", health_check), - # To not break legacy health checks. TODO: Remove - path("493c5048-99f9-4eac-ad0d-98c3740b491f/health", health_check), + # OAuth integrations + path("oauth/github/callback", github_integration_callback), + # Secrets management path("secrets/", E2EESecretsView.as_view()), - path("public/v1/secrets/", PublicSecretsView.as_view()), path("secrets/tokens/", secrets_tokens), - path("oauth/github/callback", github_integration_callback), + # Lockbox path("lockbox/", LockboxView.as_view()), +] + +# Public API URLs +public_urls = [ + path("public/", root_endpoint), + path("public/v1/secrets/", PublicSecretsView.as_view()), path( "public/v1/secrets/dynamic/", include("ee.integrations.secrets.dynamic.rest.urls"), ), + path("public/identities/external/v1/aws/iam/auth/", aws_iam_auth), ] +# Add public URLs to main urlpatterns +urlpatterns.extend(public_urls) + +# Cloud-hosted specific URLs if CLOUD_HOSTED: from ee.billing.webhooks.stripe import stripe_webhook - urlpatterns.append(path("kms/", kms)) - urlpatterns.append(path("stripe/webhook/", stripe_webhook, name="stripe-webhook")) - + cloud_urls = [ + path("kms/", kms), + path("stripe/webhook/", stripe_webhook, name="stripe-webhook"), + ] + urlpatterns.extend(cloud_urls) +# Admin interface (if enabled) try: if settings.ADMIN_ENABLED: urlpatterns.append(path("admin/", admin.site.urls)) diff --git a/backend/tests/api/views/identities/aws/__init__.py b/backend/tests/api/views/identities/aws/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/tests/api/views/identities/aws/test_iam_unit.py b/backend/tests/api/views/identities/aws/test_iam_unit.py new file mode 100644 index 000000000..e94c35c04 --- /dev/null +++ b/backend/tests/api/views/identities/aws/test_iam_unit.py @@ -0,0 +1,685 @@ +import unittest +import json +import base64 +import re +from datetime import datetime, timezone +from unittest.mock import MagicMock, Mock + + +class MockRequest: + """Mock request object for testing""" + def __init__(self, body): + self.body = body + + +class MockJsonResponse: + """Mock JsonResponse for testing""" + def __init__(self, data, status=200): + self.status_code = status + self.content = json.dumps(data) + self.data = data + + +def aws_iam_auth_logic(request_body, resolve_service_account_func, resolve_attached_identity_func, + mint_service_account_token_func, requests_func, timezone_func): + """ + Simulates the AWS IAM authentication. + """ + try: + payload = json.loads(request_body) + except Exception: + return MockJsonResponse({"error": "Invalid JSON"}, status=400) + + account = (payload or {}).get("account", {}) + aws_iam = (payload or {}).get("awsIam", {}) + token_req = (payload or {}).get("tokenRequest", {}) + + account_type = (account.get("type") or "service").lower() + account_id = account.get("id") + if account_type != "service" or not account_id: + return MockJsonResponse({"error": "Only service account authentication supported"}, status=400) + + # Decode signed request + try: + method = aws_iam.get("httpRequestMethod") or "POST" + url = base64.b64decode(aws_iam["httpRequestUrl"]).decode("utf-8") + headers_json = base64.b64decode(aws_iam["httpRequestHeaders"]).decode("utf-8") + body = base64.b64decode(aws_iam.get("httpRequestBody") or b"").decode("utf-8") + headers = json.loads(headers_json) + except Exception: + return MockJsonResponse({"error": "Invalid awsIam request encoding"}, status=400) + + # Verify signature freshness using X-Amz-Date + amz_date = headers.get("X-Amz-Date") or headers.get("x-amz-date") + if not amz_date: + return MockJsonResponse({"error": "Missing X-Amz-Date header"}, status=400) + + try: + dt = ( + datetime.strptime(amz_date.replace("Z", ""), "%Y%m%dT%H%M%S") + .replace(tzinfo=timezone.utc) + ) + except Exception: + return MockJsonResponse({"error": "Invalid X-Amz-Date"}, status=400) + + # Resolve account and identity + service_account = resolve_service_account_func(account_id) + if service_account is None: + return MockJsonResponse({"error": "Service account not found"}, status=404) + if not service_account.server_wrapped_keyring: + return MockJsonResponse({"error": "Server-side key management must be enabled"}, status=403) + + identity = resolve_attached_identity_func(service_account, "aws_iam") + if not identity: + return MockJsonResponse({"error": "No AWS IAM identity attached to this account"}, status=404) + + try: + max_skew = int(identity.config.get("signatureTtlSeconds", 60)) + except Exception: + max_skew = 60 + + now = timezone_func() + if abs((now - dt).total_seconds()) > max_skew: + return MockJsonResponse({"error": "Signature expired"}, status=401) + + # Enforce that the signed request targets the identity's configured STS endpoint + try: + from urllib.parse import urlparse + configured = identity.config.get("stsEndpoint") + if not configured.startswith("http"): + configured = f"https://{configured}" + request_url = url + if not request_url.startswith("http"): + request_url = f"https://{request_url}" + + cfg_host = urlparse(configured).netloc.lower() + req_host = urlparse(request_url).netloc.lower() + header_host = (headers.get("Host") or headers.get("host") or "").lower() + + if req_host != cfg_host or (header_host and header_host != cfg_host): + return MockJsonResponse( + { + "error": "STS endpoint mismatch. Please sign the request for the configured STS endpoint.", + "expectedEndpoint": configured, + "receivedEndpoint": request_url, + }, + status=400, + ) + except Exception: + return MockJsonResponse({"error": "Invalid STS endpoint configuration"}, status=400) + + # Forward the signed request to AWS STS + try: + resp = requests_func(method, url, headers=headers, data=body) + except Exception: + return MockJsonResponse({"error": "Failed to contact AWS STS"}, status=502) + + if resp.status_code != 200: + return MockJsonResponse({"error": "AWS STS validation failed", "status": resp.status_code}, status=401) + + # Parse minimal identity info from XML + arn = None + try: + m = re.search(r"([^<]+)", resp.text) + if m: + arn = m.group(1) + except Exception: + pass + if arn is None: + return MockJsonResponse({"error": "Unable to parse AWS identity"}, status=500) + + # Trust check + import fnmatch + trusted = identity.get_trusted_list() + if any(p == "*" for p in trusted): + return MockJsonResponse({"error": "Invalid trusted principal pattern '*' is not allowed"}, status=400) + + def arn_matches_patterns(value: str, patterns: list[str]) -> bool: + for pattern in patterns: + if not pattern or pattern == "*": + continue + if fnmatch.fnmatch(value, pattern): + return True + return False + + if not arn_matches_patterns(arn, trusted): + return MockJsonResponse({"error": "Untrusted principal"}, status=403) + + # Validate requested TTL + requested_ttl = int(token_req.get("ttl") or identity.default_ttl_seconds) + max_ttl = identity.max_ttl_seconds + + if requested_ttl > max_ttl: + return MockJsonResponse({ + "error": f"Requested TTL ({requested_ttl}s) exceeds maximum allowed TTL ({max_ttl}s)" + }, status=400) + + try: + auth = mint_service_account_token_func( + service_account, + identity, + requested_ttl, + token_name_fallback="aws-iam", + ) + except Exception: + return MockJsonResponse({"error": "Failed to mint token"}, status=500) + + return MockJsonResponse({"authentication": auth}) + + +class TestAwsIamAuthLogic(unittest.TestCase): + def setUp(self): + self.service_account_id = "12345678-1234-1234-1234-123456789abc" + + # Valid AWS STS response XML (anonymized) + self.valid_aws_response = """ + + arn:aws:iam::123456789012:user/test-user + AIDACKCEVSQ6C2EXAMPLE + 123456789012 + + + 01234567-89ab-cdef-0123-456789abcdef + +""" + + # Valid request payload (anonymized) + self.valid_payload = { + "account": { + "type": "service", + "id": self.service_account_id + }, + "awsIam": { + "httpRequestMethod": "POST", + "httpRequestUrl": "aHR0cHM6Ly9zdHMuYXAtc291dGgtMS5hbWF6b25hd3MuY29t", # https://sts.ap-south-1.amazonaws.com + "httpRequestHeaders": "eyJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PXV0Zi04IiwgIlgtQW16LURhdGUiOiAiMjAyNTA4MjdUMDgwNzE5WiIsICJBdXRob3JpemF0aW9uIjogIkFXUzQtSE1BQy1TSEEyNTYgQ3JlZGVudGlhbD1BS0lBRVhBTVBMRTEyMzQ1LzIwMjUwODI3L2FwLXNvdXRoLTEvc3RzL2F3czRfcmVxdWVzdCwgU2lnbmVkSGVhZGVycz1jb250ZW50LXR5cGU7aG9zdDt4LWFtei1kYXRlLCBTaWduYXR1cmU9ZXhhbXBsZXNpZ25hdHVyZTEyMzQ1Njc4OTBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiIsICJDb250ZW50LUxlbmd0aCI6ICI0MyJ9", + "httpRequestBody": "QWN0aW9uPUdldENhbGxlcklkZW50aXR5JlZlcnNpb249MjAxMS0wNi0xNQ==" # Action=GetCallerIdentity&Version=2011-06-15 + }, + "tokenRequest": { + "ttl": 60 + } + } + + # Decoded headers for verification (anonymized) + self.decoded_headers = { + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + "X-Amz-Date": "20250827T080719Z", + "Authorization": "AWS4-HMAC-SHA256 Credential=AKIAEXAMPLE12345/20250827/ap-south-1/sts/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=examplesignature1234567890abcdefghijklmnopqrstuvwxyz", + "Content-Length": "43" + } + + def test_invalid_json_payload(self): + """Test handling of malformed JSON payload""" + response = aws_iam_auth_logic( + b"invalid json", + None, None, None, None, None + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Invalid JSON", response.content) + + def test_missing_account_id(self): + """Test handling of missing service account ID""" + payload = self.valid_payload.copy() + payload["account"]["id"] = None + + response = aws_iam_auth_logic( + json.dumps(payload).encode(), + None, None, None, None, None + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Only service account authentication supported", response.content) + + def test_invalid_account_type(self): + """Test handling of invalid account type""" + payload = self.valid_payload.copy() + payload["account"]["type"] = "user" + + response = aws_iam_auth_logic( + json.dumps(payload).encode(), + None, None, None, None, None + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Only service account authentication supported", response.content) + + def test_invalid_base64_encoding(self): + """Test handling of invalid base64 encoding in AWS IAM data""" + payload = self.valid_payload.copy() + payload["awsIam"]["httpRequestUrl"] = "invalid_base64!!!" + + response = aws_iam_auth_logic( + json.dumps(payload).encode(), + None, None, None, None, None + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Invalid awsIam request encoding", response.content) + + def test_missing_amz_date_header(self): + """Test handling of missing X-Amz-Date header""" + headers_without_date = { + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + "Authorization": "AWS4-HMAC-SHA256 Credential=...", + "Content-Length": "43" + } + encoded_headers = base64.b64encode(json.dumps(headers_without_date).encode()).decode() + + payload = self.valid_payload.copy() + payload["awsIam"]["httpRequestHeaders"] = encoded_headers + + response = aws_iam_auth_logic( + json.dumps(payload).encode(), + None, None, None, None, None + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Missing X-Amz-Date header", response.content) + + def test_invalid_amz_date_format(self): + """Test handling of invalid X-Amz-Date format""" + headers_invalid_date = self.decoded_headers.copy() + headers_invalid_date["X-Amz-Date"] = "invalid-date-format" + encoded_headers = base64.b64encode(json.dumps(headers_invalid_date).encode()).decode() + + payload = self.valid_payload.copy() + payload["awsIam"]["httpRequestHeaders"] = encoded_headers + + response = aws_iam_auth_logic( + json.dumps(payload).encode(), + None, None, None, None, None + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Invalid X-Amz-Date", response.content) + + def test_service_account_not_found(self): + """Test handling of non-existent service account""" + def mock_resolve_service_account(account_id): + return None + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, None, None, None, None + ) + + self.assertEqual(response.status_code, 404) + self.assertIn("Service account not found", response.content) + + def test_server_side_key_management_required(self): + """Test requirement for server-side key management""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = None # No server-side keyring + return mock_service_account + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, None, None, None, None + ) + + self.assertEqual(response.status_code, 403) + self.assertIn("Server-side key management must be enabled", response.content) + + def test_no_aws_iam_identity_attached(self): + """Test handling of service account without AWS IAM identity""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + return None + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, None, None + ) + + self.assertEqual(response.status_code, 404) + self.assertIn("No AWS IAM identity attached to this account", response.content) + + def test_signature_expired(self): + """Test handling of expired signature""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = {"signatureTtlSeconds": 60} + return mock_identity + + def mock_timezone_now(): + # Mock current time to be way after X-Amz-Date (signature expired) + return datetime(2025, 8, 27, 10, 0, 0, tzinfo=timezone.utc) # 2 hours later + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, None, mock_timezone_now + ) + + self.assertEqual(response.status_code, 401) + self.assertIn("Signature expired", response.content) + + def test_sts_endpoint_mismatch(self): + """Test handling of STS endpoint mismatch""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.us-east-1.amazonaws.com" # Different from request + } + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, None, mock_timezone_now + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("STS endpoint mismatch", response.content) + + def test_aws_sts_request_failure(self): + """Test handling of AWS STS request failure""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + raise Exception("Connection failed") + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 502) + self.assertIn("Failed to contact AWS STS", response.content) + + def test_aws_sts_validation_failed(self): + """Test handling of AWS STS validation failure""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 403 + return mock_response + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 401) + self.assertIn("AWS STS validation failed", response.content) + + def test_unable_to_parse_aws_identity(self): + """Test handling of malformed AWS STS response""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = "Invalid" + return mock_response + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 500) + self.assertIn("Unable to parse AWS identity", response.content) + + def test_wildcard_trusted_principal_rejected(self): + """Test rejection of wildcard trusted principal patterns""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + mock_identity.get_trusted_list.return_value = ["*"] # Wildcard not allowed + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = self.valid_aws_response + return mock_response + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Invalid trusted principal pattern '*' is not allowed", response.content) + + def test_untrusted_principal(self): + """Test rejection of untrusted AWS principal""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + # Principal in response doesn't match trusted patterns + mock_identity.get_trusted_list.return_value = [ + "arn:aws:iam::111111111111:user/allowed-user" + ] + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = self.valid_aws_response + return mock_response + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 403) + self.assertIn("Untrusted principal", response.content) + + def test_ttl_exceeds_maximum(self): + """Test handling of TTL request exceeding maximum allowed""" + payload = self.valid_payload.copy() + payload["tokenRequest"]["ttl"] = 100000 # Exceeds max TTL + + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + mock_identity.default_ttl_seconds = 3600 + mock_identity.max_ttl_seconds = 86400 # 24 hours max + mock_identity.get_trusted_list.return_value = [ + "arn:aws:iam::123456789012:user/test-*" + ] + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = self.valid_aws_response + return mock_response + + response = aws_iam_auth_logic( + json.dumps(payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, None, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 400) + self.assertIn("Requested TTL", response.content) + self.assertIn("exceeds maximum allowed TTL", response.content) + + def test_token_minting_failure(self): + """Test handling of token minting failure""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + mock_identity.default_ttl_seconds = 3600 + mock_identity.max_ttl_seconds = 86400 + mock_identity.get_trusted_list.return_value = [ + "arn:aws:iam::123456789012:user/test-*" + ] + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = self.valid_aws_response + return mock_response + + def mock_mint_token(service_account, identity, requested_ttl, token_name_fallback): + raise Exception("Token mint failed") + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, mock_mint_token, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 500) + self.assertIn("Failed to mint token", response.content) + + def test_valid_authentication_success(self): + """Test successful AWS IAM authentication with valid request""" + def mock_resolve_service_account(account_id): + mock_service_account = MagicMock() + mock_service_account.server_wrapped_keyring = "encrypted_keyring_data" + return mock_service_account + + def mock_resolve_attached_identity(service_account, provider): + mock_identity = MagicMock() + mock_identity.config = { + "signatureTtlSeconds": 300, + "stsEndpoint": "https://sts.ap-south-1.amazonaws.com" + } + mock_identity.default_ttl_seconds = 3600 + mock_identity.max_ttl_seconds = 86400 + mock_identity.get_trusted_list.return_value = [ + "arn:aws:iam::123456789012:user/test-*" + ] + return mock_identity + + def mock_timezone_now(): + return datetime(2025, 8, 27, 8, 7, 30, tzinfo=timezone.utc) + + def mock_requests(method, url, headers=None, data=None): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = self.valid_aws_response + return mock_response + + def mock_mint_token(service_account, identity, requested_ttl, token_name_fallback): + return { + "tokenType": "ServiceAccount", + "token": "pss_service:v2:token123:pubkey:share:wrapkey", + "bearerToken": "ServiceAccount token123", + "TTL": 60, + "maxTTL": 86400 + } + + response = aws_iam_auth_logic( + json.dumps(self.valid_payload).encode(), + mock_resolve_service_account, mock_resolve_attached_identity, mock_mint_token, mock_requests, mock_timezone_now + ) + + self.assertEqual(response.status_code, 200) + response_data = json.loads(response.content) + self.assertIn("authentication", response_data) + self.assertEqual(response_data["authentication"]["tokenType"], "ServiceAccount") + + +if __name__ == '__main__': + unittest.main() diff --git a/backend/tests/utils/__init__.py b/backend/tests/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/tests/utils/syncing/__init__.py b/backend/tests/utils/syncing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/version.txt b/backend/version.txt index 6e380ebf9..804c37de6 100644 --- a/backend/version.txt +++ b/backend/version.txt @@ -1 +1 @@ -v2.52.0 +v2.53.0 diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index b4c16e884..abb28ea82 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -61,6 +61,9 @@ const documents = { "mutation UpdateDynamicSecret($dynamicSecretId: ID!, $organisationId: ID!, $path: String, $name: String!, $description: String, $defaultTtl: Int, $maxTtl: Int, $authenticationId: ID, $config: AWSConfigInput!, $keyMap: [KeyMapInput]!) {\n updateAwsDynamicSecret(\n organisationId: $organisationId\n dynamicSecretId: $dynamicSecretId\n path: $path\n name: $name\n description: $description\n defaultTtl: $defaultTtl\n maxTtl: $maxTtl\n authenticationId: $authenticationId\n config: $config\n keyMap: $keyMap\n ) {\n dynamicSecret {\n id\n name\n description\n provider\n createdAt\n updatedAt\n }\n }\n}": types.UpdateDynamicSecretDocument, "mutation CreateSharedSecret($input: LockboxInput!) {\n createLockbox(input: $input) {\n lockbox {\n id\n allowedViews\n expiresAt\n }\n }\n}": types.CreateSharedSecretDocument, "mutation SwapEnvOrder($environment1Id: ID!, $environment2Id: ID!) {\n swapEnvironmentOrder(\n environment1Id: $environment1Id\n environment2Id: $environment2Id\n ) {\n ok\n }\n}": types.SwapEnvOrderDocument, + "mutation CreateExtIdentity($organisationId: ID!, $provider: String!, $name: String!, $description: String, $trustedPrincipals: String!, $signatureTtlSeconds: Int, $stsEndpoint: String, $tokenNamePattern: String, $defaultTtlSeconds: Int!, $maxTtlSeconds: Int!) {\n createIdentity(\n organisationId: $organisationId\n provider: $provider\n name: $name\n description: $description\n trustedPrincipals: $trustedPrincipals\n signatureTtlSeconds: $signatureTtlSeconds\n stsEndpoint: $stsEndpoint\n tokenNamePattern: $tokenNamePattern\n defaultTtlSeconds: $defaultTtlSeconds\n maxTtlSeconds: $maxTtlSeconds\n ) {\n identity {\n id\n provider\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n }\n }\n}": types.CreateExtIdentityDocument, + "mutation DeleteExtIdentity($id: ID!) {\n deleteIdentity(id: $id) {\n ok\n }\n}": types.DeleteExtIdentityDocument, + "mutation UpdateExtIdentity($id: ID!, $name: String, $description: String, $trustedPrincipals: String, $signatureTtlSeconds: Int, $stsEndpoint: String, $tokenNamePattern: String, $defaultTtlSeconds: Int, $maxTtlSeconds: Int) {\n updateIdentity(\n id: $id\n name: $name\n description: $description\n trustedPrincipals: $trustedPrincipals\n signatureTtlSeconds: $signatureTtlSeconds\n stsEndpoint: $stsEndpoint\n tokenNamePattern: $tokenNamePattern\n defaultTtlSeconds: $defaultTtlSeconds\n maxTtlSeconds: $maxTtlSeconds\n ) {\n identity {\n id\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n }\n }\n}": types.UpdateExtIdentityDocument, "mutation AcceptOrganisationInvite($orgId: ID!, $identityKey: String!, $wrappedKeyring: String!, $wrappedRecovery: String!, $inviteId: ID!) {\n createOrganisationMember(\n orgId: $orgId\n identityKey: $identityKey\n wrappedKeyring: $wrappedKeyring\n wrappedRecovery: $wrappedRecovery\n inviteId: $inviteId\n ) {\n orgMember {\n id\n email\n createdAt\n role {\n name\n }\n }\n }\n}": types.AcceptOrganisationInviteDocument, "mutation BulkInviteMembers($orgId: ID!, $invites: [InviteInput!]!) {\n bulkInviteOrganisationMembers(orgId: $orgId, invites: $invites) {\n invites {\n id\n inviteeEmail\n expiresAt\n }\n }\n}": types.BulkInviteMembersDocument, "mutation DeleteOrgInvite($inviteId: ID!) {\n deleteInvitation(inviteId: $inviteId) {\n ok\n }\n}": types.DeleteOrgInviteDocument, @@ -72,8 +75,10 @@ const documents = { "mutation CreateSAToken($serviceAccountId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createServiceAccountToken(\n serviceAccountId: $serviceAccountId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n token {\n id\n }\n }\n}": types.CreateSaTokenDocument, "mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountOpDocument, "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}": types.DeleteServiceAccountTokenOpDocument, + "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n }\n }\n}": types.EnableSaClientKeyManagementDocument, + "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n serverSideKeyManagementEnabled\n }\n }\n}": types.EnableSaServerKeyManagementDocument, "mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}": types.UpdateServiceAccountHandlerKeysDocument, - "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n }\n }\n}": types.UpdateServiceAccountOpDocument, + "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!, $identityIds: [ID!]) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n identityIds: $identityIds\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n identities {\n id\n name\n }\n }\n }\n}": types.UpdateServiceAccountOpDocument, "mutation CreateNewAWSSecretsSync($envId: ID!, $path: String!, $credentialId: ID!, $secretName: String!, $kmsId: String) {\n createAwsSecretSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n secretName: $secretName\n kmsId: $kmsId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewAwsSecretsSyncDocument, "mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument, "mutation CreateNewCfWorkersSync($envId: ID!, $path: String!, $workerName: String!, $credentialId: ID!) {\n createCloudflareWorkersSync(\n envId: $envId\n path: $path\n workerName: $workerName\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfWorkersSyncDocument, @@ -108,6 +113,9 @@ const documents = { "query GetApps($organisationId: ID!, $appId: ID) {\n apps(organisationId: $organisationId, appId: $appId) {\n id\n name\n identityKey\n createdAt\n updatedAt\n sseEnabled\n members {\n id\n email\n fullName\n avatarUrl\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n envType\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\n }\n }\n}": types.GetAppsDocument, "query GetDashboard($organisationId: ID!) {\n apps(organisationId: $organisationId) {\n id\n name\n sseEnabled\n }\n userTokens(organisationId: $organisationId) {\n id\n }\n organisationInvites(orgId: $organisationId) {\n id\n }\n organisationMembers(organisationId: $organisationId, role: null) {\n id\n }\n savedCredentials(orgId: $organisationId) {\n id\n }\n syncs(orgId: $organisationId) {\n id\n }\n}": types.GetDashboardDocument, "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}": types.GetOrganisationsDocument, + "query GetAwsStsEndpoints {\n awsStsEndpoints\n}": types.GetAwsStsEndpointsDocument, + "query GetIdentityProviders {\n identityProviders {\n id\n name\n description\n iconId\n supported\n }\n}": types.GetIdentityProvidersDocument, + "query GetOrganisationIdentities($organisationId: ID!) {\n identities(organisationId: $organisationId) {\n id\n provider\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n createdAt\n }\n}": types.GetOrganisationIdentitiesDocument, "query CheckOrganisationNameAvailability($name: String!) {\n organisationNameAvailable(name: $name)\n}": types.CheckOrganisationNameAvailabilityDocument, "query GetGlobalAccessUsers($organisationId: ID!) {\n organisationGlobalAccessUsers(organisationId: $organisationId) {\n id\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetGlobalAccessUsersDocument, "query GetInvites($orgId: ID!) {\n organisationInvites(orgId: $orgId) {\n id\n createdAt\n expiresAt\n invitedBy {\n email\n fullName\n self\n }\n inviteeEmail\n role {\n id\n name\n description\n color\n }\n }\n}": types.GetInvitesDocument, @@ -132,9 +140,9 @@ const documents = { "query GetSecretTags($orgId: ID!) {\n secretTags(orgId: $orgId) {\n id\n name\n color\n }\n}": types.GetSecretTagsDocument, "query GetSecrets($appId: ID!, $envId: ID!, $path: String) {\n secrets(envId: $envId, path: $path) {\n id\n key\n value\n path\n tags {\n id\n name\n color\n }\n comment\n createdAt\n updatedAt\n override {\n value\n isActive\n }\n environment {\n id\n app {\n id\n }\n }\n }\n folders(envId: $envId, path: $path) {\n id\n name\n path\n createdAt\n folderCount\n secretCount\n }\n appEnvironments(appId: $appId, environmentId: $envId) {\n id\n name\n envType\n identityKey\n app {\n id\n name\n sseEnabled\n }\n }\n environmentKeys(appId: $appId, environmentId: $envId) {\n id\n identityKey\n wrappedSeed\n wrappedSalt\n }\n envSyncs(envId: $envId) {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n options\n isActive\n status\n lastSync\n createdAt\n }\n dynamicSecrets(envId: $envId, path: $path) {\n id\n name\n path\n description\n provider\n keyMap {\n id\n keyName\n masked\n }\n config {\n ... on AWSConfigType {\n usernameTemplate\n groups\n iamPath\n permissionBoundaryArn\n policyArns\n policyDocument\n }\n }\n defaultTtlSeconds\n maxTtlSeconds\n authentication {\n id\n name\n }\n createdAt\n }\n}": types.GetSecretsDocument, "query GetServiceTokens($appId: ID!) {\n serviceTokens(appId: $appId) {\n id\n name\n createdAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n expiresAt\n keys {\n id\n identityKey\n }\n }\n}": types.GetServiceTokensDocument, - "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}": types.GetServiceAccountDetailDocument, + "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n identities {\n id\n name\n description\n }\n }\n}": types.GetServiceAccountDetailDocument, "query GetServiceAccountHandlers($orgId: ID!) {\n serviceAccountHandlers(orgId: $orgId) {\n id\n email\n role {\n name\n permissions\n }\n identityKey\n self\n }\n}": types.GetServiceAccountHandlersDocument, - "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n }\n}": types.GetServiceAccountTokensDocument, + "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}": types.GetServiceAccountTokensDocument, "query GetServiceAccounts($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n }\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n createdAt\n }\n}": types.GetServiceAccountsDocument, "query GetOrganisationSyncs($orgId: ID!) {\n syncs(orgId: $orgId) {\n id\n environment {\n id\n name\n envType\n app {\n id\n name\n }\n }\n path\n serviceInfo {\n id\n name\n provider {\n id\n }\n }\n options\n isActive\n lastSync\n status\n authentication {\n id\n name\n credentials\n }\n createdAt\n history {\n id\n status\n createdAt\n completedAt\n meta\n }\n }\n savedCredentials(orgId: $orgId) {\n id\n name\n credentials\n createdAt\n provider {\n id\n name\n expectedCredentials\n optionalCredentials\n }\n syncCount\n }\n apps(organisationId: $orgId, appId: null) {\n id\n name\n identityKey\n createdAt\n sseEnabled\n members {\n id\n fullName\n avatarUrl\n email\n }\n serviceAccounts {\n id\n name\n }\n environments {\n id\n name\n syncs {\n id\n serviceInfo {\n id\n name\n provider {\n id\n name\n }\n }\n status\n }\n }\n }\n}": types.GetOrganisationSyncsDocument, "query GetAwsSecrets($credentialId: ID!) {\n awsSecrets(credentialId: $credentialId) {\n name\n arn\n }\n}": types.GetAwsSecretsDocument, @@ -364,6 +372,18 @@ export function graphql(source: "mutation CreateSharedSecret($input: LockboxInpu * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation SwapEnvOrder($environment1Id: ID!, $environment2Id: ID!) {\n swapEnvironmentOrder(\n environment1Id: $environment1Id\n environment2Id: $environment2Id\n ) {\n ok\n }\n}"): (typeof documents)["mutation SwapEnvOrder($environment1Id: ID!, $environment2Id: ID!) {\n swapEnvironmentOrder(\n environment1Id: $environment1Id\n environment2Id: $environment2Id\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 CreateExtIdentity($organisationId: ID!, $provider: String!, $name: String!, $description: String, $trustedPrincipals: String!, $signatureTtlSeconds: Int, $stsEndpoint: String, $tokenNamePattern: String, $defaultTtlSeconds: Int!, $maxTtlSeconds: Int!) {\n createIdentity(\n organisationId: $organisationId\n provider: $provider\n name: $name\n description: $description\n trustedPrincipals: $trustedPrincipals\n signatureTtlSeconds: $signatureTtlSeconds\n stsEndpoint: $stsEndpoint\n tokenNamePattern: $tokenNamePattern\n defaultTtlSeconds: $defaultTtlSeconds\n maxTtlSeconds: $maxTtlSeconds\n ) {\n identity {\n id\n provider\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n }\n }\n}"): (typeof documents)["mutation CreateExtIdentity($organisationId: ID!, $provider: String!, $name: String!, $description: String, $trustedPrincipals: String!, $signatureTtlSeconds: Int, $stsEndpoint: String, $tokenNamePattern: String, $defaultTtlSeconds: Int!, $maxTtlSeconds: Int!) {\n createIdentity(\n organisationId: $organisationId\n provider: $provider\n name: $name\n description: $description\n trustedPrincipals: $trustedPrincipals\n signatureTtlSeconds: $signatureTtlSeconds\n stsEndpoint: $stsEndpoint\n tokenNamePattern: $tokenNamePattern\n defaultTtlSeconds: $defaultTtlSeconds\n maxTtlSeconds: $maxTtlSeconds\n ) {\n identity {\n id\n provider\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\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 DeleteExtIdentity($id: ID!) {\n deleteIdentity(id: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteExtIdentity($id: ID!) {\n deleteIdentity(id: $id) {\n ok\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation UpdateExtIdentity($id: ID!, $name: String, $description: String, $trustedPrincipals: String, $signatureTtlSeconds: Int, $stsEndpoint: String, $tokenNamePattern: String, $defaultTtlSeconds: Int, $maxTtlSeconds: Int) {\n updateIdentity(\n id: $id\n name: $name\n description: $description\n trustedPrincipals: $trustedPrincipals\n signatureTtlSeconds: $signatureTtlSeconds\n stsEndpoint: $stsEndpoint\n tokenNamePattern: $tokenNamePattern\n defaultTtlSeconds: $defaultTtlSeconds\n maxTtlSeconds: $maxTtlSeconds\n ) {\n identity {\n id\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n }\n }\n}"): (typeof documents)["mutation UpdateExtIdentity($id: ID!, $name: String, $description: String, $trustedPrincipals: String, $signatureTtlSeconds: Int, $stsEndpoint: String, $tokenNamePattern: String, $defaultTtlSeconds: Int, $maxTtlSeconds: Int) {\n updateIdentity(\n id: $id\n name: $name\n description: $description\n trustedPrincipals: $trustedPrincipals\n signatureTtlSeconds: $signatureTtlSeconds\n stsEndpoint: $stsEndpoint\n tokenNamePattern: $tokenNamePattern\n defaultTtlSeconds: $defaultTtlSeconds\n maxTtlSeconds: $maxTtlSeconds\n ) {\n identity {\n id\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -408,6 +428,14 @@ export function graphql(source: "mutation DeleteServiceAccountOp($id: ID!) {\n * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAClientKeyManagement($serviceAccountId: ID!) {\n enableServiceAccountClientSideKeyManagement(serviceAccountId: $serviceAccountId) {\n serviceAccount {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n }\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n serverSideKeyManagementEnabled\n }\n }\n}"): (typeof documents)["mutation EnableSAServerKeyManagement($serviceAccountId: ID!, $serverWrappedKeyring: String!, $serverWrappedRecovery: String!) {\n enableServiceAccountServerSideKeyManagement(\n serviceAccountId: $serviceAccountId\n serverWrappedKeyring: $serverWrappedKeyring\n serverWrappedRecovery: $serverWrappedRecovery\n ) {\n serviceAccount {\n id\n name\n serverSideKeyManagementEnabled\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -415,7 +443,7 @@ export function graphql(source: "mutation UpdateServiceAccountHandlerKeys($orgId /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n }\n }\n}"): (typeof documents)["mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n }\n }\n}"]; +export function graphql(source: "mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!, $identityIds: [ID!]) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n identityIds: $identityIds\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n identities {\n id\n name\n }\n }\n }\n}"): (typeof documents)["mutation UpdateServiceAccountOp($serviceAccountId: ID!, $name: String!, $roleId: ID!, $identityIds: [ID!]) {\n updateServiceAccount(\n serviceAccountId: $serviceAccountId\n name: $name\n roleId: $roleId\n identityIds: $identityIds\n ) {\n serviceAccount {\n id\n name\n role {\n id\n name\n description\n permissions\n }\n identities {\n id\n name\n }\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -552,6 +580,18 @@ export function graphql(source: "query GetDashboard($organisationId: ID!) {\n a * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"): (typeof documents)["query GetOrganisations {\n organisations {\n id\n name\n identityKey\n createdAt\n plan\n planDetail {\n name\n maxUsers\n maxApps\n maxEnvsPerApp\n seatsUsed {\n users\n serviceAccounts\n total\n }\n appCount\n }\n role {\n name\n description\n color\n permissions\n }\n memberId\n keyring\n recovery\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query GetAwsStsEndpoints {\n awsStsEndpoints\n}"): (typeof documents)["query GetAwsStsEndpoints {\n awsStsEndpoints\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 GetIdentityProviders {\n identityProviders {\n id\n name\n description\n iconId\n supported\n }\n}"): (typeof documents)["query GetIdentityProviders {\n identityProviders {\n id\n name\n description\n iconId\n supported\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 GetOrganisationIdentities($organisationId: ID!) {\n identities(organisationId: $organisationId) {\n id\n provider\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n createdAt\n }\n}"): (typeof documents)["query GetOrganisationIdentities($organisationId: ID!) {\n identities(organisationId: $organisationId) {\n id\n provider\n name\n description\n config {\n ... on AwsIamConfigType {\n trustedPrincipals\n signatureTtlSeconds\n stsEndpoint\n }\n }\n tokenNamePattern\n defaultTtlSeconds\n maxTtlSeconds\n createdAt\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -651,7 +691,7 @@ export function graphql(source: "query GetServiceTokens($appId: ID!) {\n servic /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"): (typeof documents)["query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n }\n}"]; +export function graphql(source: "query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n identities {\n id\n name\n description\n }\n }\n}"): (typeof documents)["query GetServiceAccountDetail($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n name\n identityKey\n serverSideKeyManagementEnabled\n role {\n id\n name\n description\n color\n permissions\n }\n createdAt\n handlers {\n id\n wrappedKeyring\n wrappedRecovery\n user {\n self\n }\n }\n appMemberships {\n id\n name\n environments {\n id\n name\n }\n sseEnabled\n }\n networkPolicies {\n id\n name\n allowedIps\n isGlobal\n }\n identities {\n id\n name\n description\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -659,7 +699,7 @@ export function graphql(source: "query GetServiceAccountHandlers($orgId: ID!) {\ /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n }\n}"): (typeof documents)["query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n lastUsed\n }\n }\n}"]; +export function graphql(source: "query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}"): (typeof documents)["query GetServiceAccountTokens($orgId: ID!, $id: ID) {\n serviceAccounts(orgId: $orgId, serviceAccountId: $id) {\n id\n tokens {\n id\n name\n createdAt\n expiresAt\n createdBy {\n fullName\n avatarUrl\n self\n }\n createdByServiceAccount {\n id\n name\n identityKey\n }\n lastUsed\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 42c091cb2..48d2a2d45 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -250,6 +250,13 @@ export type AwsCredentialsType = { username?: Maybe; }; +export type AwsIamConfigType = { + __typename?: 'AwsIamConfigType'; + signatureTtlSeconds?: Maybe; + stsEndpoint?: Maybe; + trustedPrincipals?: Maybe>>; +}; + export enum BillingPeriodEnum { Monthly = 'MONTHLY', Yearly = 'YEARLY' @@ -355,6 +362,11 @@ export type CreateGitLabCiSync = { sync?: Maybe; }; +export type CreateIdentityMutation = { + __typename?: 'CreateIdentityMutation'; + identity?: Maybe; +}; + export type CreateLockboxMutation = { __typename?: 'CreateLockboxMutation'; lockbox?: Maybe; @@ -476,6 +488,11 @@ export type DeleteEnvironmentMutation = { ok?: Maybe; }; +export type DeleteIdentityMutation = { + __typename?: 'DeleteIdentityMutation'; + ok?: Maybe; +}; + export type DeleteInviteMutation = { __typename?: 'DeleteInviteMutation'; ok?: Maybe; @@ -608,8 +625,13 @@ export type EditSecretMutation = { secret?: Maybe; }; -export type EnableServiceAccountThirdPartyAuthMutation = { - __typename?: 'EnableServiceAccountThirdPartyAuthMutation'; +export type EnableServiceAccountClientSideKeyManagementMutation = { + __typename?: 'EnableServiceAccountClientSideKeyManagementMutation'; + serviceAccount?: Maybe; +}; + +export type EnableServiceAccountServerSideKeyManagementMutation = { + __typename?: 'EnableServiceAccountServerSideKeyManagementMutation'; serviceAccount?: Maybe; }; @@ -765,6 +787,34 @@ export type GitLabProjectType = { webUrl?: Maybe; }; +export type IdentityConfigUnion = AwsIamConfigType; + +export type IdentityProviderType = { + __typename?: 'IdentityProviderType'; + description: Scalars['String']['output']; + iconId: Scalars['String']['output']; + id: Scalars['String']['output']; + name: Scalars['String']['output']; + supported: Scalars['Boolean']['output']; +}; + +export type IdentityType = { + __typename?: 'IdentityType'; + config?: Maybe; + createdAt?: Maybe; + defaultTtlSeconds: Scalars['Int']['output']; + deletedAt?: Maybe; + description?: Maybe; + id: Scalars['String']['output']; + maxTtlSeconds: Scalars['Int']['output']; + name: Scalars['String']['output']; + organisation: OrganisationType; + provider: Scalars['String']['output']; + serviceAccounts: Array; + tokenNamePattern?: Maybe; + updatedAt: Scalars['DateTime']['output']; +}; + export type InitEnvSync = { __typename?: 'InitEnvSync'; app?: Maybe; @@ -858,6 +908,7 @@ export type Mutation = { createEnvironmentToken?: Maybe; createGhActionsSync?: Maybe; createGitlabCiSync?: Maybe; + createIdentity?: Maybe; createLockbox?: Maybe; createNetworkAccessPolicy?: Maybe; createNomadSync?: Maybe; @@ -884,6 +935,7 @@ export type Mutation = { deleteDynamicSecret?: Maybe; deleteEnvSync?: Maybe; deleteEnvironment?: Maybe; + deleteIdentity?: Maybe; deleteInvitation?: Maybe; deleteNetworkAccessPolicy?: Maybe; deleteOrganisationMember?: Maybe; @@ -898,7 +950,8 @@ export type Mutation = { deleteUserToken?: Maybe; editSecret?: Maybe; editSecrets?: Maybe; - enableServiceAccountThirdPartyAuth?: Maybe; + enableServiceAccountClientSideKeyManagement?: Maybe; + enableServiceAccountServerSideKeyManagement?: Maybe; initEnvSync?: Maybe; modifySubscription?: Maybe; readSecret?: Maybe; @@ -917,6 +970,7 @@ export type Mutation = { updateAppName?: Maybe; updateAwsDynamicSecret?: Maybe; updateCustomRole?: Maybe; + updateIdentity?: Maybe; updateMemberEnvironmentScope?: Maybe; updateMemberWrappedSecrets?: Maybe; updateNetworkAccessPolicy?: Maybe; @@ -1070,6 +1124,20 @@ export type MutationCreateGitlabCiSyncArgs = { }; +export type MutationCreateIdentityArgs = { + defaultTtlSeconds: Scalars['Int']['input']; + description?: InputMaybe; + maxTtlSeconds: Scalars['Int']['input']; + name: Scalars['String']['input']; + organisationId: Scalars['ID']['input']; + provider: Scalars['String']['input']; + signatureTtlSeconds?: InputMaybe; + stsEndpoint?: InputMaybe; + tokenNamePattern?: InputMaybe; + trustedPrincipals: Scalars['String']['input']; +}; + + export type MutationCreateLockboxArgs = { input?: InputMaybe; }; @@ -1269,6 +1337,11 @@ export type MutationDeleteEnvironmentArgs = { }; +export type MutationDeleteIdentityArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationDeleteInvitationArgs = { inviteId: Scalars['ID']['input']; }; @@ -1341,7 +1414,12 @@ export type MutationEditSecretsArgs = { }; -export type MutationEnableServiceAccountThirdPartyAuthArgs = { +export type MutationEnableServiceAccountClientSideKeyManagementArgs = { + serviceAccountId?: InputMaybe; +}; + + +export type MutationEnableServiceAccountServerSideKeyManagementArgs = { serverWrappedKeyring?: InputMaybe; serverWrappedRecovery?: InputMaybe; serviceAccountId?: InputMaybe; @@ -1466,6 +1544,19 @@ export type MutationUpdateCustomRoleArgs = { }; +export type MutationUpdateIdentityArgs = { + defaultTtlSeconds?: InputMaybe; + description?: InputMaybe; + id: Scalars['ID']['input']; + maxTtlSeconds?: InputMaybe; + name?: InputMaybe; + signatureTtlSeconds?: InputMaybe; + stsEndpoint?: InputMaybe; + tokenNamePattern?: InputMaybe; + trustedPrincipals?: InputMaybe; +}; + + export type MutationUpdateMemberEnvironmentScopeArgs = { appId?: InputMaybe; envKeys?: InputMaybe>>; @@ -1500,6 +1591,7 @@ export type MutationUpdateProviderCredentialsArgs = { export type MutationUpdateServiceAccountArgs = { + identityIds?: InputMaybe>; name?: InputMaybe; roleId?: InputMaybe; serviceAccountId?: InputMaybe; @@ -1689,6 +1781,7 @@ export type Query = { appUsers?: Maybe>>; apps?: Maybe>>; awsSecrets?: Maybe>>; + awsStsEndpoints?: Maybe>>; clientIp?: Maybe; cloudflarePagesProjects?: Maybe>>; cloudflareWorkers?: Maybe>>; @@ -1701,6 +1794,8 @@ export type Query = { githubRepos?: Maybe>>; gitlabGroups?: Maybe>>; gitlabProjects?: Maybe>>; + identities?: Maybe>>; + identityProviders?: Maybe>>; kmsLogs?: Maybe; license?: Maybe; networkAccessPolicies?: Maybe>>; @@ -1834,6 +1929,11 @@ export type QueryGitlabProjectsArgs = { }; +export type QueryIdentitiesArgs = { + organisationId?: InputMaybe; +}; + + export type QueryKmsLogsArgs = { appId?: InputMaybe; end?: InputMaybe; @@ -2213,6 +2313,7 @@ export type ServiceAccountTokenType = { __typename?: 'ServiceAccountTokenType'; createdAt?: Maybe; createdBy?: Maybe; + createdByServiceAccount?: Maybe; deletedAt?: Maybe; expiresAt?: Maybe; id: Scalars['String']['output']; @@ -2233,11 +2334,12 @@ export type ServiceAccountType = { deletedAt?: Maybe; handlers?: Maybe>>; id: Scalars['String']['output']; + identities?: Maybe>; identityKey?: Maybe; name: Scalars['String']['output']; networkPolicies?: Maybe>; role?: Maybe; - thirdPartyAuthEnabled?: Maybe; + serverSideKeyManagementEnabled?: Maybe; tokens?: Maybe>>; updatedAt: Scalars['DateTime']['output']; }; @@ -2339,6 +2441,11 @@ export type UpdateCustomRoleMutation = { role?: Maybe; }; +export type UpdateIdentityMutation = { + __typename?: 'UpdateIdentityMutation'; + identity?: Maybe; +}; + export type UpdateMemberEnvScopeMutation = { __typename?: 'UpdateMemberEnvScopeMutation'; app?: Maybe; @@ -2851,6 +2958,44 @@ export type SwapEnvOrderMutationVariables = Exact<{ export type SwapEnvOrderMutation = { __typename?: 'Mutation', swapEnvironmentOrder?: { __typename?: 'SwapEnvironmentOrderMutation', ok?: boolean | null } | null }; +export type CreateExtIdentityMutationVariables = Exact<{ + organisationId: Scalars['ID']['input']; + provider: Scalars['String']['input']; + name: Scalars['String']['input']; + description?: InputMaybe; + trustedPrincipals: Scalars['String']['input']; + signatureTtlSeconds?: InputMaybe; + stsEndpoint?: InputMaybe; + tokenNamePattern?: InputMaybe; + defaultTtlSeconds: Scalars['Int']['input']; + maxTtlSeconds: Scalars['Int']['input']; +}>; + + +export type CreateExtIdentityMutation = { __typename?: 'Mutation', createIdentity?: { __typename?: 'CreateIdentityMutation', identity?: { __typename?: 'IdentityType', id: string, provider: string, name: string, description?: string | null, tokenNamePattern?: string | null, defaultTtlSeconds: number, maxTtlSeconds: number, config?: { __typename?: 'AwsIamConfigType', trustedPrincipals?: Array | null, signatureTtlSeconds?: number | null, stsEndpoint?: string | null } | null } | null } | null }; + +export type DeleteExtIdentityMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type DeleteExtIdentityMutation = { __typename?: 'Mutation', deleteIdentity?: { __typename?: 'DeleteIdentityMutation', ok?: boolean | null } | null }; + +export type UpdateExtIdentityMutationVariables = Exact<{ + id: Scalars['ID']['input']; + name?: InputMaybe; + description?: InputMaybe; + trustedPrincipals?: InputMaybe; + signatureTtlSeconds?: InputMaybe; + stsEndpoint?: InputMaybe; + tokenNamePattern?: InputMaybe; + defaultTtlSeconds?: InputMaybe; + maxTtlSeconds?: InputMaybe; +}>; + + +export type UpdateExtIdentityMutation = { __typename?: 'Mutation', updateIdentity?: { __typename?: 'UpdateIdentityMutation', identity?: { __typename?: 'IdentityType', id: string, name: string, description?: string | null, tokenNamePattern?: string | null, defaultTtlSeconds: number, maxTtlSeconds: number, config?: { __typename?: 'AwsIamConfigType', trustedPrincipals?: Array | null, signatureTtlSeconds?: number | null, stsEndpoint?: string | null } | null } | null } | null }; + export type AcceptOrganisationInviteMutationVariables = Exact<{ orgId: Scalars['ID']['input']; identityKey: Scalars['String']['input']; @@ -2949,6 +3094,22 @@ export type DeleteServiceAccountTokenOpMutationVariables = Exact<{ export type DeleteServiceAccountTokenOpMutation = { __typename?: 'Mutation', deleteServiceAccountToken?: { __typename?: 'DeleteServiceAccountTokenMutation', ok?: boolean | null } | null }; +export type EnableSaClientKeyManagementMutationVariables = Exact<{ + serviceAccountId: Scalars['ID']['input']; +}>; + + +export type EnableSaClientKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountClientSideKeyManagement?: { __typename?: 'EnableServiceAccountClientSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, serverSideKeyManagementEnabled?: boolean | null } | null } | null }; + +export type EnableSaServerKeyManagementMutationVariables = Exact<{ + serviceAccountId: Scalars['ID']['input']; + serverWrappedKeyring: Scalars['String']['input']; + serverWrappedRecovery: Scalars['String']['input']; +}>; + + +export type EnableSaServerKeyManagementMutation = { __typename?: 'Mutation', enableServiceAccountServerSideKeyManagement?: { __typename?: 'EnableServiceAccountServerSideKeyManagementMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, serverSideKeyManagementEnabled?: boolean | null } | null } | null }; + export type UpdateServiceAccountHandlerKeysMutationVariables = Exact<{ orgId: Scalars['ID']['input']; handlers?: InputMaybe> | InputMaybe>; @@ -2961,10 +3122,11 @@ export type UpdateServiceAccountOpMutationVariables = Exact<{ serviceAccountId: Scalars['ID']['input']; name: Scalars['String']['input']; roleId: Scalars['ID']['input']; + identityIds?: InputMaybe | Scalars['ID']['input']>; }>; -export type UpdateServiceAccountOpMutation = { __typename?: 'Mutation', updateServiceAccount?: { __typename?: 'UpdateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, permissions?: any | null } | null } | null } | null }; +export type UpdateServiceAccountOpMutation = { __typename?: 'Mutation', updateServiceAccount?: { __typename?: 'UpdateServiceAccountMutation', serviceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, permissions?: any | null } | null, identities?: Array<{ __typename?: 'IdentityType', id: string, name: string }> | null } | null } | null }; export type CreateNewAwsSecretsSyncMutationVariables = Exact<{ envId: Scalars['ID']['input']; @@ -3267,6 +3429,23 @@ export type GetOrganisationsQueryVariables = Exact<{ [key: string]: never; }>; export type GetOrganisationsQuery = { __typename?: 'Query', organisations?: Array<{ __typename?: 'OrganisationType', id: string, name: string, identityKey: string, createdAt?: any | null, plan: ApiOrganisationPlanChoices, memberId?: string | null, keyring?: string | null, recovery?: string | null, planDetail?: { __typename?: 'OrganisationPlanType', name?: string | null, maxUsers?: number | null, maxApps?: number | null, maxEnvsPerApp?: number | null, appCount?: number | null, seatsUsed?: { __typename?: 'SeatsUsed', users?: number | null, serviceAccounts?: number | null, total?: number | null } | null } | null, role?: { __typename?: 'RoleType', name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null } | null> | null }; +export type GetAwsStsEndpointsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetAwsStsEndpointsQuery = { __typename?: 'Query', awsStsEndpoints?: Array | null }; + +export type GetIdentityProvidersQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetIdentityProvidersQuery = { __typename?: 'Query', identityProviders?: Array<{ __typename?: 'IdentityProviderType', id: string, name: string, description: string, iconId: string, supported: boolean } | null> | null }; + +export type GetOrganisationIdentitiesQueryVariables = Exact<{ + organisationId: Scalars['ID']['input']; +}>; + + +export type GetOrganisationIdentitiesQuery = { __typename?: 'Query', identities?: Array<{ __typename?: 'IdentityType', id: string, provider: string, name: string, description?: string | null, tokenNamePattern?: string | null, defaultTtlSeconds: number, maxTtlSeconds: number, createdAt?: any | null, config?: { __typename?: 'AwsIamConfigType', trustedPrincipals?: Array | null, signatureTtlSeconds?: number | null, stsEndpoint?: string | null } | null } | null> | null }; + export type CheckOrganisationNameAvailabilityQueryVariables = Exact<{ name: Scalars['String']['input']; }>; @@ -3459,7 +3638,7 @@ export type GetServiceAccountDetailQueryVariables = Exact<{ }>; -export type GetServiceAccountDetailQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, appMemberships?: Array<{ __typename?: 'AppMembershipType', id: string, name: string, sseEnabled: boolean, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null, networkPolicies?: Array<{ __typename?: 'NetworkAccessPolicyType', id: string, name: string, allowedIps: string, isGlobal: boolean }> | null } | null> | null }; +export type GetServiceAccountDetailQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null, serverSideKeyManagementEnabled?: boolean | null, createdAt?: any | null, role?: { __typename?: 'RoleType', id: string, name?: string | null, description?: string | null, color?: string | null, permissions?: any | null } | null, handlers?: Array<{ __typename?: 'ServiceAccountHandlerType', id: string, wrappedKeyring: string, wrappedRecovery: string, user: { __typename?: 'OrganisationMemberType', self?: boolean | null } } | null> | null, appMemberships?: Array<{ __typename?: 'AppMembershipType', id: string, name: string, sseEnabled: boolean, environments: Array<{ __typename?: 'EnvironmentType', id: string, name: string } | null> }> | null, networkPolicies?: Array<{ __typename?: 'NetworkAccessPolicyType', id: string, name: string, allowedIps: string, isGlobal: boolean }> | null, identities?: Array<{ __typename?: 'IdentityType', id: string, name: string, description?: string | null }> | null } | null> | null }; export type GetServiceAccountHandlersQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3474,7 +3653,7 @@ export type GetServiceAccountTokensQueryVariables = Exact<{ }>; -export type GetServiceAccountTokensQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, lastUsed?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null } | null> | null } | null> | null }; +export type GetServiceAccountTokensQuery = { __typename?: 'Query', serviceAccounts?: Array<{ __typename?: 'ServiceAccountType', id: string, tokens?: Array<{ __typename?: 'ServiceAccountTokenType', id: string, name: string, createdAt?: any | null, expiresAt?: any | null, lastUsed?: any | null, createdBy?: { __typename?: 'OrganisationMemberType', fullName?: string | null, avatarUrl?: string | null, self?: boolean | null } | null, createdByServiceAccount?: { __typename?: 'ServiceAccountType', id: string, name: string, identityKey?: string | null } | null } | null> | null } | null> | null }; export type GetServiceAccountsQueryVariables = Exact<{ orgId: Scalars['ID']['input']; @@ -3668,6 +3847,9 @@ export const RevokeDynamicSecretLeaseOpDocument = {"kind":"Document","definition export const UpdateDynamicSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateDynamicSecret"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"dynamicSecretId"}},"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":"path"}},"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":"description"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"defaultTtl"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"maxTtl"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"authenticationId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"config"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AWSConfigInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"keyMap"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"KeyMapInput"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateAwsDynamicSecret"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"dynamicSecretId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"dynamicSecretId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"description"},"value":{"kind":"Variable","name":{"kind":"Name","value":"description"}}},{"kind":"Argument","name":{"kind":"Name","value":"defaultTtl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"defaultTtl"}}},{"kind":"Argument","name":{"kind":"Name","value":"maxTtl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"maxTtl"}}},{"kind":"Argument","name":{"kind":"Name","value":"authenticationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"authenticationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"config"},"value":{"kind":"Variable","name":{"kind":"Name","value":"config"}}},{"kind":"Argument","name":{"kind":"Name","value":"keyMap"},"value":{"kind":"Variable","name":{"kind":"Name","value":"keyMap"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dynamicSecret"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"provider"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateSharedSecretDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSharedSecret"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LockboxInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createLockbox"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lockbox"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"allowedViews"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const SwapEnvOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SwapEnvOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"environment1Id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"environment2Id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"swapEnvironmentOrder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"environment1Id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environment1Id"}}},{"kind":"Argument","name":{"kind":"Name","value":"environment2Id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environment2Id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; +export const CreateExtIdentityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateExtIdentity"},"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":"provider"}},"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":"description"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"trustedPrincipals"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"signatureTtlSeconds"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stsEndpoint"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tokenNamePattern"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"defaultTtlSeconds"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"maxTtlSeconds"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createIdentity"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"provider"},"value":{"kind":"Variable","name":{"kind":"Name","value":"provider"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"description"},"value":{"kind":"Variable","name":{"kind":"Name","value":"description"}}},{"kind":"Argument","name":{"kind":"Name","value":"trustedPrincipals"},"value":{"kind":"Variable","name":{"kind":"Name","value":"trustedPrincipals"}}},{"kind":"Argument","name":{"kind":"Name","value":"signatureTtlSeconds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"signatureTtlSeconds"}}},{"kind":"Argument","name":{"kind":"Name","value":"stsEndpoint"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stsEndpoint"}}},{"kind":"Argument","name":{"kind":"Name","value":"tokenNamePattern"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tokenNamePattern"}}},{"kind":"Argument","name":{"kind":"Name","value":"defaultTtlSeconds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"defaultTtlSeconds"}}},{"kind":"Argument","name":{"kind":"Name","value":"maxTtlSeconds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"maxTtlSeconds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identity"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"provider"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AwsIamConfigType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"trustedPrincipals"}},{"kind":"Field","name":{"kind":"Name","value":"signatureTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"stsEndpoint"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tokenNamePattern"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"maxTtlSeconds"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteExtIdentityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteExtIdentity"},"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":"deleteIdentity"},"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":"ok"}}]}}]}}]} as unknown as DocumentNode; +export const UpdateExtIdentityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateExtIdentity"},"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":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"description"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"trustedPrincipals"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"signatureTtlSeconds"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stsEndpoint"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tokenNamePattern"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"defaultTtlSeconds"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"maxTtlSeconds"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateIdentity"},"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":"description"},"value":{"kind":"Variable","name":{"kind":"Name","value":"description"}}},{"kind":"Argument","name":{"kind":"Name","value":"trustedPrincipals"},"value":{"kind":"Variable","name":{"kind":"Name","value":"trustedPrincipals"}}},{"kind":"Argument","name":{"kind":"Name","value":"signatureTtlSeconds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"signatureTtlSeconds"}}},{"kind":"Argument","name":{"kind":"Name","value":"stsEndpoint"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stsEndpoint"}}},{"kind":"Argument","name":{"kind":"Name","value":"tokenNamePattern"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tokenNamePattern"}}},{"kind":"Argument","name":{"kind":"Name","value":"defaultTtlSeconds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"defaultTtlSeconds"}}},{"kind":"Argument","name":{"kind":"Name","value":"maxTtlSeconds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"maxTtlSeconds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identity"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AwsIamConfigType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"trustedPrincipals"}},{"kind":"Field","name":{"kind":"Name","value":"signatureTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"stsEndpoint"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tokenNamePattern"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"maxTtlSeconds"}}]}}]}}]}}]} as unknown as DocumentNode; export const AcceptOrganisationInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AcceptOrganisationInvite"},"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":"identityKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"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":"createOrganisationMember"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedRecovery"}}},{"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":"orgMember"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const BulkInviteMembersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BulkInviteMembers"},"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":"invites"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"InviteInput"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bulkInviteOrganisationMembers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"invites"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invites"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteOrgInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOrgInvite"},"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; @@ -3679,8 +3861,10 @@ export const CreateServiceAccountOpDocument = {"kind":"Document","definitions":[ export const CreateSaTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSAToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"expiry"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const DeleteServiceAccountTokenOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteServiceAccountTokenOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteServiceAccountToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tokenId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; +export const EnableSaClientKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAClientKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountClientSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"serverSideKeyManagementEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; +export const EnableSaServerKeyManagementDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EnableSAServerKeyManagement"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enableServiceAccountServerSideKeyManagement"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedKeyring"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedKeyring"}}},{"kind":"Argument","name":{"kind":"Name","value":"serverWrappedRecovery"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serverWrappedRecovery"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"serverSideKeyManagementEnabled"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateServiceAccountHandlerKeysDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountHandlerKeys"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceAccountHandlerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"handlers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handlers"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; -export const UpdateServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpdateServiceAccountOpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateServiceAccountOp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identityIds"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateServiceAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"serviceAccountId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewAwsSecretsSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewAWSSecretsSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createAwsSecretSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretName"}}},{"kind":"Argument","name":{"kind":"Name","value":"kmsId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"kmsId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewCfPagesSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfPagesSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflarePagesSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"deploymentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deploymentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectEnv"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectEnv"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewCfWorkersSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewCfWorkersSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workerName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCloudflareWorkersSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"workerName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workerName"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -3715,6 +3899,9 @@ export const GetAppKmsLogsDocument = {"kind":"Document","definitions":[{"kind":" export const GetAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetApps"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"userTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationInvites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"organisationMembers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"savedCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"planDetail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"maxUsers"}},{"kind":"Field","name":{"kind":"Name","value":"maxApps"}},{"kind":"Field","name":{"kind":"Name","value":"maxEnvsPerApp"}},{"kind":"Field","name":{"kind":"Name","value":"seatsUsed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"users"}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"appCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"memberId"}},{"kind":"Field","name":{"kind":"Name","value":"keyring"}},{"kind":"Field","name":{"kind":"Name","value":"recovery"}}]}}]}}]} as unknown as DocumentNode; +export const GetAwsStsEndpointsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAwsStsEndpoints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"awsStsEndpoints"}}]}}]} as unknown as DocumentNode; +export const GetIdentityProvidersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetIdentityProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identityProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"iconId"}},{"kind":"Field","name":{"kind":"Name","value":"supported"}}]}}]}}]} as unknown as DocumentNode; +export const GetOrganisationIdentitiesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationIdentities"},"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":"identities"},"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":"provider"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AwsIamConfigType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"trustedPrincipals"}},{"kind":"Field","name":{"kind":"Name","value":"signatureTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"stsEndpoint"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tokenNamePattern"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"maxTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const CheckOrganisationNameAvailabilityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheckOrganisationNameAvailability"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationNameAvailable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}]}]}}]} as unknown as DocumentNode; export const GetGlobalAccessUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetGlobalAccessUsers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationGlobalAccessUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}}]} as unknown as DocumentNode; export const GetInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetInvites"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"organisationInvites"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inviteeEmail"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -3739,9 +3926,9 @@ export const GetEnvSecretsKvDocument = {"kind":"Document","definitions":[{"kind" export const GetSecretTagsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecretTags"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secretTags"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]} as unknown as DocumentNode; export const GetSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSecrets"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"secrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"override"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}}]}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"folders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"folderCount"}},{"kind":"Field","name":{"kind":"Name","value":"secretCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"appEnvironments"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"environmentKeys"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}},{"kind":"Argument","name":{"kind":"Name","value":"environmentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSeed"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedSalt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"envSyncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"dynamicSecrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"provider"}},{"kind":"Field","name":{"kind":"Name","value":"keyMap"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"keyName"}},{"kind":"Field","name":{"kind":"Name","value":"masked"}}]}},{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AWSConfigType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"usernameTemplate"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"iamPath"}},{"kind":"Field","name":{"kind":"Name","value":"permissionBoundaryArn"}},{"kind":"Field","name":{"kind":"Name","value":"policyArns"}},{"kind":"Field","name":{"kind":"Name","value":"policyDocument"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"defaultTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"maxTtlSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"authentication"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetServiceTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"keys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetServiceAccountDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"serverSideKeyManagementEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"appMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"networkPolicies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowedIps"}},{"kind":"Field","name":{"kind":"Name","value":"isGlobal"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountHandlersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountHandlers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccountHandlers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}}]}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}}]} as unknown as DocumentNode; -export const GetServiceAccountTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUsed"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetServiceAccountTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccountTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"tokens"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"self"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdByServiceAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastUsed"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetServiceAccountsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServiceAccounts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"serviceAccountId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"handlers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyring"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedRecovery"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"self"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const GetOrganisationSyncsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrganisationSyncs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}},{"kind":"Field","name":{"kind":"Name","value":"app"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"authentication"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"credentials"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"completedAt"}},{"kind":"Field","name":{"kind":"Name","value":"meta"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"savedCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"credentials"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"expectedCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"optionalCredentials"}}]}},{"kind":"Field","name":{"kind":"Name","value":"syncCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"sseEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceAccounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"syncs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetAwsSecretsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAwsSecrets"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"awsSecrets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"arn"}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index ce0f2e1b8..6154c9b23 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -3,6 +3,7 @@ type Query { organisations: [OrganisationType] roles(orgId: ID): [RoleType] networkAccessPolicies(organisationId: ID): [NetworkAccessPolicyType] + identities(organisationId: ID): [IdentityType] organisationNameAvailable(name: String): Boolean license: PhaseLicenseType organisationLicense(organisationId: ID): ActivatedPhaseLicenseType @@ -13,22 +14,9 @@ type Query { validateInvite(inviteId: ID): OrganisationMemberInviteType apps(organisationId: ID, appId: ID): [AppType] kmsLogs(appId: ID, start: BigInt, end: BigInt): KMSLogsResponseType - secretLogs( - appId: ID - start: BigInt - end: BigInt - eventTypes: [String] - memberId: ID - memberType: MemberType - environmentId: ID - ): SecretLogsResponseType + secretLogs(appId: ID, start: BigInt, end: BigInt, eventTypes: [String], memberId: ID, memberType: MemberType, environmentId: ID): SecretLogsResponseType appActivityChart(appId: ID, period: TimeRange): [ChartDataPointType] - appEnvironments( - appId: ID - environmentId: ID - memberId: ID - memberType: MemberType - ): [EnvironmentType] + appEnvironments(appId: ID, environmentId: ID, memberId: ID, memberType: MemberType): [EnvironmentType] appUsers(appId: ID): [OrganisationMemberType] appServiceAccounts(appId: ID): [ServiceAccountType] secrets(envId: ID, path: String, id: ID): [SecretType] @@ -45,6 +33,8 @@ type Query { sseEnabled(appId: ID): Boolean providers: [ProviderType] services: [ServiceType] + awsStsEndpoints: [JSONString] + identityProviders: [IdentityProviderType] savedCredentials(orgId: ID): [ProviderCredentialsType] syncs(orgId: ID, appId: ID, envId: ID): [EnvironmentSyncType] envSyncs(envId: ID): [EnvironmentSyncType] @@ -62,11 +52,7 @@ type Query { testVaultCreds(credentialId: ID): Boolean testNomadCreds(credentialId: ID): Boolean validateAwsAssumeRoleAuth: AWSValidationResultType - validateAwsAssumeRoleCredentials( - roleArn: String! - region: String - externalId: String - ): AWSValidationResultType + validateAwsAssumeRoleCredentials(roleArn: String!, region: String, externalId: String): AWSValidationResultType stripeCheckoutDetails(stripeSessionId: String!): StripeCheckoutDetails stripeSubscriptionDetails(organisationId: ID): StripeSubscriptionDetails stripeCustomerPortalUrl(organisationId: ID!): String @@ -95,19 +81,13 @@ value as specified by scalar DateTime enum ApiOrganisationPlanChoices { - """ - Free - """ + """Free""" FR - """ - Pro - """ + """Pro""" PR - """ - Enterprise - """ + """Enterprise""" EN } @@ -209,24 +189,16 @@ type EnvironmentType { } enum ApiEnvironmentEnvTypeChoices { - """ - Development - """ + """Development""" DEV - """ - Staging - """ + """Staging""" STAGING - """ - Production - """ + """Production""" PROD - """ - Custom - """ + """Custom""" CUSTOM } @@ -314,11 +286,12 @@ type ServiceAccountType { createdAt: DateTime updatedAt: DateTime! deletedAt: DateTime - thirdPartyAuthEnabled: Boolean + serverSideKeyManagementEnabled: Boolean handlers: [ServiceAccountHandlerType] tokens: [ServiceAccountTokenType] appMemberships: [AppMembershipType!] networkPolicies: [NetworkAccessPolicyType!] + identities: [IdentityType!] } type ServiceAccountHandlerType { @@ -339,6 +312,7 @@ type ServiceAccountTokenType { token: String! wrappedKeyShare: String! createdBy: OrganisationMemberType + createdByServiceAccount: ServiceAccountType createdAt: DateTime updatedAt: DateTime! deletedAt: DateTime @@ -347,25 +321,41 @@ type ServiceAccountTokenType { lastUsed: DateTime } +type IdentityType { + id: String! + organisation: OrganisationType! + provider: String! + name: String! + description: String + config: IdentityConfigUnion + tokenNamePattern: String + defaultTtlSeconds: Int! + maxTtlSeconds: Int! + createdAt: DateTime + updatedAt: DateTime! + deletedAt: DateTime + serviceAccounts: [ServiceAccountType!]! +} + +union IdentityConfigUnion = AwsIamConfigType + +type AwsIamConfigType { + trustedPrincipals: [String] + signatureTtlSeconds: Int + stsEndpoint: String +} + enum ApiSecretEventEventTypeChoices { - """ - Create - """ + """Create""" C - """ - Read - """ + """Read""" R - """ - Update - """ + """Update""" U - """ - Delete - """ + """Delete""" D } @@ -388,9 +378,7 @@ type DynamicSecretType { path: String! authentication: ProviderCredentialsType - """ - Which provider this secret is associated with. - """ + """Which provider this secret is associated with.""" provider: ApiDynamicSecretProviderChoices! config: DynamicSecretConfigUnion keyMap: [KeyMap] @@ -421,9 +409,7 @@ type ProviderType { } enum ApiDynamicSecretProviderChoices { - """ - AWS - """ + """AWS""" AWS } @@ -459,9 +445,7 @@ type DynamicSecretLeaseType { serviceAccount: ServiceAccountType ttl: Int - """ - Current status of the lease - """ + """Current status of the lease""" status: ApiDynamicSecretLeaseStatusChoices! credentials: LeaseCredentialsUnion createdAt: DateTime @@ -472,29 +456,19 @@ type DynamicSecretLeaseType { } enum ApiDynamicSecretLeaseStatusChoices { - """ - Created - """ + """Created""" CREATED - """ - Active - """ + """Active""" ACTIVE - """ - Renewed - """ + """Renewed""" RENEWED - """ - Revoked - """ + """Revoked""" REVOKED - """ - Expired - """ + """Expired""" EXPIRED } @@ -519,29 +493,19 @@ type DynamicSecretLeaseEventType { } enum ApiDynamicSecretLeaseEventEventTypeChoices { - """ - Created - """ + """Created""" CREATED - """ - Active - """ + """Active""" ACTIVE - """ - Renewed - """ + """Renewed""" RENEWED - """ - Revoked - """ + """Revoked""" REVOKED - """ - Expired - """ + """Expired""" EXPIRED } @@ -560,29 +524,19 @@ type EnvironmentSyncType { } enum ApiEnvironmentSyncStatusChoices { - """ - In progress - """ + """In progress""" IN_PROGRESS - """ - Completed - """ + """Completed""" COMPLETED - """ - cancelled - """ + """cancelled""" CANCELLED - """ - Timed out - """ + """Timed out""" TIMED_OUT - """ - Failed - """ + """Failed""" FAILED } @@ -603,29 +557,19 @@ type EnvironmentSyncEventType { } enum ApiEnvironmentSyncEventStatusChoices { - """ - In progress - """ + """In progress""" IN_PROGRESS - """ - Completed - """ + """Completed""" COMPLETED - """ - cancelled - """ + """cancelled""" CANCELLED - """ - Timed out - """ + """Timed out""" TIMED_OUT - """ - Failed - """ + """Failed""" FAILED } @@ -688,19 +632,13 @@ type ActivatedPhaseLicenseType { } enum ApiActivatedPhaseLicensePlanChoices { - """ - Free - """ + """Free""" FR - """ - Pro - """ + """Pro""" PR - """ - Enterprise - """ + """Enterprise""" EN } @@ -755,13 +693,9 @@ type KMSLogType implements Node { longitude: Float } -""" -An object with an ID -""" +"""An object with an ID""" interface Node { - """ - The ID of the object - """ + """The ID of the object""" id: ID! } @@ -817,6 +751,14 @@ type EnvironmentTokenType { updatedAt: DateTime! } +type IdentityProviderType { + id: String! + name: String! + description: String! + iconId: String! + supported: Boolean! +} + type CloudFlarePagesType { name: String deploymentId: String @@ -999,253 +941,66 @@ type DynamicSecretProviderType { } type Mutation { - createOrganisation( - id: ID! - identityKey: String! - name: String! - wrappedKeyring: String! - wrappedRecovery: String! - ): CreateOrganisationMutation - bulkInviteOrganisationMembers( - invites: [InviteInput]! - orgId: ID! - ): BulkInviteOrganisationMembersMutation - createOrganisationMember( - identityKey: String! - inviteId: ID! - orgId: ID! - wrappedKeyring: String - wrappedRecovery: String - ): CreateOrganisationMemberMutation + createOrganisation(id: ID!, identityKey: String!, name: String!, wrappedKeyring: String!, wrappedRecovery: String!): CreateOrganisationMutation + bulkInviteOrganisationMembers(invites: [InviteInput]!, orgId: ID!): BulkInviteOrganisationMembersMutation + createOrganisationMember(identityKey: String!, inviteId: ID!, orgId: ID!, wrappedKeyring: String, wrappedRecovery: String): CreateOrganisationMemberMutation deleteOrganisationMember(memberId: ID!): DeleteOrganisationMemberMutation updateOrganisationMemberRole(memberId: ID!, roleId: ID!): UpdateOrganisationMemberRole - updateMemberWrappedSecrets( - orgId: ID! - wrappedKeyring: String! - wrappedRecovery: String! - ): UpdateUserWrappedSecretsMutation + updateMemberWrappedSecrets(orgId: ID!, wrappedKeyring: String!, wrappedRecovery: String!): UpdateUserWrappedSecretsMutation deleteInvitation(inviteId: ID!): DeleteInviteMutation - createApp( - appSeed: String! - appToken: String! - appVersion: Int! - id: ID! - identityKey: String! - name: String! - organisationId: ID! - wrappedKeyShare: String! - ): CreateAppMutation + 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 updateAppName(id: ID!, name: String!): UpdateAppNameMutation - addAppMember( - appId: ID - envKeys: [EnvironmentKeyInput] - memberId: ID - memberType: MemberType - ): AddAppMemberMutation + addAppMember(appId: ID, envKeys: [EnvironmentKeyInput], memberId: ID, memberType: MemberType): AddAppMemberMutation bulkAddAppMembers(appId: ID!, members: [AppMemberInputType]!): BulkAddAppMembersMutation removeAppMember(appId: ID, memberId: ID, memberType: MemberType): RemoveAppMemberMutation - updateMemberEnvironmentScope( - appId: ID - envKeys: [EnvironmentKeyInput] - memberId: ID - memberType: MemberType - ): UpdateMemberEnvScopeMutation - createEnvironment( - adminKeys: [EnvironmentKeyInput] - environmentData: EnvironmentInput! - wrappedSalt: String - wrappedSeed: String - ): CreateEnvironmentMutation + updateMemberEnvironmentScope(appId: ID, envKeys: [EnvironmentKeyInput], memberId: ID, memberType: MemberType): UpdateMemberEnvScopeMutation + createEnvironment(adminKeys: [EnvironmentKeyInput], environmentData: EnvironmentInput!, wrappedSalt: String, wrappedSeed: String): CreateEnvironmentMutation deleteEnvironment(environmentId: ID!): DeleteEnvironmentMutation renameEnvironment(environmentId: ID!, name: String!): RenameEnvironmentMutation swapEnvironmentOrder(environment1Id: ID!, environment2Id: ID!): SwapEnvironmentOrderMutation - createEnvironmentKey( - envId: ID! - identityKey: String! - userId: ID - wrappedSalt: String! - wrappedSeed: String! - ): CreateEnvironmentKeyMutation - createEnvironmentToken( - envId: ID! - identityKey: String! - name: String! - token: String! - wrappedKeyShare: String! - ): CreateEnvironmentTokenMutation - createCustomRole( - color: String - description: String - name: String - organisationId: ID! - permissions: JSONString - ): CreateCustomRoleMutation - updateCustomRole( - color: String - description: String - id: ID! - name: String - permissions: JSONString - ): UpdateCustomRoleMutation + createEnvironmentKey(envId: ID!, identityKey: String!, userId: ID, wrappedSalt: String!, wrappedSeed: String!): CreateEnvironmentKeyMutation + createEnvironmentToken(envId: ID!, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateEnvironmentTokenMutation + createCustomRole(color: String, description: String, name: String, organisationId: ID!, permissions: JSONString): CreateCustomRoleMutation + updateCustomRole(color: String, description: String, id: ID!, name: String, permissions: JSONString): UpdateCustomRoleMutation deleteCustomRole(id: ID!): DeleteCustomRoleMutation - createNetworkAccessPolicy( - allowedIps: String! - isGlobal: Boolean! - name: String - organisationId: ID! - ): CreateNetworkAccessPolicyMutation + createNetworkAccessPolicy(allowedIps: String!, isGlobal: Boolean!, name: String, organisationId: ID!): CreateNetworkAccessPolicyMutation updateNetworkAccessPolicy(policyInputs: [UpdatePolicyInput]): UpdateNetworkAccessPolicyMutation deleteNetworkAccessPolicy(id: ID!): DeleteNetworkAccessPolicyMutation - updateAccountNetworkAccessPolicies( - accountInputs: [AccountPolicyInput] - organisationId: ID! - ): UpdateAccountNetworkAccessPolicies - createServiceAccount( - handlers: [ServiceAccountHandlerInput] - identityKey: String - name: String - organisationId: ID - roleId: ID - serverWrappedKeyring: String - serverWrappedRecovery: String - ): CreateServiceAccountMutation - enableServiceAccountThirdPartyAuth( - serverWrappedKeyring: String - serverWrappedRecovery: String - serviceAccountId: ID - ): EnableServiceAccountThirdPartyAuthMutation - updateServiceAccountHandlers( - handlers: [ServiceAccountHandlerInput] - organisationId: ID - ): UpdateServiceAccountHandlersMutation - updateServiceAccount(name: String, roleId: ID, serviceAccountId: ID): UpdateServiceAccountMutation + updateAccountNetworkAccessPolicies(accountInputs: [AccountPolicyInput], organisationId: ID!): UpdateAccountNetworkAccessPolicies + createIdentity(defaultTtlSeconds: Int!, description: String, maxTtlSeconds: Int!, name: String!, organisationId: ID!, provider: String!, signatureTtlSeconds: Int, stsEndpoint: String, tokenNamePattern: String, trustedPrincipals: String!): CreateIdentityMutation + updateIdentity(defaultTtlSeconds: Int, description: String, id: ID!, maxTtlSeconds: Int, name: String, signatureTtlSeconds: Int, stsEndpoint: String, tokenNamePattern: String, trustedPrincipals: String): UpdateIdentityMutation + deleteIdentity(id: ID!): DeleteIdentityMutation + createServiceAccount(handlers: [ServiceAccountHandlerInput], identityKey: String, name: String, organisationId: ID, roleId: ID, serverWrappedKeyring: String, serverWrappedRecovery: String): CreateServiceAccountMutation + enableServiceAccountServerSideKeyManagement(serverWrappedKeyring: String, serverWrappedRecovery: String, serviceAccountId: ID): EnableServiceAccountServerSideKeyManagementMutation + enableServiceAccountClientSideKeyManagement(serviceAccountId: ID): EnableServiceAccountClientSideKeyManagementMutation + updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], organisationId: ID): UpdateServiceAccountHandlersMutation + updateServiceAccount(identityIds: [ID!], name: String, roleId: ID, serviceAccountId: ID): UpdateServiceAccountMutation deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation - createServiceAccountToken( - expiry: BigInt - identityKey: String! - name: String! - serviceAccountId: ID - token: String! - wrappedKeyShare: String! - ): CreateServiceAccountTokenMutation + createServiceAccountToken(expiry: BigInt, identityKey: String!, name: String!, serviceAccountId: ID, token: String!, wrappedKeyShare: String!): CreateServiceAccountTokenMutation deleteServiceAccountToken(tokenId: ID): DeleteServiceAccountTokenMutation initEnvSync(appId: ID, envKeys: [EnvironmentKeyInput]): InitEnvSync deleteEnvSync(syncId: ID): DeleteSync triggerSync(syncId: ID): TriggerSync toggleSyncActive(syncId: ID): ToggleSyncActive updateSyncAuthentication(credentialId: ID, syncId: ID): UpdateSyncAuthentication - createProviderCredentials( - credentials: JSONString - name: String - orgId: ID - provider: String - ): CreateProviderCredentials - updateProviderCredentials( - credentialId: ID - credentials: JSONString - name: String - ): UpdateProviderCredentials + createProviderCredentials(credentials: JSONString, name: String, orgId: ID, provider: String): CreateProviderCredentials + updateProviderCredentials(credentialId: ID, credentials: JSONString, name: String): UpdateProviderCredentials deleteProviderCredentials(credentialId: ID): DeleteProviderCredentials - createCloudflarePagesSync( - credentialId: ID - deploymentId: ID - envId: ID - path: String - projectEnv: String - projectName: String - ): CreateCloudflarePagesSync - createCloudflareWorkersSync( - credentialId: ID - envId: ID - path: String - workerName: String - ): CreateCloudflareWorkersSync - createAwsSecretSync( - credentialId: ID - envId: ID - kmsId: String - path: String - secretName: String - ): CreateAWSSecretsManagerSync - createGhActionsSync( - credentialId: ID - envId: ID - owner: String - path: String - repoName: String - ): CreateGitHubActionsSync - createVaultSync( - credentialId: ID - engine: String - envId: ID - path: String - vaultPath: String - ): CreateVaultSync - createNomadSync( - credentialId: ID - envId: ID - nomadNamespace: String - nomadPath: String - path: String - ): CreateNomadSync - createGitlabCiSync( - credentialId: ID - envId: ID - isGroup: Boolean - masked: Boolean - path: String - protected: Boolean - resourceId: String - resourcePath: String - ): CreateGitLabCISync - createRailwaySync( - credentialId: ID - envId: ID - path: String - railwayEnvironment: RailwayResourceInput - railwayProject: RailwayResourceInput - railwayService: RailwayResourceInput - ): CreateRailwaySync - createVercelSync( - credentialId: ID - envId: ID - environment: String - path: String - projectId: String - projectName: String - secretType: String - teamId: String - teamName: String - ): CreateVercelSync - createRenderSync( - credentialId: ID - envId: ID - path: String - resourceId: String - resourceName: String - resourceType: RenderResourceType - secretFileName: String - ): CreateRenderSync - createUserToken( - expiry: BigInt - identityKey: String! - name: String! - orgId: ID! - token: String! - wrappedKeyShare: String! - ): CreateUserTokenMutation + createCloudflarePagesSync(credentialId: ID, deploymentId: ID, envId: ID, path: String, projectEnv: String, projectName: String): CreateCloudflarePagesSync + createCloudflareWorkersSync(credentialId: ID, envId: ID, path: String, workerName: String): CreateCloudflareWorkersSync + createAwsSecretSync(credentialId: ID, envId: ID, kmsId: String, path: String, secretName: String): CreateAWSSecretsManagerSync + createGhActionsSync(credentialId: ID, envId: ID, owner: String, path: String, repoName: String): CreateGitHubActionsSync + createVaultSync(credentialId: ID, engine: String, envId: ID, path: String, vaultPath: String): CreateVaultSync + createNomadSync(credentialId: ID, envId: ID, nomadNamespace: String, nomadPath: String, path: String): CreateNomadSync + createGitlabCiSync(credentialId: ID, envId: ID, isGroup: Boolean, masked: Boolean, path: String, protected: Boolean, resourceId: String, resourcePath: String): CreateGitLabCISync + createRailwaySync(credentialId: ID, envId: ID, path: String, railwayEnvironment: RailwayResourceInput, railwayProject: RailwayResourceInput, railwayService: RailwayResourceInput): CreateRailwaySync + createVercelSync(credentialId: ID, envId: ID, environment: String, path: String, projectId: String, projectName: String, secretType: String, teamId: String, teamName: String): CreateVercelSync + createRenderSync(credentialId: ID, envId: ID, path: String, resourceId: String, resourceName: String, resourceType: RenderResourceType, secretFileName: String): CreateRenderSync + 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 + createServiceToken(appId: ID!, environmentKeys: [EnvironmentKeyInput], expiry: BigInt, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateServiceTokenMutation deleteServiceToken(tokenId: ID!): DeleteServiceTokenMutation createSecretFolder(envId: ID, name: String, path: String): CreateSecretFolderMutation deleteSecretFolder(folderId: ID): DeleteSecretFolderMutation @@ -1260,53 +1015,20 @@ type Mutation { createOverride(overrideData: PersonalSecretInput): CreatePersonalSecretMutation removeOverride(secretId: ID): DeletePersonalSecretMutation createLockbox(input: LockboxInput): CreateLockboxMutation - createSubscriptionCheckoutSession( - billingPeriod: BillingPeriodEnum - organisationId: ID! - planType: PlanTypeEnum - ): CreateSubscriptionCheckoutSession + createSubscriptionCheckoutSession(billingPeriod: BillingPeriodEnum, organisationId: ID!, planType: PlanTypeEnum): CreateSubscriptionCheckoutSession deletePaymentMethod(organisationId: ID, paymentMethodId: String): DeletePaymentMethodMutation cancelSubscription(organisationId: ID, subscriptionId: String!): UpdateSubscriptionResponse resumeSubscription(organisationId: ID, subscriptionId: String!): UpdateSubscriptionResponse - modifySubscription( - billingPeriod: BillingPeriodEnum - organisationId: ID! - planType: PlanTypeEnum - subscriptionId: String! - ): UpdateSubscriptionResponse + modifySubscription(billingPeriod: BillingPeriodEnum, organisationId: ID!, planType: PlanTypeEnum, subscriptionId: String!): UpdateSubscriptionResponse createSetupIntent(organisationId: ID): CreateSetupIntentMutation setDefaultPaymentMethod( organisationId: ID - """ - Payment Method ID to set as default - """ + """Payment Method ID to set as default""" paymentMethodId: String! ): SetDefaultPaymentMethodMutation - createAwsDynamicSecret( - authenticationId: ID - config: AWSConfigInput! - defaultTtl: Int - description: String - environmentId: ID! - keyMap: [KeyMapInput]! - maxTtl: Int - name: String! - organisationId: ID! - path: String - ): CreateAWSDynamicSecretMutation - updateAwsDynamicSecret( - authenticationId: ID - config: AWSConfigInput! - defaultTtl: Int - description: String - dynamicSecretId: ID! - keyMap: [KeyMapInput]! - maxTtl: Int - name: String! - organisationId: ID! - path: String - ): UpdateAWSDynamicSecretMutation + createAwsDynamicSecret(authenticationId: ID, config: AWSConfigInput!, defaultTtl: Int, description: String, environmentId: ID!, keyMap: [KeyMapInput]!, maxTtl: Int, name: String!, organisationId: ID!, path: String): CreateAWSDynamicSecretMutation + updateAwsDynamicSecret(authenticationId: ID, config: AWSConfigInput!, defaultTtl: Int, description: String, dynamicSecretId: ID!, keyMap: [KeyMapInput]!, maxTtl: Int, name: String!, organisationId: ID!, path: String): UpdateAWSDynamicSecretMutation deleteDynamicSecret(secretId: ID!): DeleteDynamicSecretMutation createDynamicSecretLease(name: String, secretId: ID!, ttl: Int): LeaseDynamicSecret renewDynamicSecretLease(leaseId: ID!, ttl: Int): RenewLeaseMutation @@ -1472,6 +1194,18 @@ enum AccountTypeEnum { SERVICE } +type CreateIdentityMutation { + identity: IdentityType +} + +type UpdateIdentityMutation { + identity: IdentityType +} + +type DeleteIdentityMutation { + ok: Boolean +} + type CreateServiceAccountMutation { serviceAccount: ServiceAccountType } @@ -1483,7 +1217,11 @@ input ServiceAccountHandlerInput { wrappedRecovery: String! } -type EnableServiceAccountThirdPartyAuthMutation { +type EnableServiceAccountServerSideKeyManagementMutation { + serviceAccount: ServiceAccountType +} + +type EnableServiceAccountClientSideKeyManagementMutation { serviceAccount: ServiceAccountType } @@ -1749,4 +1487,4 @@ type RenewLeaseMutation { type RevokeLeaseMutation { lease: DynamicSecretLeaseType -} +} \ No newline at end of file diff --git a/frontend/app/[team]/access/identities/page.tsx b/frontend/app/[team]/access/identities/page.tsx new file mode 100644 index 000000000..9313bda4d --- /dev/null +++ b/frontend/app/[team]/access/identities/page.tsx @@ -0,0 +1,230 @@ +'use client' + +import { useContext, useState, useRef, useEffect } from 'react' +import { organisationContext } from '@/contexts/organisationContext' +import { userHasPermission } from '@/utils/access/permissions' +import { useQuery } from '@apollo/client' +import GetOrganisationIdentities from '@/graphql/queries/identities/getOrganisationIdentities.gql' +import { Button } from '@/components/common/Button' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' +import { FaPlus, FaEdit, FaBan } from 'react-icons/fa' +import { EmptyState } from '@/components/common/EmptyState' +import { AddNewIdentityDialog } from '@/components/identities/AddNewIdentityDialog' + +import { ProviderCards } from '@/components/identities/ProviderCards' +import GetIdentityProviders from '@/graphql/queries/identities/getIdentityProviders.gql' +import { AwsIamIdentityForm } from '@/components/identities/providers/aws/iam' +import GenericDialog from '@/components/common/GenericDialog' +import { TbLockShare } from 'react-icons/tb' +import { DeleteExternalIdentityDialog } from '@/components/identities/providers/DeleteExternalIdentityDialog' +import { IdentityType } from '@/apollo/graphql' +import { EditExternalIdentityDialog } from '@/components/identities/providers/EditExternalIdentityDialog' + +export default function IdentityPage() { + const { activeOrganisation: organisation } = useContext(organisationContext) + + // Permission checks + const canReadIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'ExternalIdentities', 'read') + : false + const canCreateIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'ExternalIdentities', 'create') + : false + const canUpdateIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'ExternalIdentities', 'update') + : false + + const { data, refetch } = useQuery(GetOrganisationIdentities, { + variables: { organisationId: organisation?.id }, + skip: !organisation || !canReadIdentities, + fetchPolicy: 'cache-and-network', + }) + + const { data: providersData } = useQuery(GetIdentityProviders) + + const [selectedProvider, setSelectedProvider] = useState(null) + + const awsEditDialogRef = useRef<{ openModal: () => void; closeModal: () => void }>(null) + const createDialogRef = useRef<{ openModal: () => void; closeModal: () => void }>(null) + const [awsEditDialogOpen, setAwsEditDialogOpen] = useState(false) + const [editingIdentity, setEditingIdentity] = useState(null) + + const identities: IdentityType[] = data?.identities ?? [] + const identityProviders = providersData?.identityProviders ?? [] + + const openCreateIdentityDialog = () => createDialogRef.current?.openModal() + + // Effect to open dialog when awsEditDialogOpen changes + useEffect(() => { + if (awsEditDialogOpen && awsEditDialogRef.current) { + awsEditDialogRef.current.openModal() + } + }, [awsEditDialogOpen]) + + // Helper function to get provider info by ID + const getProviderInfo = (providerId: string) => { + const provider = identityProviders.find((p: any) => p.id === providerId) + return provider || { name: providerId, iconId: 'unknown' } + } + + const handleProviderSelect = (providerId: string) => { + setEditingIdentity(null) // Ensure we're creating, not editing + setSelectedProvider(providerId) + openCreateIdentityDialog() + } + + const handleEditIdentity = (identity: IdentityType) => { + if (!canUpdateIdentities) return + setEditingIdentity(identity) + if (identity.provider === 'aws_iam') { + setAwsEditDialogOpen(true) + } + } + + const handleProviderSelectorSuccess = () => { + refetch() + } + + const handleAwsEditSuccess = () => { + setAwsEditDialogOpen(false) + setEditingIdentity(null) + awsEditDialogRef.current?.closeModal() + refetch() + } + + // Early return if no read permission + if (!canReadIdentities) { + return ( + + + + } + > + <> + + ) + } + + return ( +
+ {identities.length === 0 ? ( +
+
+

+ Set up a new identity +

+
+ Third party identity platforms can be used to authenticate clients and provision + access tokens. Select a provider below to get started. +
+
+ {canCreateIdentities ? ( +
+ +
+ ) : ( + + +
+ } + > + <> + + )} + + ) : ( +
+
+
External identities
+
Manage identities used for external auth
+
+
+ {canCreateIdentities && ( +
+ +
+ )} +
+ + + + + + + + + + {identities.map((idn: IdentityType) => { + const providerInfo = getProviderInfo(idn.provider) + return ( + + + + + + + ) + })} + +
+ Provider + + Name + + Description +
+
+ +
+ {providerInfo.name} +
+ {idn.name} + + {idn.description} + +
+ {canUpdateIdentities && } + +
+
+
+
+
+ )} + + + + {/* AWS IAM Edit Dialog */} + { + setAwsEditDialogOpen(false) + setEditingIdentity(null) + }} + > + + +
+ ) +} diff --git a/frontend/app/[team]/access/layout.tsx b/frontend/app/[team]/access/layout.tsx index 119b59fc9..8b15a4f46 100644 --- a/frontend/app/[team]/access/layout.tsx +++ b/frontend/app/[team]/access/layout.tsx @@ -5,7 +5,6 @@ import { Tab } from '@headlessui/react' import clsx from 'clsx' import Link from 'next/link' import { usePathname } from 'next/navigation' -import { organisationContext } from '@/contexts/organisationContext' export default function AccessLayout({ params, @@ -15,9 +14,6 @@ export default function AccessLayout({ children: React.ReactNode }) { const path = usePathname() - //const router = useRouter() - - const { activeOrganisation } = useContext(organisationContext) const [tabIndex, setTabIndex] = useState(0) @@ -35,6 +31,10 @@ export default function AccessLayout({ name: 'Roles', link: 'roles', }, + { + name: 'External Identities', + link: 'identities', + }, { name: 'Authentication', link: 'authentication', diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx new file mode 100644 index 000000000..88d484be1 --- /dev/null +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountIdentities.tsx @@ -0,0 +1,262 @@ +'use client' + +import { ServiceAccountType } from '@/apollo/graphql' +import { organisationContext } from '@/contexts/organisationContext' +import { useContext, useMemo, useRef, useState } from 'react' +import { Button } from '@/components/common/Button' +import { EmptyState } from '@/components/common/EmptyState' +import { Dialog, Transition } from '@headlessui/react' +import { Fragment } from 'react' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' +import { ToggleSwitch } from '@/components/common/ToggleSwitch' +import { useMutation, useQuery } from '@apollo/client' +import GetOrganisationIdentities from '@/graphql/queries/identities/getOrganisationIdentities.gql' +import { GetServiceAccountDetail } from '@/graphql/queries/service-accounts/getServiceAccountDetail.gql' +import { toast } from 'react-toastify' +import { KeyManagementDialog } from '@/components/service-accounts/KeyManagementDialog' +import UpdateServiceAccount from '@/graphql/mutations/service-accounts/updateServiceAccount.gql' +import { TbLockShare } from 'react-icons/tb' +import { FaSearch, FaTimesCircle, FaServer } from 'react-icons/fa' +import clsx from 'clsx' +import GenericDialog from '@/components/common/GenericDialog' +import { MdSearchOff } from 'react-icons/md' +import Link from 'next/link' + +export const ServiceAccountIdentities = ({ account }: { account: ServiceAccountType }) => { + const { activeOrganisation: organisation } = useContext(organisationContext) + const { data } = useQuery(GetOrganisationIdentities, { + variables: { organisationId: organisation?.id }, + skip: !organisation, + }) + + const dialogRef = useRef<{ openModal: () => void; closeModal: () => void }>(null) + + const [updateAccount, { loading }] = useMutation(UpdateServiceAccount) + + const orgIdentities: any[] = data?.identities ?? [] + const initialSelected = new Set( + (account as any).identities?.map((i: any) => i.id as string) ?? [] + ) + const [selected, setSelected] = useState>(initialSelected) + const [searchQuery, setSearchQuery] = useState('') + + const toggle = (id: string) => { + const s = new Set(selected) + s.has(id) ? s.delete(id) : s.add(id) + setSelected(s) + } + + const openManageIdentitiesDialog = () => dialogRef.current?.openModal() + const closeManageIdentitiesDialog = () => dialogRef.current?.closeModal() + + // Filter identities based on search query + const filteredIdentities = orgIdentities.filter( + (identity: any) => + identity.name.toLowerCase().includes(searchQuery.toLowerCase()) || + identity.description?.toLowerCase().includes(searchQuery.toLowerCase()) + ) + + const handleSave = async () => { + await updateAccount({ + variables: { + serviceAccountId: account.id, + name: account.name, + roleId: account.role!.id, + identityIds: Array.from(selected), + }, + refetchQueries: [ + { query: GetServiceAccountDetail, variables: { orgId: organisation?.id, id: account.id } }, + ], + }) + closeManageIdentitiesDialog() + toast.success('Updated identities for this account') + } + + if (!account.serverSideKeyManagementEnabled) { + return ( +
+ {/* Server-side key management is required state */} +
External Identities
+
+ Manage which external identities are trusted for this account +
+ + +
+ } + > + + + + ) + } + + return ( +
+
+
+
External Identities
+
+
+ Manage which external identities are trusted for this account +
+ {(account as any).identities && (account as any).identities.length > 0 && ( + + )} +
+
+
+ {(account as any).identities && (account as any).identities.length > 0 ? ( +
+ {(account as any).identities.map((identity: any) => ( +
+
+
+ +
+
+ {identity.name} +
+
+
+ {identity.description} +
+
+ ))} +
+ ) : ( + + +
+ } + > + + + )} +
+
+ + + {orgIdentities.length > 0 ? ( +
+
+ Manage External Identities associated with this Service Account +
+
+
+
+ +
+ setSearchQuery(e.target.value)} + /> + setSearchQuery('')} + /> +
+
+ + + + + + + + + + + {filteredIdentities.map((idn: any) => ( + + + + + + + ))} + +
+ Provider + + Identity + + Enabled +
+ + +
+
{idn.name}
+
+ {idn.description} +
+
+
+ toggle(idn.id)} /> +
+ {orgIdentities.length > 0 && filteredIdentities.length === 0 && ( + + +
+ } + > + <> + + )} +
+ + +
+ + ) : ( +
+ + +
+ } + > + <> + + + + + + + )} +
+ + ) +} diff --git a/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx index 9ef68e79a..a187190a3 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/_components/ServiceAccountTokens.tsx @@ -94,10 +94,23 @@ export const ServiceAccountTokens = ({ account }: { account: ServiceAccountType
by - - - {token?.createdBy?.fullName} - + {token?.createdBy ? ( + <> + + + {token.createdBy.fullName} + + + ) : token?.createdByServiceAccount ? ( + <> + + + {token.createdByServiceAccount.name} + + + ) : ( + Unknown + )}
diff --git a/frontend/app/[team]/access/service-accounts/[account]/page.tsx b/frontend/app/[team]/access/service-accounts/[account]/page.tsx index f54f2c4c6..1525c95c0 100644 --- a/frontend/app/[team]/access/service-accounts/[account]/page.tsx +++ b/frontend/app/[team]/access/service-accounts/[account]/page.tsx @@ -9,6 +9,7 @@ import { useMutation, useQuery } from '@apollo/client' import Link from 'next/link' import { useContext, useEffect, useState } from 'react' import { FaBan, FaBoxOpen, FaChevronLeft, FaCog, FaEdit, FaNetworkWired } from 'react-icons/fa' +import { FaServer, FaArrowDownUpLock } from 'react-icons/fa6' import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog' import { AddAppButton } from './_components/AddAppsToServiceAccountsButton' import { ServiceAccountType } from '@/apollo/graphql' @@ -22,6 +23,8 @@ import { SseLabel } from '@/components/apps/EncryptionModeIndicator' import { IPChip } from '../../network/_components/IPChip' import { UpdateAccountNetworkPolicies } from '@/components/access/UpdateAccountNetworkPolicies' import { ServiceAccountTokens } from './_components/ServiceAccountTokens' +import { KeyManagementDialog } from '@/components/service-accounts/KeyManagementDialog' +import { ServiceAccountIdentities } from './_components/ServiceAccountIdentities' export default function ServiceAccount({ params }: { params: { team: string; account: string } }) { const { activeOrganisation: organisation } = useContext(organisationContext) @@ -133,39 +136,61 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
Back to service accounts
-
- -
-

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

- {account.id} +
+
+ +
+

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

+ + {account.id} + +
+
+
+ {account.serverSideKeyManagementEnabled ? ( + + ) : ( + + )} + + + {account.serverSideKeyManagementEnabled ? 'Server-side' : 'Client-side'} KMS + + {userCanUpdateSA && ( + + )}
@@ -295,6 +320,8 @@ export default function ServiceAccount({ params }: { params: { team: string; acc )}
+ + {userCanViewNetworkAccess && (
diff --git a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx index 2b1f2d68c..6bac30866 100644 --- a/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx +++ b/frontend/app/[team]/access/service-accounts/_components/CreateServiceAccountDialog.tsx @@ -221,12 +221,6 @@ export const CreateServiceAccountDialog = () => { )}
- {/*
- - setThirdParty(!thirdParty)} /> -
*/}
diff --git a/frontend/components/identities/AddNewIdentityDialog.tsx b/frontend/components/identities/AddNewIdentityDialog.tsx new file mode 100644 index 000000000..5c90ce547 --- /dev/null +++ b/frontend/components/identities/AddNewIdentityDialog.tsx @@ -0,0 +1,73 @@ +import { useState, forwardRef, useImperativeHandle, useRef, useEffect } from 'react' + +import { FaPlus } from 'react-icons/fa' +import { AwsIamIdentityForm } from './providers/aws/iam' +import { ProviderCards } from './ProviderCards' +import GenericDialog from '../common/GenericDialog' +import { set } from 'lodash' + +interface AddNewIdentityDialogProps { + organisationId: string + onSuccess: () => void + preSelectedProvider?: string | null +} + +export const AddNewIdentityDialog = forwardRef< + { openModal: () => void; closeModal: () => void }, + AddNewIdentityDialogProps +>(({ organisationId, onSuccess, preSelectedProvider = null }, ref) => { + const dialogRef = useRef<{ openModal: () => void; closeModal: () => void }>(null) + const [selectedProvider, setSelectedProvider] = useState(preSelectedProvider) + + useImperativeHandle(ref, () => ({ + openModal: () => dialogRef.current?.openModal(), + closeModal: () => dialogRef.current?.closeModal(), + })) + + useEffect(() => { + if (preSelectedProvider) setSelectedProvider(preSelectedProvider) + }, [preSelectedProvider]) + + const handleProviderSelect = (providerId: string) => { + setSelectedProvider(providerId) + } + + const handleSuccess = () => { + setSelectedProvider(null) + dialogRef.current?.closeModal() + onSuccess() + } + + const handleBack = () => { + setSelectedProvider(null) + } + + const handleDialogClose = () => { + setSelectedProvider(null) + } + + return ( + +
+
+ {selectedProvider + ? 'Set up a new external identity' + : 'Select a provider below to set up a new identity'} +
+ {!selectedProvider ? ( +
+ +
+ ) : selectedProvider === 'aws_iam' ? ( + + ) : null} +
+
+ ) +}) + +AddNewIdentityDialog.displayName = 'AddNewIdentityDialog' diff --git a/frontend/components/identities/ProviderCards.tsx b/frontend/components/identities/ProviderCards.tsx new file mode 100644 index 000000000..2132abf94 --- /dev/null +++ b/frontend/components/identities/ProviderCards.tsx @@ -0,0 +1,63 @@ +import { Card } from '../common/Card' +import { ProviderIcon } from '../syncing/ProviderIcon' +import { FaArrowRight } from 'react-icons/fa' +import { useQuery } from '@apollo/client' +import GetIdentityProviders from '@/graphql/queries/identities/getIdentityProviders.gql' + +interface IdentityProvider { + id: string + name: string + description: string + iconId: string + supported: boolean +} + +const ProviderCard = ({ + provider, + onClick, +}: { + provider: IdentityProvider + onClick: () => void +}) => { + return ( + + + + ) +} + +interface ProviderCardsProps { + onProviderSelect: (providerId: string) => void +} + +export const ProviderCards = ({ onProviderSelect }: ProviderCardsProps) => { + const { data } = useQuery(GetIdentityProviders) + const providers: IdentityProvider[] = data?.identityProviders ?? [] + + return ( + <> + {providers.map((provider) => ( +
+ onProviderSelect(provider.id)} /> +
+ ))} + + ) +} diff --git a/frontend/components/identities/providers/DeleteExternalIdentityDialog.tsx b/frontend/components/identities/providers/DeleteExternalIdentityDialog.tsx new file mode 100644 index 000000000..358d71e2f --- /dev/null +++ b/frontend/components/identities/providers/DeleteExternalIdentityDialog.tsx @@ -0,0 +1,78 @@ +import { IdentityType } from '@/apollo/graphql' +import { Button } from '@/components/common/Button' +import GenericDialog from '@/components/common/GenericDialog' +import { FaTrashAlt } from 'react-icons/fa' +import DeleteIdentity from '@/graphql/mutations/identities/deleteIdentity.gql' +import GetOrganisationIdentities from '@/graphql/queries/identities/getOrganisationIdentities.gql' +import { organisationContext } from '@/contexts/organisationContext' +import { userHasPermission } from '@/utils/access/permissions' +import { useMutation } from '@apollo/client' +import { useContext, useRef } from 'react' +import { toast } from 'react-toastify' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' + +export const DeleteExternalIdentityDialog = ({ identity }: { identity: IdentityType }) => { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const dialogRef = useRef<{ closeModal: () => void }>(null) + + const closeModal = () => dialogRef.current?.closeModal() + + const canDeleteIdentities = organisation + ? userHasPermission(organisation.role!.permissions, 'ExternalIdentities', 'delete') + : false + + const [deleteIdentity, { loading: deleting }] = useMutation(DeleteIdentity) + + const handleDelete = async () => { + if (!canDeleteIdentities) return + + try { + const { id } = identity + await deleteIdentity({ + variables: { id }, + refetchQueries: [ + { query: GetOrganisationIdentities, variables: { organisationId: organisation?.id } }, + ], + }) + toast.success('Identity deleted') + } catch (e) { + toast.error('Failed to delete identity') + } + } + + if (!canDeleteIdentities) return <> + + return ( + + + Delete + + } + > +
+

+ Are you sure you want to delete the{' '} + + + {' '} + {identity.name} external + identity? +

+
+ + +
+
+
+ ) +} diff --git a/frontend/components/identities/providers/EditExternalIdentityDialog.tsx b/frontend/components/identities/providers/EditExternalIdentityDialog.tsx new file mode 100644 index 000000000..b25a60222 --- /dev/null +++ b/frontend/components/identities/providers/EditExternalIdentityDialog.tsx @@ -0,0 +1,60 @@ +import { IdentityType } from '@/apollo/graphql' +import GenericDialog from '@/components/common/GenericDialog' +import { AwsIamIdentityForm } from './aws/iam' +import { organisationContext } from '@/contexts/organisationContext' +import { useContext, useRef } from 'react' +import { FaCog, FaEdit } from 'react-icons/fa' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' + +export const EditExternalIdentityDialog = ({ identity }: { identity: IdentityType }) => { + const { activeOrganisation: organisation } = useContext(organisationContext) + + const dialogRef = useRef<{ openModal: () => void; closeModal: () => void }>(null) + + const dialogTitle = () => { + switch (identity.provider) { + case 'aws_iam': + return ( +
+
+ +
+
+

+ Edit AWS IAM Identity +

+
+ Configure trusted entities and token settings +
+
+
+ ) + default: + return <>Edit Identity + } + } + + return ( + + Configure + + } + > +
+ {identity.provider === 'aws_iam' && ( + dialogRef.current?.closeModal()} + /> + )} +
+
+ ) +} diff --git a/frontend/components/identities/providers/aws/iam.tsx b/frontend/components/identities/providers/aws/iam.tsx new file mode 100644 index 000000000..e9a9a27f1 --- /dev/null +++ b/frontend/components/identities/providers/aws/iam.tsx @@ -0,0 +1,493 @@ +'use client' + +import { useState, useMemo, Fragment, useEffect } from 'react' +import { useMutation, useQuery } from '@apollo/client' +import GetAwsStsEndpoints from '@/graphql/queries/identities/getAwsStsEndpoints.gql' +import CreateIdentity from '@/graphql/mutations/identities/createIdentity.gql' +import UpdateIdentity from '@/graphql/mutations/identities/updateIdentity.gql' +import GetOrganisationIdentities from '@/graphql/queries/identities/getOrganisationIdentities.gql' +import { Combobox, Transition } from '@headlessui/react' +import { Input } from '@/components/common/Input' +import { Button } from '@/components/common/Button' +import { FaChevronDown, FaCheckCircle } from 'react-icons/fa' +import { LiaAws } from 'react-icons/lia' +import { toast } from 'react-toastify' +import clsx from 'clsx' +import { parseTTL, formatTTL, isValidTTL, getTTLExamples } from '@/utils/ttl' +import { ProviderIcon } from '@/components/syncing/ProviderIcon' +import { IdentityType } from '@/apollo/graphql' + +type AwsIamConfig = { + trustedPrincipals: string[] + signatureTtlSeconds: number + stsEndpoint: string +} + +type AwsStsEndpoint = { + regionCode: string | null + regionName: string + endpoint: string +} + +interface AwsIamIdentityDialogProps { + organisationId: string + identity?: IdentityType | null + onSuccess: () => void + onBack?: () => void +} + +export const AwsIamIdentityForm = ({ + organisationId, + identity, + onSuccess, + onBack, +}: AwsIamIdentityDialogProps) => { + const { data: stsData } = useQuery(GetAwsStsEndpoints) + const [createIdentity, { loading: creating }] = useMutation(CreateIdentity) + const [updateIdentity, { loading: updating }] = useMutation(UpdateIdentity) + + const [initialState, setInitialState] = useState<{ + name: string + description: string + trustedPrincipals: string + signatureTtlInput: string + signatureTtlSeconds: number + stsEndpoint: string + tokenNamePattern: string + defaultTtlInput: string + maxTtlInput: string + defaultTtlSeconds: number + maxTtlSeconds: number + } | null>(null) + + // Form state + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [trustedPrincipals, setTrustedPrincipals] = useState('') + const [signatureTtlInput, setSignatureTtlInput] = useState('60s') + const [signatureTtlSeconds, setSignatureTtlSeconds] = useState(60) + const [useCustomEndpoint, setUseCustomEndpoint] = useState(false) + const [stsEndpoint, setStsEndpoint] = useState('https://sts.amazonaws.com') + const [selectedStsEndpoint, setSelectedStsEndpoint] = useState(null) + const [stsQuery, setStsQuery] = useState('') + const [tokenNamePattern, setTokenNamePattern] = useState('') + const [defaultTtlInput, setDefaultTtlInput] = useState('1h') + const [maxTtlInput, setMaxTtlInput] = useState('24h') + const [defaultTtlSeconds, setDefaultTtlSeconds] = useState(3600) + const [maxTtlSeconds, setMaxTtlSeconds] = useState(86400) + + const awsStsEndpoints: AwsStsEndpoint[] = useMemo(() => { + try { + return (stsData?.awsStsEndpoints || []).map((raw: string) => JSON.parse(raw)) + } catch { + return [] + } + }, [stsData]) + + const filteredStsEndpoints = awsStsEndpoints.filter((endpoint) => { + if (stsQuery === '') { + return true + } + const queryLower = stsQuery.toLowerCase() + const regionNameMatches = endpoint.regionName?.toLowerCase().includes(queryLower) || false + const regionCodeMatches = endpoint.regionCode?.toLowerCase().includes(queryLower) || false + const endpointMatches = endpoint.endpoint?.toLowerCase().includes(queryLower) || false + + return regionNameMatches || regionCodeMatches || endpointMatches + }) + + useEffect(() => { + if (identity) { + // Populate form with identity data + setName(identity.name) + setDescription(identity.description || '') + setTrustedPrincipals(identity.config?.trustedPrincipals?.join(', ') || '') + setSignatureTtlInput(formatTTL(identity.config?.signatureTtlSeconds ?? 60)) + setSignatureTtlSeconds(identity.config?.signatureTtlSeconds ?? 60) + setStsEndpoint(identity.config?.stsEndpoint ?? '') + + const existingEndpoint = awsStsEndpoints.find( + (ep) => identity.config && ep.endpoint === identity.config.stsEndpoint + ) + setUseCustomEndpoint(!existingEndpoint) + setSelectedStsEndpoint(existingEndpoint || null) + setStsQuery('') + + setTokenNamePattern(identity.tokenNamePattern || '') + setDefaultTtlInput(formatTTL(identity.defaultTtlSeconds)) + setMaxTtlInput(formatTTL(identity.maxTtlSeconds)) + setDefaultTtlSeconds(identity.defaultTtlSeconds) + setMaxTtlSeconds(identity.maxTtlSeconds) + setInitialState({ + name: identity.name, + description: identity.description || '', + trustedPrincipals: identity.config?.trustedPrincipals?.join(', ') || '', + signatureTtlInput: formatTTL(identity.config?.signatureTtlSeconds ?? 60), + signatureTtlSeconds: identity.config?.signatureTtlSeconds ?? 60, + stsEndpoint: identity.config?.stsEndpoint ?? '', + tokenNamePattern: identity.tokenNamePattern || '', + defaultTtlInput: formatTTL(identity.defaultTtlSeconds), + maxTtlInput: formatTTL(identity.maxTtlSeconds), + defaultTtlSeconds: identity.defaultTtlSeconds, + maxTtlSeconds: identity.maxTtlSeconds, + }) + } else { + // Set default to Global (Legacy) endpoint if available + if (awsStsEndpoints.length > 0) { + const globalEndpoint = awsStsEndpoints.find( + (ep) => ep.endpoint === 'https://sts.amazonaws.com' + ) + if (globalEndpoint) { + setSelectedStsEndpoint(globalEndpoint) + setStsEndpoint(globalEndpoint.endpoint) + setUseCustomEndpoint(false) + } + } + } + }, [identity, awsStsEndpoints]) + + const handleDefaultTtlChange = (value: string) => { + setDefaultTtlInput(value) + if (isValidTTL(value)) { + setDefaultTtlSeconds(parseTTL(value)) + } + } + + const handleMaxTtlChange = (value: string) => { + setMaxTtlInput(value) + if (isValidTTL(value)) { + setMaxTtlSeconds(parseTTL(value)) + } + } + + const handleSignatureTtlChange = (value: string) => { + setSignatureTtlInput(value) + if (isValidTTL(value)) { + setSignatureTtlSeconds(parseTTL(value)) + } + } + + const handleSave = async () => { + // Client-side guard to avoid firing mutation and duplicating toasts + if (defaultTtlSeconds > maxTtlSeconds) { + toast.error('Default token expiry must be less than or equal to Maximum token expiry') + return + } + if (trustedPrincipals.trim() === '*') { + toast.error("'*' is not allowed as a trusted principal pattern") + return + } + if (!stsEndpoint || stsEndpoint.trim() === '') { + toast.error('Please select an STS endpoint') + return + } + + try { + if (identity) { + await updateIdentity({ + variables: { + id: identity.id, + name, + description: description || null, + trustedPrincipals, + signatureTtlSeconds, + stsEndpoint, + tokenNamePattern: tokenNamePattern || null, + defaultTtlSeconds, + maxTtlSeconds, + }, + }) + toast.success('Identity updated') + } else { + await createIdentity({ + variables: { + organisationId, + provider: 'aws_iam', + name, + description: description || null, + trustedPrincipals, + signatureTtlSeconds, + stsEndpoint, + tokenNamePattern: tokenNamePattern || null, + defaultTtlSeconds, + maxTtlSeconds, + }, + refetchQueries: [{ query: GetOrganisationIdentities, variables: { organisationId } }], + }) + toast.success('Create new AWS IAM External Identity') + } + onSuccess() + } catch (e: any) { + const hasGraphQLErrors = Array.isArray(e?.graphQLErrors) && e.graphQLErrors.length > 0 + if (!hasGraphQLErrors) { + toast.error('Failed to create External Identity') + } else { + toast.error(e.message) + } + } + } + + const isSaving = creating || updating + + const hasChanges = useMemo(() => { + if (!initialState) return true + return ( + name !== initialState.name || + description !== initialState.description || + trustedPrincipals !== initialState.trustedPrincipals || + signatureTtlInput !== initialState.signatureTtlInput || + stsEndpoint !== initialState.stsEndpoint || + tokenNamePattern !== initialState.tokenNamePattern || + defaultTtlInput !== initialState.defaultTtlInput || + maxTtlInput !== initialState.maxTtlInput + ) + }, [ + name, + description, + trustedPrincipals, + signatureTtlInput, + stsEndpoint, + tokenNamePattern, + defaultTtlInput, + maxTtlInput, + initialState, + ]) + + return ( +
+
+
+ + +
+ +
+
Trusted entities
+
+ +