Skip to content

Commit

Permalink
feat: manage service account handler updates when updating, assigning…
Browse files Browse the repository at this point in the history
… roles
  • Loading branch information
rohan-chaturvedi committed Oct 30, 2024
1 parent d92b5fd commit 2bb3c27
Show file tree
Hide file tree
Showing 15 changed files with 297 additions and 87 deletions.
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'),
),
]
8 changes: 6 additions & 2 deletions backend/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ def delete(self, *args, **kwargs):
class ServiceAccount(models.Model):
id = models.TextField(default=uuid4, primary_key=True, editable=False)
name = models.CharField(max_length=255)
organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE)
organisation = models.ForeignKey(
Organisation, on_delete=models.CASCADE, related_name="service_accounts"
)
role = models.ForeignKey(
Role,
on_delete=models.SET_NULL,
Expand All @@ -236,7 +238,9 @@ class ServiceAccount(models.Model):

class ServiceAccountHandler(models.Model):
id = models.TextField(default=uuid4, primary_key=True)
service_account = models.ForeignKey(ServiceAccount, on_delete=models.CASCADE)
service_account = models.ForeignKey(
ServiceAccount, on_delete=models.CASCADE, related_name="handlers"
)
user = models.ForeignKey(OrganisationMember, on_delete=models.CASCADE)
wrapped_keyring = models.TextField()
wrapped_recovery = models.TextField()
Expand Down
26 changes: 14 additions & 12 deletions backend/backend/graphene/mutations/service_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
ServiceAccountHandler,
ServiceAccountToken,
)
from api.utils.access.permissions import user_has_permission
from api.utils.access.permissions import user_has_permission, user_is_org_member
from backend.graphene.types import ServiceAccountTokenType, ServiceAccountType
from datetime import datetime


class ServiceAccountHandlerInput(graphene.InputObjectType):
service_account_id = graphene.ID(required=False)
member_id = graphene.ID(required=False)
wrapped_keyring = graphene.String(required=True)
wrapped_recovery = graphene.String(required=True)
Expand Down Expand Up @@ -141,24 +142,27 @@ def mutate(cls, root, info, service_account_id, name, role_id):

class UpdateServiceAccountHandlersMutation(graphene.Mutation):
class Arguments:
service_account_id = graphene.ID()
organisation_id = graphene.ID()
handlers = graphene.List(ServiceAccountHandlerInput)

service_account = graphene.Field(ServiceAccountType)
ok = graphene.Boolean()

@classmethod
def mutate(cls, root, info, service_account_id, handlers):
def mutate(cls, root, info, organisation_id, handlers):
user = info.context.user
service_account = ServiceAccount.objects.get(id=service_account_id)
org = Organisation.objects.get(id=organisation_id)

if not user_has_permission(
user, "update", "ServiceAccounts", service_account.organisation
):
if not user_is_org_member(user.userId, organisation_id):
raise GraphQLError(
"You don't have the permissions required to update Service Accounts in this organisation"
"You are not a member of this organisation and cannot perform this operation"
)

for account in org.service_accounts.all():
[handler.delete() for handler in account.handlers.all()]

for handler in handlers:
service_account = ServiceAccount.objects.get(id=handler.service_account_id)

if not ServiceAccountHandler.objects.filter(
service_account=service_account, user_id=handler.member_id
).exists():
Expand All @@ -169,9 +173,7 @@ def mutate(cls, root, info, service_account_id, handlers):
wrapped_recovery=handler.wrapped_recovery,
)

return EnableServiceAccountThirdPartyAuthMutation(
service_account=service_account
)
return UpdateServiceAccountHandlersMutation(ok=True)


class DeleteServiceAccountMutation(graphene.Mutation):
Expand Down
5 changes: 5 additions & 0 deletions frontend/apollo/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const documents = {
"mutation CreateSAToken($serviceAccountId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createServiceAccountToken(\n serviceAccountId: $serviceAccountId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n token {\n id\n }\n }\n}": types.CreateSaTokenDocument,
"mutation DeleteServiceAccountOp($id: ID!) {\n deleteServiceAccount(serviceAccountId: $id) {\n ok\n }\n}": types.DeleteServiceAccountOpDocument,
"mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}": types.DeleteServiceAccountTokenOpDocument,
"mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}": types.UpdateServiceAccountHandlerKeysDocument,
"mutation CreateNewAWSSecretsSync($envId: ID!, $path: String!, $credentialId: ID!, $secretName: String!, $kmsId: String) {\n createAwsSecretSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n secretName: $secretName\n kmsId: $kmsId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewAwsSecretsSyncDocument,
"mutation CreateNewCfPagesSync($envId: ID!, $path: String!, $projectName: String!, $deploymentId: ID!, $projectEnv: String!, $credentialId: ID!) {\n createCloudflarePagesSync(\n envId: $envId\n path: $path\n projectName: $projectName\n deploymentId: $deploymentId\n projectEnv: $projectEnv\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewCfPagesSyncDocument,
"mutation DeleteProviderCreds($credentialId: ID!) {\n deleteProviderCredentials(credentialId: $credentialId) {\n ok\n }\n}": types.DeleteProviderCredsDocument,
Expand Down Expand Up @@ -294,6 +295,10 @@ export function graphql(source: "mutation DeleteServiceAccountOp($id: ID!) {\n
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"): (typeof documents)["mutation DeleteServiceAccountTokenOp($id: ID!) {\n deleteServiceAccountToken(tokenId: $id) {\n ok\n }\n}"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}"): (typeof documents)["mutation UpdateServiceAccountHandlerKeys($orgId: ID!, $handlers: [ServiceAccountHandlerInput]) {\n updateServiceAccountHandlers(organisationId: $orgId, handlers: $handlers) {\n ok\n }\n}"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading

0 comments on commit 2bb3c27

Please sign in to comment.