diff --git a/baseapp-profiles/baseapp_profiles/graphql/mutations.py b/baseapp-profiles/baseapp_profiles/graphql/mutations.py index fc89c54f..6e2b6781 100644 --- a/baseapp-profiles/baseapp_profiles/graphql/mutations.py +++ b/baseapp-profiles/baseapp_profiles/graphql/mutations.py @@ -13,6 +13,8 @@ from graphql.error import GraphQLError from rest_framework import serializers +from .object_types import ProfileRoleTypesEnum + Profile = swapper.load_model("baseapp_profiles", "Profile") app_label = Profile._meta.app_label ProfileUserRole = swapper.load_model("baseapp_profiles", "ProfileUserRole") @@ -120,6 +122,40 @@ def perform_mutate(cls, serializer, info): ) +class RoleUpdate(RelayMutation): + profile_user_role = graphene.Field(get_object_type_for_model(ProfileUserRole)) + + class Input: + profile_id = graphene.ID(required=True) + user_id = graphene.ID(required=True) + role_type = graphene.Field(ProfileRoleTypesEnum) + + @classmethod + @login_required + def mutate_and_get_payload(cls, root, info, **input): + user_id = input.get("user_id") + profile_id = input.get("profile_id") + role_type = input.get("role_type") + user_pk = get_pk_from_relay_id(user_id) + profile_pk = get_pk_from_relay_id(profile_id) + + try: + obj = ProfileUserRole.objects.get(user_id=user_pk, profile_id=profile_pk) + except ProfileUserRole.DoesNotExist: + raise GraphQLError(_("Role not found")) + + if not info.context.user.has_perm("baseapp_profiles.change_profileuserrole", obj.profile): + raise GraphQLError( + str(_("You don't have permission to perform this action")), + extensions={"code": "permission_required"}, + ) + + obj.role = role_type + obj.save() + + return RoleUpdate(profile_user_role=obj) + + class ProfileUpdate(SerializerMutation): profile = graphene.Field(get_object_type_for_model(Profile)) @@ -210,3 +246,4 @@ class ProfilesMutations(object): profile_create = ProfileCreate.Field() profile_update = ProfileUpdate.Field() profile_delete = ProfileDelete.Field() + profile_role_update = RoleUpdate.Field() diff --git a/baseapp-profiles/baseapp_profiles/permissions.py b/baseapp-profiles/baseapp_profiles/permissions.py index 1d7b32ec..29d2d1a0 100644 --- a/baseapp-profiles/baseapp_profiles/permissions.py +++ b/baseapp-profiles/baseapp_profiles/permissions.py @@ -1,6 +1,7 @@ import swapper from django.contrib.auth.backends import BaseBackend +ProfileUserRole = swapper.load_model("baseapp_profiles", "ProfileUserRole") Profile = swapper.load_model("baseapp_profiles", "Profile") @@ -45,3 +46,12 @@ def has_perm(self, user_obj, perm, obj=None): or user_obj.is_superuser or obj.members.filter(user_id=user_obj.id).exists() ) + + if perm == "baseapp_profiles.change_profileuserrole" and obj: + if isinstance(obj, Profile): + return ( + obj.owner_id == user_obj.id + or obj.members.filter( + user_id=user_obj.id, role=ProfileUserRole.ProfileRoles.ADMIN + ).exists() + ) diff --git a/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py b/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py index d784da00..b81bca33 100644 --- a/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py +++ b/baseapp-profiles/baseapp_profiles/tests/test_get_queries.py @@ -3,12 +3,12 @@ from baseapp_pages.tests.factories import URLPathFactory from django.contrib.contenttypes.models import ContentType -from ..models import ProfileUserRole from .factories import ProfileFactory, ProfileUserRoleFactory pytestmark = pytest.mark.django_db Profile = swapper.load_model("baseapp_profiles", "Profile") +ProfileUserRole = swapper.load_model("baseapp_profiles", "ProfileUserRole") GET_PROFILE_BY_PATH = """ query Profile($id: ID!) { diff --git a/baseapp-profiles/baseapp_profiles/tests/test_graphql_mutations_update.py b/baseapp-profiles/baseapp_profiles/tests/test_graphql_mutations_update.py index eedb81e6..8767b2a2 100644 --- a/baseapp-profiles/baseapp_profiles/tests/test_graphql_mutations_update.py +++ b/baseapp-profiles/baseapp_profiles/tests/test_graphql_mutations_update.py @@ -1,14 +1,16 @@ import pytest import swapper +from baseapp_core.tests.factories import UserFactory from baseapp_pages.tests.factories import URLPathFactory from django.contrib.auth.models import Permission from django.test.client import MULTIPART_CONTENT -from .factories import ProfileFactory +from .factories import ProfileFactory, ProfileUserRoleFactory pytestmark = pytest.mark.django_db Profile = swapper.load_model("baseapp_profiles", "Profile") +ProfileUserRole = swapper.load_model("baseapp_profiles", "ProfileUserRole") PROFILE_UPDATE_GRAPHQL = """ mutation ProfileUpdateMutation($input: ProfileUpdateInput!) { @@ -32,6 +34,22 @@ } """ +PROFILE_ROLE_UPDATE_GRAPHQL = """ +mutation ProfileRoleUpdateMutation($input: RoleUpdateInput!) { + profileRoleUpdate(input: $input) { + profileUserRole { + id + role + status + } + errors { + field + messages + } + } +} +""" + def test_anon_cant_update_profile(graphql_client): profile = ProfileFactory() @@ -199,3 +217,81 @@ def test_user_with_permission_can_update_profile(django_user_client, graphql_use assert content["data"]["profileUpdate"]["profile"]["biography"] == new_biography profile.refresh_from_db() assert profile.biography == new_biography + + +def test_user_profile_owner_can_update_role(django_user_client, graphql_user_client): + + perm = Permission.objects.get( + content_type__app_label="baseapp_profiles", codename="change_profileuserrole" + ) + + user = django_user_client.user + user_2 = UserFactory() + + user.user_permissions.add(perm) + profile = ProfileFactory(owner=user) + ProfileUserRoleFactory(profile=profile, user=user_2, role=ProfileUserRole.ProfileRoles.MANAGER) + + response = graphql_user_client( + PROFILE_ROLE_UPDATE_GRAPHQL, + variables={ + "input": {"userId": user_2.relay_id, "profileId": profile.relay_id, "roleType": "ADMIN"} + }, + ) + content = response.json() + + assert content["data"]["profileRoleUpdate"]["profileUserRole"]["role"] == "ADMIN" + profile.refresh_from_db() + + +def test_user_with_permission_can_update_role(django_user_client, graphql_user_client): + + perm = Permission.objects.get( + content_type__app_label="baseapp_profiles", codename="change_profileuserrole" + ) + + user = django_user_client.user + user.user_permissions.add(perm) + user_2 = UserFactory() + user_3 = UserFactory() + + profile = ProfileFactory(owner=user_2) + ProfileUserRoleFactory(profile=profile, user=user, role=ProfileUserRole.ProfileRoles.ADMIN) + ProfileUserRoleFactory(profile=profile, user=user_3, role=ProfileUserRole.ProfileRoles.MANAGER) + + response = graphql_user_client( + PROFILE_ROLE_UPDATE_GRAPHQL, + variables={ + "input": {"userId": user_3.relay_id, "profileId": profile.relay_id, "roleType": "ADMIN"} + }, + ) + content = response.json() + + assert content["data"]["profileRoleUpdate"]["profileUserRole"]["role"] == "ADMIN" + profile.refresh_from_db() + + +def test_user_without_permission_cant_update_role(django_user_client, graphql_user_client): + + perm = Permission.objects.get( + content_type__app_label="baseapp_profiles", codename="change_profileuserrole" + ) + + user = django_user_client.user + user.user_permissions.add(perm) + user_2 = UserFactory() + user_3 = UserFactory() + + profile = ProfileFactory(owner=user_2) + ProfileUserRoleFactory(profile=profile, user=user, role=ProfileUserRole.ProfileRoles.MANAGER) + ProfileUserRoleFactory(profile=profile, user=user_3, role=ProfileUserRole.ProfileRoles.MANAGER) + + response = graphql_user_client( + PROFILE_ROLE_UPDATE_GRAPHQL, + variables={ + "input": {"userId": user_3.relay_id, "profileId": profile.relay_id, "roleType": "ADMIN"} + }, + ) + content = response.json() + assert content["errors"][0]["message"] == "You don't have permission to perform this action" + profile.refresh_from_db() diff --git a/baseapp-profiles/setup.cfg b/baseapp-profiles/setup.cfg index d54be3e0..0fe64f92 100644 --- a/baseapp-profiles/setup.cfg +++ b/baseapp-profiles/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = baseapp_profiles -version = 0.3.5 +version = 0.3.6 description = BaseApp Profiles long_description = file: README.md long_description_content_type = text/markdown