Skip to content

Commit

Permalink
refactor: app access
Browse files Browse the repository at this point in the history
  • Loading branch information
rohan-chaturvedi committed Oct 26, 2024
1 parent 3b21285 commit 5e251ba
Show file tree
Hide file tree
Showing 24 changed files with 1,489 additions and 208 deletions.
97 changes: 72 additions & 25 deletions backend/backend/graphene/mutations/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
from api.utils.access.permissions import (
user_can_access_app,
user_has_permission,
user_is_admin,
user_is_org_member,
)
import graphene
from graphql import GraphQLError
from api.models import App, EnvironmentKey, Organisation, OrganisationMember, Role
from backend.graphene.types import AppType
from api.models import (
App,
EnvironmentKey,
Organisation,
OrganisationMember,
Role,
ServiceAccount,
)
from backend.graphene.types import AppType, MemberType
from django.conf import settings
from django.db.models import Q

Expand Down Expand Up @@ -160,37 +166,54 @@ class Arguments:
member_id = graphene.ID()
app_id = graphene.ID()
env_keys = graphene.List(EnvironmentKeyInput)
member_type = MemberType(required=False)

app = graphene.Field(AppType)

@classmethod
def mutate(cls, root, info, member_id, app_id, env_keys):
def mutate(
cls, root, info, member_id, app_id, env_keys, member_type=MemberType.USER
):
user = info.context.user
app = App.objects.get(id=app_id)

if member_type == MemberType.USER:
permission_key = "Members"
member = OrganisationMember.objects.get(id=member_id, deleted_at=None)
else:
permission_key = "ServiceAccounts"
member = ServiceAccount.objects.get(id=member_id, deleted_at=None)

if not user_has_permission(
info.context.user, "create", "Members", app.organisation, True
info.context.user, "create", permission_key, app.organisation, True
):
raise GraphQLError("You don't have permission to add members to this App")

if not user_can_access_app(user.userId, app.id):
raise GraphQLError("You don't have access to this app")

org_member = OrganisationMember.objects.get(id=member_id, deleted_at=None)

app.members.add(org_member)
if member_type == MemberType.USER:
app.members.add(member)
else:
app.service_accounts.add(member)

# Create new env keys
for key in env_keys:
EnvironmentKey.objects.update_or_create(
environment_id=key.env_id,
user_id=key.user_id,
defaults={
"wrapped_seed": key.wrapped_seed,
"wrapped_salt": key.wrapped_salt,
"identity_key": key.identity_key,
},
)
defaults = {
"wrapped_seed": key.wrapped_seed,
"wrapped_salt": key.wrapped_salt,
"identity_key": key.identity_key,
}

condition = {
"environment_id": key.env_id,
"user_id": key.user_id if member_type == MemberType.USER else None,
"service_account_id": (
key.user_id if member_type == MemberType.SERVICE else None
),
}

EnvironmentKey.objects.update_or_create(**condition, defaults=defaults)

return AddAppMemberMutation(app=app)

Expand All @@ -199,31 +222,55 @@ class RemoveAppMemberMutation(graphene.Mutation):
class Arguments:
member_id = graphene.ID()
app_id = graphene.ID()
member_type = MemberType(required=False) # Add member_type argument

app = graphene.Field(AppType)

@classmethod
def mutate(cls, root, info, member_id, app_id):
def mutate(cls, root, info, member_id, app_id, member_type=MemberType.USER):
user = info.context.user
app = App.objects.get(id=app_id)

if member_type == MemberType.USER:
permission_key = "Members"
else:
permission_key = "ServiceAccounts"

if not user_has_permission(
info.context.user, "delete", "Members", app.organisation, True
info.context.user, "delete", permission_key, app.organisation, True
):
raise GraphQLError(
"You don't have permission to remove members from this App"
f"You don't have permission to remove {permission_key} from this App"
)

if not user_can_access_app(user.userId, app.id):
raise GraphQLError("You don't have access to this app")

org_member = OrganisationMember.objects.get(id=member_id, deleted_at=None)
if org_member not in app.members.all():
raise GraphQLError("This user is not a member of this app")
else:
app.members.remove(org_member)
member = None
if member_type == MemberType.USER:
member = OrganisationMember.objects.get(id=member_id)
elif member_type == MemberType.SERVICE:
member = ServiceAccount.objects.get(id=member_id)

if not member:
raise GraphQLError("Invalid member type or ID")

if member_type == MemberType.USER:
if member not in app.members.all():
raise GraphQLError("This user is not a member of this app")

app.members.remove(member)
EnvironmentKey.objects.filter(
environment__app=app, user_id=member_id
).delete()

elif member_type == MemberType.SERVICE:
if member not in app.service_accounts.all():
raise GraphQLError("This service account is not a member of this app")

app.service_accounts.remove(member)
EnvironmentKey.objects.filter(
environment__app=app, service_account_id=member_id
).delete()

return RemoveAppMemberMutation(app=app)
66 changes: 46 additions & 20 deletions backend/backend/graphene/mutations/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
user_can_access_app,
user_can_access_environment,
user_has_permission,
user_is_admin,
user_is_org_member,
)
from api.utils.audit_logging import log_secret_event
from api.utils.secrets import normalize_path_string

from backend.quotas import can_add_environment, can_use_custom_envs
import graphene
from graphql import GraphQLError
Expand All @@ -28,6 +28,7 @@
SecretFolder,
SecretTag,
ServerEnvironmentKey,
ServiceAccount,
UserToken,
ServiceToken,
)
Expand All @@ -36,6 +37,7 @@
EnvironmentKeyType,
EnvironmentTokenType,
EnvironmentType,
MemberType,
PersonalSecretType,
SecretFolderType,
SecretTagType,
Expand Down Expand Up @@ -342,43 +344,67 @@ def mutate(
class UpdateMemberEnvScopeMutation(graphene.Mutation):
class Arguments:
member_id = graphene.ID()
member_type = MemberType(required=False)
app_id = graphene.ID()
env_keys = graphene.List(EnvironmentKeyInput)

app = graphene.Field(AppType)

@classmethod
def mutate(cls, root, info, member_id, app_id, env_keys):
def mutate(
cls, root, info, member_id, app_id, env_keys, member_type=MemberType.USER
):
user = info.context.user
app = App.objects.get(id=app_id)

if member_type == MemberType.USER:
permission_key = "Members"
else:
permission_key = "ServiceAccounts"

if not user_has_permission(
info.context.user, "update", "Members", app.organisation, True
info.context.user, "update", permission_key, app.organisation, True
):
raise GraphQLError("You don't have permission to update App member access")

if not user_can_access_app(user.userId, app.id):
raise GraphQLError("You don't have access to this app")

org_member = OrganisationMember.objects.get(id=member_id, deleted_at=None)
if org_member not in app.members.all():
raise GraphQLError("This user does not have access to this app")
else:
# delete all existing keys
EnvironmentKey.objects.filter(
environment__app=app, user_id=member_id
).delete()

# set new keys
for key in env_keys:
EnvironmentKey.objects.create(
environment_id=key.env_id,
user_id=key.user_id,
wrapped_seed=key.wrapped_seed,
wrapped_salt=key.wrapped_salt,
identity_key=key.identity_key,
key_to_delete_filter = {
"environment__app": app,
}

if member_type == MemberType.USER:
app_member = OrganisationMember.objects.get(id=member_id, deleted_at=None)
key_to_delete_filter["user_id"] = member_id
if app_member not in app.members.all():
raise GraphQLError("This user does not have access to this app")

elif member_type == MemberType.SERVICE:
app_member = ServiceAccount.objects.get(id=member_id)
key_to_delete_filter["service_account_id"] = member_id
if app_member not in app.service_accounts.all():
raise GraphQLError(
"This service account does not have access to this app"
)

# delete all existing keys for this member
EnvironmentKey.objects.filter(**key_to_delete_filter).delete()

# set new keys
for key in env_keys:

EnvironmentKey.objects.create(
environment_id=key.env_id,
user_id=key.user_id if member_type == MemberType.USER else None,
service_account_id=(
key.user_id if member_type == MemberType.SERVICE else None
),
wrapped_seed=key.wrapped_seed,
wrapped_salt=key.wrapped_salt,
identity_key=key.identity_key,
)

return UpdateMemberEnvScopeMutation(app=app)


Expand Down
29 changes: 28 additions & 1 deletion backend/backend/graphene/mutations/service_accounts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from backend.graphene.mutations.environment import EnvironmentKeyInput
import graphene
from graphql import GraphQLError
from api.models import (
Expand Down Expand Up @@ -109,6 +108,34 @@ def mutate(
)


class UpdateServiceAccountMutation(graphene.Mutation):
class Arguments:
service_account_id = graphene.ID()
name = graphene.String()
role_id = graphene.ID()

service_account = graphene.Field(ServiceAccountType)

@classmethod
def mutate(cls, root, info, service_account_id, name, role_id):
user = info.context.user
service_account = ServiceAccountToken.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"
)

role = Role.objects.get(id=role_id)
service_account.name = name
service_account.role = role
service_account.save()

return UpdateServiceAccountMutation(service_account=service_account)


class UpdateServiceAccountHandlersMutation(graphene.Mutation):
class Arguments:
service_account_id = graphene.ID()
Expand Down
24 changes: 22 additions & 2 deletions backend/backend/graphene/queries/service_accounts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from api.utils.access.permissions import user_has_permission, user_is_org_member
from api.models import Organisation, OrganisationMember, Role, ServiceAccount
from api.utils.access.permissions import (
user_can_access_app,
user_has_permission,
user_is_org_member,
)
from api.models import App, Organisation, OrganisationMember, Role, ServiceAccount
from .access import resolve_organisation_global_access_users
from django.db.models import Q
from graphql import GraphQLError
Expand Down Expand Up @@ -39,3 +43,19 @@ def resolve_service_account_handlers(root, info, org_id):
)

return members


def resolve_app_service_accounts(root, info, app_id):
app = App.objects.get(id=app_id)

if not user_has_permission(
info.context.user, "read", "ServiceAccounts", app.organisation, True
):
raise GraphQLError(
"You don't have permission to read service accounts in this App"
)

if not user_can_access_app(info.context.user.userId, app_id):
raise GraphQLError("You don't have access to this app")

return app.service_accounts.filter(deleted_at=None)
5 changes: 5 additions & 0 deletions backend/backend/graphene/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,11 @@ class Meta:
fields = "__all__"


class MemberType(graphene.Enum):
USER = "user"
SERVICE = "service"


class ServiceAccountType(DjangoObjectType):

third_party_auth_enabled = graphene.Boolean()
Expand Down
Loading

0 comments on commit 5e251ba

Please sign in to comment.