From dc75273e5e0a32359e580b1007d39a6bda9868f4 Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Thu, 21 Jun 2018 09:29:20 -0300 Subject: [PATCH 1/7] Add role to RepositoryAuthorization --- .../0011_repositoryauthorization_role.py | 18 +++++++ bothub/common/models.py | 40 +++++++++++++-- bothub/common/tests.py | 50 +++++++++++++++++++ 3 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 bothub/common/migrations/0011_repositoryauthorization_role.py diff --git a/bothub/common/migrations/0011_repositoryauthorization_role.py b/bothub/common/migrations/0011_repositoryauthorization_role.py new file mode 100644 index 000000000..fd96afc8d --- /dev/null +++ b/bothub/common/migrations/0011_repositoryauthorization_role.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.6 on 2018-06-21 12:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0010_auto_20180611_1123'), + ] + + operations = [ + migrations.AddField( + model_name='repositoryauthorization', + name='role', + field=models.PositiveIntegerField(choices=[(0, 'not set'), (1, 'user'), (2, 'contributor'), (3, 'admin')], default=0, verbose_name='role'), + ), + ] diff --git a/bothub/common/models.py b/bothub/common/models.py index dba53179d..4a44f6f8a 100644 --- a/bothub/common/models.py +++ b/bothub/common/models.py @@ -584,7 +584,20 @@ class Meta: LEVEL_NOTHING = 0 LEVEL_READER = 1 - LEVEL_ADMIN = 2 + LEVEL_CONTRIBUTOR = 2 + LEVEL_ADMIN = 3 + + ROLE_NOT_SET = 0 + ROLE_USER = 1 + ROLE_CONTRIBUTOR = 2 + ROLE_ADMIN = 3 + + ROLE_CHOICES = [ + (ROLE_NOT_SET, _('not set')), + (ROLE_USER, _('user')), + (ROLE_CONTRIBUTOR, _('contributor')), + (ROLE_ADMIN, _('admin')), + ] uuid = models.UUIDField( _('UUID'), @@ -597,6 +610,10 @@ class Meta: repository = models.ForeignKey( Repository, models.CASCADE) + role = models.PositiveIntegerField( + _('role'), + choices=ROLE_CHOICES, + default=ROLE_NOT_SET) created_at = models.DateTimeField( _('created at'), auto_now_add=True) @@ -610,20 +627,35 @@ def level(self): if user and self.repository.owner == user: return RepositoryAuthorization.LEVEL_ADMIN - if self.repository.is_private: - return RepositoryAuthorization.LEVEL_NOTHING - return RepositoryAuthorization.LEVEL_READER + + if self.role == RepositoryAuthorization.ROLE_NOT_SET: + if self.repository.is_private: + return RepositoryAuthorization.LEVEL_NOTHING + return RepositoryAuthorization.LEVEL_READER + + if self.role == RepositoryAuthorization.ROLE_USER: + return RepositoryAuthorization.LEVEL_READER + + if self.role == RepositoryAuthorization.ROLE_CONTRIBUTOR: + return RepositoryAuthorization.LEVEL_CONTRIBUTOR + + if self.role == RepositoryAuthorization.ROLE_ADMIN: + return RepositoryAuthorization.LEVEL_ADMIN + + return RepositoryAuthorization.LEVEL_NOTHING @property def can_read(self): return self.level in [ RepositoryAuthorization.LEVEL_READER, + RepositoryAuthorization.LEVEL_CONTRIBUTOR, RepositoryAuthorization.LEVEL_ADMIN, ] @property def can_contribute(self): return self.level in [ + RepositoryAuthorization.LEVEL_CONTRIBUTOR, RepositoryAuthorization.LEVEL_ADMIN, ] diff --git a/bothub/common/tests.py b/bothub/common/tests.py index c2f1fc8f0..54adbf188 100644 --- a/bothub/common/tests.py +++ b/bothub/common/tests.py @@ -418,6 +418,56 @@ def test_is_admin(self): .get_user_authorization(self.user) self.assertFalse(private_authorization_user.is_admin) + def test_owner_ever_admin(self): + authorization_owner = self.repository.get_user_authorization( + self.owner) + self.assertTrue(authorization_owner.is_admin) + + def test_role_user_can_read(self): + # public repository + authorization_user = self.repository.get_user_authorization( + self.user) + authorization_user.role = RepositoryAuthorization.ROLE_USER + authorization_user.save() + self.assertTrue(authorization_user.can_read) + + # private repository + authorization_user = self.private_repository.get_user_authorization( + self.user) + authorization_user.role = RepositoryAuthorization.ROLE_USER + authorization_user.save() + self.assertTrue(authorization_user.can_read) + + def test_role_user_can_t_contribute(self): + # public repository + authorization_user = self.repository.get_user_authorization( + self.user) + authorization_user.role = RepositoryAuthorization.ROLE_USER + authorization_user.save() + self.assertFalse(authorization_user.can_contribute) + + # private repository + authorization_user = self.private_repository.get_user_authorization( + self.user) + authorization_user.role = RepositoryAuthorization.ROLE_USER + authorization_user.save() + self.assertFalse(authorization_user.can_contribute) + + def test_role_contributor_can_contribute(self): + # public repository + authorization_user = self.repository.get_user_authorization( + self.user) + authorization_user.role = RepositoryAuthorization.ROLE_CONTRIBUTOR + authorization_user.save() + self.assertTrue(authorization_user.can_contribute) + + # private repository + authorization_user = self.private_repository.get_user_authorization( + self.user) + authorization_user.role = RepositoryAuthorization.ROLE_CONTRIBUTOR + authorization_user.save() + self.assertTrue(authorization_user.can_contribute) + class RepositoryUpdateTrainingTestCase(TestCase): def setUp(self): From 188fa8f389ad66d1d3f589bf6115b9609ab68ea7 Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Thu, 21 Jun 2018 13:52:24 -0300 Subject: [PATCH 2/7] Add list authorizations and update authorization role routes --- bothub/api/routers.py | 5 + bothub/api/serializers/__init__.py | 1 + bothub/api/serializers/repository.py | 9 ++ bothub/api/tests/test_authorization.py | 132 +++++++++++++++++++++++++ bothub/api/views.py | 72 ++++++++++++++ 5 files changed, 219 insertions(+) diff --git a/bothub/api/routers.py b/bothub/api/routers.py index 07cc81235..9437cd68d 100644 --- a/bothub/api/routers.py +++ b/bothub/api/routers.py @@ -20,6 +20,8 @@ from .views import Categories from .views import RepositoriesViewSet from .views import TranslationsViewSet +from .views import RepositoryAuthorizationViewSet +from .views import RepositoryAuthorizationSetRoleViewSet class Router(routers.SimpleRouter): @@ -110,3 +112,6 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('categories', Categories) router.register('repositories', RepositoriesViewSet) router.register('translations', TranslationsViewSet) +router.register('list-authorizations', RepositoryAuthorizationViewSet) +router.register('update-authorization-role', + RepositoryAuthorizationSetRoleViewSet) diff --git a/bothub/api/serializers/__init__.py b/bothub/api/serializers/__init__.py index 7d49f7239..eb88b23e0 100644 --- a/bothub/api/serializers/__init__.py +++ b/bothub/api/serializers/__init__.py @@ -5,6 +5,7 @@ AnalyzeTextSerializer, EditRepositorySerializer, VoteSerializer, + RepositoryAuthorizationRoleSerializer, ) from .category import ( # noqa: F401 diff --git a/bothub/api/serializers/repository.py b/bothub/api/serializers/repository.py index 94e6cb3e8..48599a373 100644 --- a/bothub/api/serializers/repository.py +++ b/bothub/api/serializers/repository.py @@ -100,6 +100,7 @@ class Meta: 'uuid', 'user', 'repository', + 'role', 'level', 'can_read', 'can_contribute', @@ -120,3 +121,11 @@ class Meta: fields = [ 'vote', ] + + +class RepositoryAuthorizationRoleSerializer(serializers.ModelSerializer): + class Meta: + model = RepositoryAuthorization + fields = [ + 'role', + ] diff --git a/bothub/api/tests/test_authorization.py b/bothub/api/tests/test_authorization.py index 2c20111de..81d7ec69c 100644 --- a/bothub/api/tests/test_authorization.py +++ b/bothub/api/tests/test_authorization.py @@ -2,12 +2,16 @@ from django.test import TestCase from django.test import RequestFactory +from django.test.client import MULTIPART_CONTENT from rest_framework import status from bothub.common import languages from bothub.common.models import Repository +from bothub.common.models import RepositoryAuthorization from ..views import RepositoryViewSet +from ..views import RepositoryAuthorizationViewSet +from ..views import RepositoryAuthorizationSetRoleViewSet from .utils import create_user_and_token @@ -90,3 +94,131 @@ def test_user_forbidden_private(self): self.assertEqual( response.status_code, status.HTTP_403_FORBIDDEN) + + +class ListAuthorizationTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + self.owner, self.owner_token = create_user_and_token('owner') + self.user, self.user_token = create_user_and_token() + + self.repository = Repository.objects.create( + owner=self.owner, + name='Testing', + slug='test', + language=languages.LANGUAGE_EN) + + self.user_auth = self.repository.get_user_authorization(self.user) + self.user_auth.role = RepositoryAuthorization.ROLE_CONTRIBUTOR + self.user_auth.save() + + def request(self, repository, token): + authorization_header = { + 'HTTP_AUTHORIZATION': 'Token {}'.format(token.key), + } + request = self.factory.get( + '/api/list-authorizations/', + { + 'repository': repository.uuid, + }, + **authorization_header) + response = RepositoryAuthorizationViewSet.as_view( + {'get': 'list'})(request) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + response, content_data = self.request( + self.repository, + self.owner_token) + + self.assertEqual( + response.status_code, + status.HTTP_200_OK) + + self.assertEqual( + content_data.get('count'), + 1) + + self.assertEqual( + content_data.get('results')[0].get('user'), + self.user.id) + + def test_user_forbidden(self): + response, content_data = self.request( + self.repository, + self.user_token) + + self.assertEqual( + response.status_code, + status.HTTP_403_FORBIDDEN) + + +class UpdateAuthorizationRoleTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + self.owner, self.owner_token = create_user_and_token('owner') + self.user, self.user_token = create_user_and_token() + + self.repository = Repository.objects.create( + owner=self.owner, + name='Testing', + slug='test', + language=languages.LANGUAGE_EN) + + def request(self, repository, token, user, data): + authorization_header = { + 'HTTP_AUTHORIZATION': 'Token {}'.format(token.key), + } + request = self.factory.patch( + '/api/update-authorization-role/{}/{}/'.format( + repository.uuid, user.nickname), + self.factory._encode_data(data, MULTIPART_CONTENT), + MULTIPART_CONTENT, + **authorization_header) + view = RepositoryAuthorizationSetRoleViewSet.as_view( + {'patch': 'update'}) + response = view( + request, + repository__uuid=repository.uuid, + user__nickname=user.nickname) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + response, content_data = self.request( + self.repository, + self.owner_token, + self.user, + { + 'role': RepositoryAuthorization.ROLE_CONTRIBUTOR, + }) + + self.assertEqual( + response.status_code, + status.HTTP_200_OK) + self.assertEqual( + content_data.get('role'), + RepositoryAuthorization.ROLE_CONTRIBUTOR) + + user_authorization = self.repository.get_user_authorization(self.user) + self.assertEqual( + user_authorization.role, + RepositoryAuthorization.ROLE_CONTRIBUTOR) + + def test_forbidden(self): + response, content_data = self.request( + self.repository, + self.user_token, + self.user, + { + 'role': RepositoryAuthorization.ROLE_CONTRIBUTOR, + }) + + self.assertEqual( + response.status_code, + status.HTTP_403_FORBIDDEN) diff --git a/bothub/api/views.py b/bothub/api/views.py index 9bb78d704..c00703227 100644 --- a/bothub/api/views.py +++ b/bothub/api/views.py @@ -25,6 +25,7 @@ from bothub.common.models import RepositoryTranslatedExampleEntity from bothub.common.models import RepositoryCategory from bothub.common.models import RepositoryVote +from bothub.common.models import RepositoryAuthorization from bothub.authentication.models import User from .serializers import RepositorySerializer @@ -45,6 +46,7 @@ from .serializers import EditRepositorySerializer from .serializers import NewRepositoryTranslatedExampleSerializer from .serializers import VoteSerializer +from .serializers import RepositoryAuthorizationRoleSerializer # Permisions @@ -94,6 +96,12 @@ def has_object_permission(self, request, view, obj): return authorization.can_contribute +class RepositoryAdminManagerAuthorization(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + authorization = obj.repository.get_user_authorization(request.user) + return authorization.is_admin + + # Filters class ExamplesFilter(filters.FilterSet): @@ -222,6 +230,31 @@ def filter_to_language(self, queryset, name, value): return queryset.filter(language=value) +class RepositoryAuthorizationFilter(filters.FilterSet): + class Meta: + model = RepositoryAuthorization + fields = ['repository'] + + repository = filters.CharFilter( + name='repository', + method='filter_repository_uuid', + help_text=_('Repository\'s UUID')) + + def filter_repository_uuid(self, queryset, name, value): + request = self.request + try: + repository = Repository.objects.get(uuid=value) + authorization = repository.get_user_authorization(request.user) + if not authorization.is_admin: + raise PermissionDenied() + return queryset.filter(repository=repository) + except Repository.DoesNotExist: + raise NotFound( + _('Repository {} does not exist').format(value)) + except DjangoValidationError: + raise NotFound(_('Invalid repository UUID')) + + # Mixins class MultipleFieldLookupMixin(object): @@ -753,3 +786,42 @@ class TranslationsViewSet( serializer_class = RepositoryTranslatedExampleSerializer queryset = RepositoryTranslatedExample.objects.all() filter_class = TranslationsFilter + + +class RepositoryAuthorizationViewSet( + mixins.ListModelMixin, + GenericViewSet): + queryset = RepositoryAuthorization.objects.exclude( + role=RepositoryAuthorization.ROLE_NOT_SET) + serializer_class = RepositoryAuthorizationSerializer + filter_class = RepositoryAuthorizationFilter + permission_classes = [ + IsAuthenticated, + ] + + +class RepositoryAuthorizationSetRoleViewSet( + MultipleFieldLookupMixin, + mixins.UpdateModelMixin, + GenericViewSet): + queryset = RepositoryAuthorization.objects.exclude( + role=RepositoryAuthorization.ROLE_NOT_SET) + lookup_field = 'user__nickname' + lookup_fields = ['repository__uuid', 'user__nickname'] + serializer_class = RepositoryAuthorizationRoleSerializer + permission_classes = [ + IsAuthenticated, + RepositoryAdminManagerAuthorization, + ] + + def get_object(self): + repository_uuid = self.kwargs.get('repository__uuid') + user_nickname = self.kwargs.get('user__nickname') + + repository = get_object_or_404(Repository, uuid=repository_uuid) + user = get_object_or_404(User, nickname=user_nickname) + + obj = repository.get_user_authorization(user) + + self.check_object_permissions(self.request, obj) + return obj From b2676e9b45652d39a8061afb70146b94ad097f13 Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Fri, 22 Jun 2018 11:41:19 -0300 Subject: [PATCH 3/7] Rename ROLE_NOT_SET to ROLE_NOT_SETTED --- bothub/common/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bothub/common/models.py b/bothub/common/models.py index 4a44f6f8a..c7e06da17 100644 --- a/bothub/common/models.py +++ b/bothub/common/models.py @@ -587,13 +587,13 @@ class Meta: LEVEL_CONTRIBUTOR = 2 LEVEL_ADMIN = 3 - ROLE_NOT_SET = 0 + ROLE_NOT_SETTED = 0 ROLE_USER = 1 ROLE_CONTRIBUTOR = 2 ROLE_ADMIN = 3 ROLE_CHOICES = [ - (ROLE_NOT_SET, _('not set')), + (ROLE_NOT_SETTED, _('not set')), (ROLE_USER, _('user')), (ROLE_CONTRIBUTOR, _('contributor')), (ROLE_ADMIN, _('admin')), @@ -613,7 +613,7 @@ class Meta: role = models.PositiveIntegerField( _('role'), choices=ROLE_CHOICES, - default=ROLE_NOT_SET) + default=ROLE_NOT_SETTED) created_at = models.DateTimeField( _('created at'), auto_now_add=True) @@ -628,7 +628,7 @@ def level(self): if user and self.repository.owner == user: return RepositoryAuthorization.LEVEL_ADMIN - if self.role == RepositoryAuthorization.ROLE_NOT_SET: + if self.role == RepositoryAuthorization.ROLE_NOT_SETTED: if self.repository.is_private: return RepositoryAuthorization.LEVEL_NOTHING return RepositoryAuthorization.LEVEL_READER From c197dee074dbd46585e4a337e27a8cefa96f6fad Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Fri, 22 Jun 2018 12:18:15 -0300 Subject: [PATCH 4/7] Add search user route --- bothub/api/routers.py | 2 ++ bothub/api/serializers/user.py | 1 - bothub/api/views.py | 31 +++++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/bothub/api/routers.py b/bothub/api/routers.py index 9437cd68d..27eab3d43 100644 --- a/bothub/api/routers.py +++ b/bothub/api/routers.py @@ -22,6 +22,7 @@ from .views import TranslationsViewSet from .views import RepositoryAuthorizationViewSet from .views import RepositoryAuthorizationSetRoleViewSet +from .views import SearchUserViewSet class Router(routers.SimpleRouter): @@ -115,3 +116,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('list-authorizations', RepositoryAuthorizationViewSet) router.register('update-authorization-role', RepositoryAuthorizationSetRoleViewSet) +router.register('search-user', SearchUserViewSet) diff --git a/bothub/api/serializers/user.py b/bothub/api/serializers/user.py index a0381cf18..7032fdcaa 100644 --- a/bothub/api/serializers/user.py +++ b/bothub/api/serializers/user.py @@ -35,7 +35,6 @@ class Meta: model = User fields = [ 'nickname', - 'email', 'name', 'locale', ] diff --git a/bothub/api/views.py b/bothub/api/views.py index c00703227..effedbba0 100644 --- a/bothub/api/views.py +++ b/bothub/api/views.py @@ -792,7 +792,7 @@ class RepositoryAuthorizationViewSet( mixins.ListModelMixin, GenericViewSet): queryset = RepositoryAuthorization.objects.exclude( - role=RepositoryAuthorization.ROLE_NOT_SET) + role=RepositoryAuthorization.ROLE_NOT_SETTED) serializer_class = RepositoryAuthorizationSerializer filter_class = RepositoryAuthorizationFilter permission_classes = [ @@ -805,7 +805,7 @@ class RepositoryAuthorizationSetRoleViewSet( mixins.UpdateModelMixin, GenericViewSet): queryset = RepositoryAuthorization.objects.exclude( - role=RepositoryAuthorization.ROLE_NOT_SET) + role=RepositoryAuthorization.ROLE_NOT_SETTED) lookup_field = 'user__nickname' lookup_fields = ['repository__uuid', 'user__nickname'] serializer_class = RepositoryAuthorizationRoleSerializer @@ -825,3 +825,30 @@ def get_object(self): self.check_object_permissions(self.request, obj) return obj + + +class SearchUserViewSet( + mixins.ListModelMixin, + GenericViewSet): + serializer_class = UserSerializer + queryset = User.objects.all() + filter_backends = [ + DjangoFilterBackend, + SearchFilter, + ] + search_fields = [ + '=name', + '^name', + '$name', + '=nickname', + '^nickname', + '$nickname', + '=email', + ] + pagination_class = None + limit = 5 + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset())[:self.limit] + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) From 24d7929ec8a2f97cc12aee32cfafe46f394608b8 Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Fri, 22 Jun 2018 12:21:55 -0300 Subject: [PATCH 5/7] Fix ROLE_NOT_SETTED --- bothub/api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bothub/api/views.py b/bothub/api/views.py index c00703227..1b3fe58aa 100644 --- a/bothub/api/views.py +++ b/bothub/api/views.py @@ -792,7 +792,7 @@ class RepositoryAuthorizationViewSet( mixins.ListModelMixin, GenericViewSet): queryset = RepositoryAuthorization.objects.exclude( - role=RepositoryAuthorization.ROLE_NOT_SET) + role=RepositoryAuthorization.ROLE_NOT_SETTED) serializer_class = RepositoryAuthorizationSerializer filter_class = RepositoryAuthorizationFilter permission_classes = [ @@ -805,7 +805,7 @@ class RepositoryAuthorizationSetRoleViewSet( mixins.UpdateModelMixin, GenericViewSet): queryset = RepositoryAuthorization.objects.exclude( - role=RepositoryAuthorization.ROLE_NOT_SET) + role=RepositoryAuthorization.ROLE_NOT_SETTED) lookup_field = 'user__nickname' lookup_fields = ['repository__uuid', 'user__nickname'] serializer_class = RepositoryAuthorizationRoleSerializer From d325d01d151c59e172057a3d9da7c2d449eee2d9 Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Tue, 26 Jun 2018 08:59:57 -0300 Subject: [PATCH 6/7] Rename route update-authorization-role to authorization-role --- bothub/api/routers.py | 6 +++--- bothub/api/tests/test_authorization.py | 6 +++--- bothub/api/views.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bothub/api/routers.py b/bothub/api/routers.py index 27eab3d43..5d956139a 100644 --- a/bothub/api/routers.py +++ b/bothub/api/routers.py @@ -21,7 +21,7 @@ from .views import RepositoriesViewSet from .views import TranslationsViewSet from .views import RepositoryAuthorizationViewSet -from .views import RepositoryAuthorizationSetRoleViewSet +from .views import RepositoryAuthorizationRoleViewSet from .views import SearchUserViewSet @@ -114,6 +114,6 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('repositories', RepositoriesViewSet) router.register('translations', TranslationsViewSet) router.register('list-authorizations', RepositoryAuthorizationViewSet) -router.register('update-authorization-role', - RepositoryAuthorizationSetRoleViewSet) +router.register('authorization-role', + RepositoryAuthorizationRoleViewSet) router.register('search-user', SearchUserViewSet) diff --git a/bothub/api/tests/test_authorization.py b/bothub/api/tests/test_authorization.py index 81d7ec69c..10802368c 100644 --- a/bothub/api/tests/test_authorization.py +++ b/bothub/api/tests/test_authorization.py @@ -11,7 +11,7 @@ from ..views import RepositoryViewSet from ..views import RepositoryAuthorizationViewSet -from ..views import RepositoryAuthorizationSetRoleViewSet +from ..views import RepositoryAuthorizationRoleViewSet from .utils import create_user_and_token @@ -174,12 +174,12 @@ def request(self, repository, token, user, data): 'HTTP_AUTHORIZATION': 'Token {}'.format(token.key), } request = self.factory.patch( - '/api/update-authorization-role/{}/{}/'.format( + '/api/authorization-role/{}/{}/'.format( repository.uuid, user.nickname), self.factory._encode_data(data, MULTIPART_CONTENT), MULTIPART_CONTENT, **authorization_header) - view = RepositoryAuthorizationSetRoleViewSet.as_view( + view = RepositoryAuthorizationRoleViewSet.as_view( {'patch': 'update'}) response = view( request, diff --git a/bothub/api/views.py b/bothub/api/views.py index effedbba0..46aa5946e 100644 --- a/bothub/api/views.py +++ b/bothub/api/views.py @@ -800,7 +800,7 @@ class RepositoryAuthorizationViewSet( ] -class RepositoryAuthorizationSetRoleViewSet( +class RepositoryAuthorizationRoleViewSet( MultipleFieldLookupMixin, mixins.UpdateModelMixin, GenericViewSet): From 816547e2875dbc0f6555af13fea69e01f859d9e6 Mon Sep 17 00:00:00 2001 From: Douglas Paz Date: Tue, 26 Jun 2018 14:50:31 -0300 Subject: [PATCH 7/7] Add intact owner role validator --- bothub/api/serializers/repository.py | 7 +++++++ bothub/api/tests/test_authorization.py | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/bothub/api/serializers/repository.py b/bothub/api/serializers/repository.py index 48599a373..d5b8886ff 100644 --- a/bothub/api/serializers/repository.py +++ b/bothub/api/serializers/repository.py @@ -1,4 +1,6 @@ from rest_framework import serializers +from rest_framework.exceptions import PermissionDenied +from django.utils.translation import gettext as _ from bothub.common.models import Repository from bothub.common.models import RepositoryCategory @@ -129,3 +131,8 @@ class Meta: fields = [ 'role', ] + + def validate(self, data): + if self.instance.user == self.instance.repository.owner: + raise PermissionDenied(_('The owner role can\'t be changed.')) + return data diff --git a/bothub/api/tests/test_authorization.py b/bothub/api/tests/test_authorization.py index 10802368c..c3ef7dcdc 100644 --- a/bothub/api/tests/test_authorization.py +++ b/bothub/api/tests/test_authorization.py @@ -222,3 +222,16 @@ def test_forbidden(self): self.assertEqual( response.status_code, status.HTTP_403_FORBIDDEN) + + def test_owner_can_t_set_your_role(self): + response, content_data = self.request( + self.repository, + self.owner_token, + self.owner, + { + 'role': RepositoryAuthorization.ROLE_CONTRIBUTOR, + }) + + self.assertEqual( + response.status_code, + status.HTTP_403_FORBIDDEN)