Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
52a877f
feat: enhance ServiceAccountToken model with creator validation and r…
nimish-ks Aug 22, 2025
e9d91ba
fix: update ServiceAccountTokenSerializer to use ServiceAccountToken …
nimish-ks Aug 22, 2025
d060d09
feat: add created_by_service_account field to ServiceAccountToken model
nimish-ks Aug 22, 2025
9c2ae45
feat: implement ServiceAccountUser class for service account handling
nimish-ks Aug 22, 2025
828cf77
feat: add createdByServiceAccount field to ServiceAccountTokenType
nimish-ks Aug 22, 2025
c42700f
feat: add EnableServiceAccountSseMutation and related types
nimish-ks Aug 22, 2025
5a45642
feat: add EnableServiceAccountSSE mutation and update GetServiceAccou…
nimish-ks Aug 22, 2025
c031aa9
feat: rename EnableServiceAccountThirdPartyAuthMutation and add clien…
nimish-ks Aug 23, 2025
3d51fa9
feat: update service account mutations for key management
nimish-ks Aug 23, 2025
5ce3ca8
feat: enhance service account mutations for key management
nimish-ks Aug 23, 2025
41001dd
feat: update service account mutations and queries for key management
nimish-ks Aug 23, 2025
9a5772f
feat: enhance service account mutations and queries for key management
nimish-ks Aug 23, 2025
234b985
feat: add service account secret wrapping and unwrapping functions
nimish-ks Aug 23, 2025
d9528d5
feat: implement KeyManagementDialog for service account key management
nimish-ks Aug 23, 2025
7018c3b
feat: enhance ServiceAccountTokens component to display creator infor…
nimish-ks Aug 23, 2025
498f4c2
feat: add createdByServiceAccount field to GetServiceAccountTokens query
nimish-ks Aug 23, 2025
a0a57ab
feat: rename third_party_auth_enabled to server_side_key_management_e…
nimish-ks Aug 23, 2025
5e80b3f
feat: update ServiceAccountType and related queries for key management
nimish-ks Aug 23, 2025
b299c77
feat: add serverSideKeyManagementEnabled field to GetServiceAccountDe…
nimish-ks Aug 23, 2025
86f6fba
feat: add mutations for enabling client-side and server-side key mana…
nimish-ks Aug 23, 2025
64bdc55
fix: update KeyManagementDialog to use serverSideKeyManagementEnabled
nimish-ks Aug 23, 2025
c9dae07
feat: enhance ServiceAccount page with key management display
nimish-ks Aug 23, 2025
b530252
refactor: streamline KeyManagementDialog save logic and remove unnece…
nimish-ks Aug 23, 2025
aeb4bac
refactor: remove unnecessary alert from KeyManagementDialog
nimish-ks Aug 23, 2025
b40b377
feat: add TTL utility functions for human-readable time formats
nimish-ks Aug 24, 2025
7023dca
feat: add IdentityProviders class for managing authentication configu…
nimish-ks Aug 25, 2025
7514944
feat: add Identity model for third-party identity configurations
nimish-ks Aug 25, 2025
6b2de44
feat: add cryptographic utility functions for secret sharing and key …
nimish-ks Aug 25, 2025
8f18ed5
feat: add function to list STS endpoints from botocore metadata
nimish-ks Aug 25, 2025
3d810ef
feat: implement service account token management utilities
nimish-ks Aug 25, 2025
120ec7f
feat: implement AWS IAM authentication endpoint for service accounts
nimish-ks Aug 25, 2025
f06add9
feat: add identity management mutations and queries
nimish-ks Aug 25, 2025
b9c9a20
feat: add AWS IAM authentication route to URL configuration
nimish-ks Aug 25, 2025
5152e90
feat: add identity management mutations for create, update, and delet…
nimish-ks Aug 25, 2025
6027a11
feat: enhance GraphQL schema with Identity and IdentityProvider types
nimish-ks Aug 25, 2025
45761ad
feat: update UpdateServiceAccountMutation to include identity management
nimish-ks Aug 25, 2025
9725fbb
feat: add Identities role to default roles configuration
nimish-ks Aug 25, 2025
77d9316
feat: add resolver for identities in organisation
nimish-ks Aug 25, 2025
4f7cd2b
feat: add resolvers for AWS STS endpoints and identity providers
nimish-ks Aug 25, 2025
8e937a8
feat: implement identity management page for organisation
nimish-ks Aug 25, 2025
4984b74
feat: enhance service account page with identity management features
nimish-ks Aug 25, 2025
61d3b76
feat: implement ServiceAccountIdentities component for managing assoc…
nimish-ks Aug 25, 2025
bc0a69b
feat: add IdentityProviderSelector component for selecting identity p…
nimish-ks Aug 25, 2025
5553582
feat: add ProviderCards component for displaying identity providers
nimish-ks Aug 25, 2025
aecfce0
feat: add AwsIamIdentityDialog component for AWS IAM identity management
nimish-ks Aug 25, 2025
6d9c348
feat: add GraphQL mutations and queries for identity management
nimish-ks Aug 25, 2025
c34a3dd
feat: update AccessLayout component to include third-party identities…
nimish-ks Aug 25, 2025
1e18deb
feat: enhance GraphQL schema with identity management features
nimish-ks Aug 25, 2025
690591d
feat: expand GraphQL schema with identity management mutations and qu…
nimish-ks Aug 25, 2025
c975241
feat: add Identity model and service account identities relationship
nimish-ks Aug 25, 2025
a8ac59c
refactor: remove unused third-party authentication toggle from Create…
nimish-ks Aug 26, 2025
cb70d5b
fix: update terminology for identity management in ServiceAccountIden…
nimish-ks Aug 26, 2025
eb5a70e
fix: update label for identity management in AccessLayout component
nimish-ks Aug 26, 2025
64c46f3
fix: update label for identity management in IdentityPage component
nimish-ks Aug 26, 2025
19b06e5
feat: add unit tests for AWS IAM authentication
nimish-ks Aug 27, 2025
1a5d3ca
feat: implement unit tests for AWS IAM authentication logic
nimish-ks Aug 27, 2025
cd1bc80
feat: add initial identity and AWS identity modules
nimish-ks Aug 27, 2025
933972c
fix: init modules
nimish-ks Aug 27, 2025
78685f4
refactor: rename identity mutation operations for consistency
nimish-ks Aug 27, 2025
cc660ad
refactor: update identity mutation operations to include "Ext" prefix
nimish-ks Aug 27, 2025
6503774
fix: use configured STS endpoint instead of user supploed
nimish-ks Aug 27, 2025
9d21dbc
feat: robust xml parsing from aws response
nimish-ks Aug 27, 2025
108cd39
feat: updated titles and descriptions for server-side key management
nimish-ks Aug 27, 2025
7f16837
chore: remove global legacy sts endpoint list insertion
nimish-ks Aug 27, 2025
080b5ed
Merge branch 'main' into feat--service-account-kms
nimish-ks Sep 10, 2025
5323368
Merge branch 'feat--service-account-kms' into feat--identities
nimish-ks Sep 10, 2025
fa5b430
refactor: external identity crud
rohan-chaturvedi Sep 26, 2025
a1ad2be
fix: misc updates to account identity management ux
rohan-chaturvedi Sep 26, 2025
4ca417d
feat: misc updates to KMS management ux
rohan-chaturvedi Sep 26, 2025
7870555
Merge branch 'main' into feat--service-account-kms
rohan-chaturvedi Oct 2, 2025
527111a
Merge branch 'feat--service-account-kms' into feat--identities
rohan-chaturvedi Oct 2, 2025
df7c5b8
fix: clean up migration graph
rohan-chaturvedi Oct 2, 2025
504662c
chore: regenrate schema and types
rohan-chaturvedi Oct 2, 2025
0a674f6
chore: organize urls
rohan-chaturvedi Oct 2, 2025
113afb7
chore: update url
rohan-chaturvedi Oct 2, 2025
bf4ecdc
fix: misc ui cleanup
rohan-chaturvedi Oct 2, 2025
d73c4ac
chore: misc cleanup
rohan-chaturvedi Oct 2, 2025
4860e08
chore: rename permission class to ExternalIdentities
rohan-chaturvedi Oct 2, 2025
dff980b
chore: combine ttl utils into single file
rohan-chaturvedi Oct 2, 2025
5be3b9a
Merge pull request #635 from phasehq/feat--identities
rohan-chaturvedi Oct 2, 2025
5a0bd0d
fix: copy, layout
rohan-chaturvedi Oct 3, 2025
c751c8a
fix: make sure clean is run on model save, remove redundant imports
rohan-chaturvedi Oct 4, 2025
b0bfa62
chore: update version to v2.53.0
nimish-ks Oct 5, 2025
1fe6860
chore: remove empty else block
rohan-chaturvedi Oct 5, 2025
0c40b7b
fix: add timeout and error handling for AWS STS request
rohan-chaturvedi Oct 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions backend/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down Expand Up @@ -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

Expand Down
57 changes: 57 additions & 0 deletions backend/api/identity_providers.py
Original file line number Diff line number Diff line change
@@ -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")

Original file line number Diff line number Diff line change
@@ -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",
),
),
]
56 changes: 56 additions & 0 deletions backend/api/migrations/0111_identity_serviceaccount_identities.py
Original file line number Diff line number Diff line change
@@ -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"
),
),
]
73 changes: 73 additions & 0 deletions backend/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion backend/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Secret,
ServiceAccount,
ServiceToken,
ServiceAccountToken,
UserToken,
PersonalSecret,
)
Expand Down Expand Up @@ -307,7 +308,7 @@ class ServiceAccountTokenSerializer(serializers.ModelSerializer):
)

class Meta:
model = UserToken
model = ServiceAccountToken
fields = [
"wrapped_key_share",
"account_id",
Expand Down
5 changes: 5 additions & 0 deletions backend/api/utils/access/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -103,6 +106,7 @@
"Members": ["read"],
"ServiceAccounts": [],
"ServiceAccountTokens": [],
"ExternalIdentities": [],
"Roles": ["read"],
"IntegrationCredentials": [
"create",
Expand Down Expand Up @@ -137,6 +141,7 @@
"Members": ["read"],
"ServiceAccounts": ["read"],
"ServiceAccountTokens": ["read"],
"ExternalIdentities": ["read"],
"Roles": ["read"],
"IntegrationCredentials": ["read"],
"NetworkAccessPolicies": ["read"],
Expand Down
Loading