Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: service accounts #383

Merged
merged 99 commits into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from 94 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
0a063d9
feat: add service account models, types, resolvers and schema
rohan-chaturvedi Oct 20, 2024
d1a52ee
chore: regenerate graphql schema
rohan-chaturvedi Oct 20, 2024
457b943
feat: add various query and mutation resolvers
rohan-chaturvedi Oct 21, 2024
29ea9ee
fix: hard delete service accounts
rohan-chaturvedi Oct 21, 2024
5da23ce
chore: regenerate schema and types
rohan-chaturvedi Oct 21, 2024
619a191
feat: add client queries
rohan-chaturvedi Oct 21, 2024
3e46b57
feat: add service account page, create and delete dialogs
rohan-chaturvedi Oct 21, 2024
75169e2
feat: add new service account token model
rohan-chaturvedi Oct 23, 2024
adb2628
feat: allow fetching single service account by id
rohan-chaturvedi Oct 23, 2024
56e4373
feat: add sa token create mutation
rohan-chaturvedi Oct 23, 2024
290da6f
feat: add client side queries
rohan-chaturvedi Oct 23, 2024
31f2e7b
chore: regenerate graphql schema and types
rohan-chaturvedi Oct 23, 2024
92698a3
feat: add service account management page, misc changes and cleanup
rohan-chaturvedi Oct 23, 2024
e0b9802
feat: delete service account tokens
rohan-chaturvedi Oct 23, 2024
816670c
fix: related name for service accounts app fk
rohan-chaturvedi Oct 24, 2024
3b21285
fix: add service accounts to app permissions
rohan-chaturvedi Oct 24, 2024
5e251ba
refactor: app access
rohan-chaturvedi Oct 26, 2024
7fd2a7a
chore: update operation names to avoid type collisions
rohan-chaturvedi Oct 26, 2024
86180d5
feat: add service accounts to app card
rohan-chaturvedi Oct 26, 2024
35460e9
feat: service account auth, permissions serializers for rest
rohan-chaturvedi Oct 28, 2024
eaf418a
feat: add service account logging for secret crud
rohan-chaturvedi Oct 28, 2024
00856bd
feat: render service accounts in logs
rohan-chaturvedi Oct 28, 2024
33241c7
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Oct 28, 2024
1e6f484
fix: check that service account has handlers
rohan-chaturvedi Oct 28, 2024
62959ad
fix: spelling
nimish-ks Oct 28, 2024
7c02d5d
test: permission check
rohan-chaturvedi Oct 28, 2024
d44163c
fix: typo
rohan-chaturvedi Oct 28, 2024
68d70dc
fix: permission checks
rohan-chaturvedi Oct 28, 2024
d92b5fd
fix: typo
rohan-chaturvedi Oct 28, 2024
2bb3c27
feat: manage service account handler updates when updating, assigning…
rohan-chaturvedi Oct 30, 2024
f3e2b41
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Oct 30, 2024
e643a9d
fix: permission check logic for pats
rohan-chaturvedi Nov 1, 2024
6874204
feat: add service account tokens permissions class
rohan-chaturvedi Nov 4, 2024
6bdaa1e
chore: bump deps for python 3.13 support
rohan-chaturvedi Nov 4, 2024
4686a0b
feat: add managed manager and service roles
rohan-chaturvedi Nov 4, 2024
385abd4
feat: add service account role selector
rohan-chaturvedi Nov 4, 2024
f59fac9
feat: add service as the default role for service accounts
rohan-chaturvedi Nov 4, 2024
445e19d
feat: add client mutation to update service accounts
rohan-chaturvedi Nov 4, 2024
1c37150
feat: restrict service accounts from global access roles
rohan-chaturvedi Nov 4, 2024
8fad91a
feat: add role selector to service account manage page
rohan-chaturvedi Nov 4, 2024
7bfff00
feat: sort roles to have managed roles first
rohan-chaturvedi Nov 5, 2024
5bc6840
feat: managed role label styles
rohan-chaturvedi Nov 5, 2024
881bbef
feat: add ui to update account name from account management screen
rohan-chaturvedi Nov 5, 2024
d144303
feat: add link to account tokens to app access
rohan-chaturvedi Nov 5, 2024
974e1f3
fix: enforce permissions for account property updates
rohan-chaturvedi Nov 5, 2024
642a002
fix: reset create account dialog after success
rohan-chaturvedi Nov 5, 2024
f0a6d4d
feat: add quota logic for service accounts
rohan-chaturvedi Nov 5, 2024
4c4e971
fix: copy
rohan-chaturvedi Nov 5, 2024
354a1fc
fix: update app settings danger zone style for consistency
rohan-chaturvedi Nov 5, 2024
5a8e2f5
feat: misc tweaks to tab ui
rohan-chaturvedi Nov 5, 2024
7c7304c
fix: command palette commands
rohan-chaturvedi Nov 5, 2024
4708a8d
fix: misc fixes and updates to navbar
rohan-chaturvedi Nov 5, 2024
ab8ce99
fix: misc styling fixes
rohan-chaturvedi Nov 6, 2024
7f20438
feat: update stripe subscription count when adding or removing servic…
rohan-chaturvedi Nov 6, 2024
8f3655e
feat: update acccount management ui
rohan-chaturvedi Nov 7, 2024
cc121ee
feat: add warning for legacy service tokens
rohan-chaturvedi Nov 7, 2024
8615bcf
fix: misc ui tweaks
rohan-chaturvedi Nov 7, 2024
9b62a9a
fix: add empty state for service accounts
rohan-chaturvedi Nov 7, 2024
b4b6397
fix: overflow in create account dialog
rohan-chaturvedi Nov 7, 2024
c47932e
fix: add button link to access when all accounts added to app
rohan-chaturvedi Nov 7, 2024
86584fd
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Nov 7, 2024
e7a07f5
fix: query updated fields during service account update
rohan-chaturvedi Nov 8, 2024
3fe36d0
chore: regenerate gql types
rohan-chaturvedi Nov 8, 2024
89c98e4
typo
rohan-chaturvedi Nov 8, 2024
9617c05
Feat service accounts service role description (#387)
nimish-ks Nov 10, 2024
a3328c0
fix: env access check for service account
rohan-chaturvedi Nov 12, 2024
d5b6ae5
feat: sort managed roles
rohan-chaturvedi Nov 13, 2024
b0f4f0b
feat: add app memberships to service account page
rohan-chaturvedi Nov 13, 2024
2e4e52d
feat: log and display service account token usage
rohan-chaturvedi Nov 13, 2024
e379198
fix: misc fixes to service account app memberships
rohan-chaturvedi Nov 14, 2024
91a55c6
fix: update env scope display
rohan-chaturvedi Nov 14, 2024
bc2196f
feat: link to service account from secret logs
rohan-chaturvedi Nov 14, 2024
ca59ebe
fix: command palette duplicate id
rohan-chaturvedi Nov 14, 2024
69db49d
fix: access template listbox positioning
rohan-chaturvedi Nov 15, 2024
25514ff
fix: add legacy label to service tokens in role manager
rohan-chaturvedi Nov 15, 2024
580e7b3
fix: tweak listbox open state styling, position
rohan-chaturvedi Nov 15, 2024
4bdbcf2
fix: misc fixes to rest api permission checking
rohan-chaturvedi Nov 15, 2024
121334a
refactor: move api permission checks to middleware
rohan-chaturvedi Nov 15, 2024
3fe52c5
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Nov 15, 2024
1e14b98
chore: regenerate types
rohan-chaturvedi Nov 15, 2024
e3566ae
feat: add empty state for service account app membership
rohan-chaturvedi Nov 15, 2024
a26219b
fix: merge migrations
rohan-chaturvedi Nov 16, 2024
ca69283
refactor: rest permissions
rohan-chaturvedi Nov 16, 2024
8bcd512
fix: exception response for 403s
rohan-chaturvedi Nov 16, 2024
195678e
fix: last used resolver
rohan-chaturvedi Nov 19, 2024
936af8a
fix: get role color
rohan-chaturvedi Nov 19, 2024
2bc19a5
fix: invite user link in command palette
rohan-chaturvedi Nov 19, 2024
ef46b4c
chore: regenerate types
rohan-chaturvedi Nov 19, 2024
41a38cc
fix: only render "danger zone" if delete allowed
rohan-chaturvedi Nov 19, 2024
e043c3f
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Nov 19, 2024
4fbbb90
fix: get started onboarding link
nimish-ks Nov 19, 2024
0015f77
fix: log row border width, alignment
rohan-chaturvedi Nov 19, 2024
0c7a089
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Nov 20, 2024
1b46ab2
Feat: collapsible sidebar (#389)
nimish-ks Nov 21, 2024
94b15b6
feat: update seat limits and usage (#393)
rohan-chaturvedi Nov 23, 2024
f58fa86
chore: bumped version
nimish-ks Nov 23, 2024
f866c8e
fix: token label
nimish-ks Nov 23, 2024
92c72a8
fix: remove the inaccurate token access note
nimish-ks Nov 23, 2024
c284cf0
Merge branch 'main' into feat--service-accounts
rohan-chaturvedi Nov 24, 2024
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
37 changes: 33 additions & 4 deletions backend/api/auth.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
from api.utils.rest import (
get_org_member_from_user_token,
get_service_account_from_token,
get_service_token,
get_token_type,
token_is_expired_or_deleted,
)
from api.models import Environment
from api.utils.access.permissions import user_can_access_environment
from api.utils.access.permissions import (
service_account_can_access_environment,
user_can_access_environment,
)
from rest_framework import authentication, exceptions


class PhaseTokenAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):

token_types = ["User", "Service"]
token_types = ["User", "Service", "ServiceAccount"]

auth_token = request.headers.get("Authorization")

Expand All @@ -24,7 +28,14 @@ def authenticate(self, request):
if token_type not in token_types:
raise exceptions.AuthenticationFailed("Invalid token")

auth = {"token": auth_token, "org_member": None, "service_token": None}
auth = {
"token": auth_token,
"auth_type": token_type,
"org_member": None,
"service_token": None,
"service_account": None,
"service_account_token": None,
}

if token_is_expired_or_deleted(auth_token):
raise exceptions.AuthenticationFailed("Token expired or deleted")
Expand Down Expand Up @@ -62,9 +73,27 @@ def authenticate(self, request):
except Exception as ex:
raise exceptions.NotFound("User not found")

else:
elif token_type == "Service":
service_token = get_service_token(auth_token)
auth["service_token"] = service_token
user = service_token.created_by.user

if token_type == "ServiceAccount":

try:
service_token = get_service_token(auth_token)
service_account = get_service_account_from_token(auth_token)
user = service_token.created_by.user
auth["service_account"] = service_account
auth["service_account_token"] = service_token

if not service_account_can_access_environment(
service_account.id, env.id
):
raise exceptions.AuthenticationFailed(
"Service account cannot access this environment"
)
except Exception as ex:
raise exceptions.NotFound("Service account not found")

return (user, auth)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Generated by Django 4.2.15 on 2024-10-20 09:12

from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
('api', '0084_auto_20241008_0708'),
]

operations = [
migrations.CreateModel(
name='ServiceAccount',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('identity_key', models.CharField(blank=True, max_length=256, null=True)),
('server_wrapped_keyring', models.TextField(null=True)),
('server_wrapped_recovery', models.TextField(null=True)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('apps', models.ManyToManyField(related_name='apps', to='api.app')),
('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisation')),
('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.role')),
],
),
migrations.AddField(
model_name='environmentkey',
name='paths',
field=models.TextField(blank=True, null=True),
),
migrations.CreateModel(
name='ServiceAccountHandler',
fields=[
('id', models.TextField(default=uuid.uuid4, primary_key=True, serialize=False)),
('wrapped_keyring', models.TextField()),
('wrapped_recovery', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('service_account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember')),
],
),
migrations.AddField(
model_name='environmentkey',
name='service_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount'),
),
migrations.AddField(
model_name='servicetoken',
name='service_account',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 4.2.15 on 2024-10-22 07:29

from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
('api', '0085_serviceaccount_environmentkey_paths_and_more'),
]

operations = [
migrations.RemoveField(
model_name='servicetoken',
name='service_account',
),
migrations.CreateModel(
name='ServiceAccountToken',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=64)),
('identity_key', models.CharField(max_length=256)),
('token', models.CharField(max_length=64)),
('wrapped_key_share', models.CharField(max_length=406)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('expires_at', models.DateTimeField(null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember')),
('service_account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.serviceaccount')),
],
),
]
18 changes: 18 additions & 0 deletions backend/api/migrations/0087_alter_serviceaccount_apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.15 on 2024-10-24 12:31

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0086_remove_servicetoken_service_account_and_more'),
]

operations = [
migrations.AlterField(
model_name='serviceaccount',
name='apps',
field=models.ManyToManyField(related_name='service_accounts', to='api.app'),
),
]
19 changes: 19 additions & 0 deletions backend/api/migrations/0088_secretevent_service_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.15 on 2024-10-28 08:43

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('api', '0087_alter_serviceaccount_apps'),
]

operations = [
migrations.AddField(
model_name='secretevent',
name='service_account',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.serviceaccount'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.15 on 2024-10-30 07:10

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('api', '0088_secretevent_service_account'),
]

operations = [
migrations.AlterField(
model_name='serviceaccounthandler',
name='service_account',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='handlers', to='api.serviceaccount'),
),
]
19 changes: 19 additions & 0 deletions backend/api/migrations/0090_alter_serviceaccount_organisation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.15 on 2024-10-30 07:11

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('api', '0089_alter_serviceaccounthandler_service_account'),
]

operations = [
migrations.AlterField(
model_name='serviceaccount',
name='organisation',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='service_accounts', to='api.organisation'),
),
]
30 changes: 30 additions & 0 deletions backend/api/migrations/0091_add_managed_manager_service_roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.15 on 2024-11-04 08:36

from django.db import migrations
from api.utils.access.roles import default_roles


def add_default_roles(apps, schema_editor):
Organisation = apps.get_model("api", "Organisation")
Role = apps.get_model("api", "Role")
OrganisationMember = apps.get_model("api", "OrganisationMember")

# Create default roles for each organisation
for organisation in Organisation.objects.all():
for role_name, _ in default_roles.items():
Role.objects.get_or_create(
name=role_name,
organisation=organisation,
is_default=True,
)


class Migration(migrations.Migration):

dependencies = [
("api", "0090_alter_serviceaccount_organisation"),
]

operations = [
migrations.RunPython(add_default_roles),
]
19 changes: 19 additions & 0 deletions backend/api/migrations/0092_secretevent_service_account_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.15 on 2024-11-13 14:47

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('api', '0091_add_managed_manager_service_roles'),
]

operations = [
migrations.AddField(
model_name='secretevent',
name='service_account_token',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.serviceaccounttoken'),
),
]
14 changes: 14 additions & 0 deletions backend/api/migrations/0093_merge_20241116_0744.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 4.2.15 on 2024-11-16 07:44

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('api', '0086_alter_environmentsync_service'),
('api', '0092_secretevent_service_account_token'),
]

operations = [
]
59 changes: 59 additions & 0 deletions backend/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,39 @@ def delete(self, *args, **kwargs):
self.save()


class ServiceAccount(models.Model):
id = models.TextField(default=uuid4, primary_key=True, editable=False)
name = models.CharField(max_length=255)
organisation = models.ForeignKey(
Organisation, on_delete=models.CASCADE, related_name="service_accounts"
)
role = models.ForeignKey(
Role,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
apps = models.ManyToManyField(App, related_name="service_accounts")
identity_key = models.CharField(max_length=256, null=True, blank=True)
server_wrapped_keyring = models.TextField(null=True)
server_wrapped_recovery = models.TextField(null=True)
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True)
deleted_at = models.DateTimeField(null=True, blank=True)


class ServiceAccountHandler(models.Model):
id = models.TextField(default=uuid4, primary_key=True)
service_account = models.ForeignKey(
ServiceAccount, on_delete=models.CASCADE, related_name="handlers"
)
user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE)
wrapped_keyring = models.TextField()
wrapped_recovery = models.TextField()
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True)


class OrganisationMemberInviteManager(models.Manager):
def create(self, *args, **kwargs):
organisation = kwargs.get("organisation")
Expand Down Expand Up @@ -299,6 +332,10 @@ class EnvironmentKey(models.Model):
user = models.ForeignKey(
OrganisationMember, on_delete=models.CASCADE, blank=True, null=True
)
service_account = models.ForeignKey(
ServiceAccount, on_delete=models.CASCADE, blank=True, null=True
)
paths = models.TextField(blank=True, null=True)
identity_key = models.CharField(max_length=256)
wrapped_seed = models.CharField(max_length=256)
wrapped_salt = models.CharField(max_length=256)
Expand Down Expand Up @@ -429,6 +466,22 @@ class ServiceToken(models.Model):
objects = ServiceTokenManager()


class ServiceAccountToken(models.Model):
id = models.TextField(default=uuid4, primary_key=True, editable=False)
service_account = models.ForeignKey(ServiceAccount, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
identity_key = models.CharField(max_length=256)
token = models.CharField(max_length=64)
wrapped_key_share = models.CharField(max_length=406)
created_by = models.ForeignKey(
OrganisationMember, on_delete=models.CASCADE, blank=True, null=True
)
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True)
deleted_at = models.DateTimeField(blank=True, null=True)
expires_at = models.DateTimeField(null=True)


class UserToken(models.Model):
id = models.TextField(default=uuid4, primary_key=True, editable=False)
user = models.ForeignKey(
Expand Down Expand Up @@ -517,6 +570,12 @@ class SecretEvent(models.Model):
service_token = models.ForeignKey(
ServiceToken, on_delete=models.SET_NULL, blank=True, null=True
)
service_account = models.ForeignKey(
ServiceAccount, on_delete=models.SET_NULL, blank=True, null=True
)
service_account_token = models.ForeignKey(
ServiceAccountToken, on_delete=models.SET_NULL, blank=True, null=True
)
key = models.TextField()
key_digest = models.TextField()
value = models.TextField()
Expand Down
Loading
Loading