Skip to content

Commit

Permalink
Merge pull request #139 from Ilhasoft/develop
Browse files Browse the repository at this point in the history
Version 1.11.0
  • Loading branch information
Douglas Paz committed Jul 2, 2018
2 parents 1ead71e + d036700 commit 02a29de
Show file tree
Hide file tree
Showing 9 changed files with 392 additions and 8 deletions.
7 changes: 7 additions & 0 deletions bothub/api/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
from .views import Categories
from .views import RepositoriesViewSet
from .views import TranslationsViewSet
from .views import RepositoryAuthorizationViewSet
from .views import RepositoryAuthorizationRoleViewSet
from .views import SearchUserViewSet


class Router(routers.SimpleRouter):
Expand Down Expand Up @@ -110,3 +113,7 @@ def get_lookup_regex(self, viewset, lookup_prefix=''):
router.register('categories', Categories)
router.register('repositories', RepositoriesViewSet)
router.register('translations', TranslationsViewSet)
router.register('authorizations', RepositoryAuthorizationViewSet)
router.register('authorization-role',
RepositoryAuthorizationRoleViewSet)
router.register('search-user', SearchUserViewSet)
1 change: 1 addition & 0 deletions bothub/api/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
AnalyzeTextSerializer,
EditRepositorySerializer,
VoteSerializer,
RepositoryAuthorizationRoleSerializer,
)

from .category import ( # noqa: F401
Expand Down
22 changes: 22 additions & 0 deletions bothub/api/serializers/repository.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -101,7 +103,9 @@ class Meta:
fields = [
'uuid',
'user',
'user__nickname',
'repository',
'role',
'level',
'can_read',
'can_contribute',
Expand All @@ -110,6 +114,11 @@ class Meta:
'created_at',
]

user__nickname = serializers.SlugRelatedField(
source='user',
slug_field='nickname',
read_only=True)


class AnalyzeTextSerializer(serializers.Serializer):
language = serializers.ChoiceField(LANGUAGE_CHOICES, required=True)
Expand All @@ -122,3 +131,16 @@ class Meta:
fields = [
'vote',
]


class RepositoryAuthorizationRoleSerializer(serializers.ModelSerializer):
class Meta:
model = RepositoryAuthorization
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
1 change: 0 additions & 1 deletion bothub/api/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class Meta:
model = User
fields = [
'nickname',
'email',
'name',
'locale',
]
Expand Down
145 changes: 145 additions & 0 deletions bothub/api/tests/test_authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 RepositoryAuthorizationRoleViewSet

from .utils import create_user_and_token

Expand Down Expand Up @@ -90,3 +94,144 @@ 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/authorization-role/{}/{}/'.format(
repository.uuid, user.nickname),
self.factory._encode_data(data, MULTIPART_CONTENT),
MULTIPART_CONTENT,
**authorization_header)
view = RepositoryAuthorizationRoleViewSet.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)

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)
99 changes: 99 additions & 0 deletions bothub/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,6 +46,7 @@
from .serializers import EditRepositorySerializer
from .serializers import NewRepositoryTranslatedExampleSerializer
from .serializers import VoteSerializer
from .serializers import RepositoryAuthorizationRoleSerializer


# Permisions
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -753,3 +786,69 @@ 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_SETTED)
serializer_class = RepositoryAuthorizationSerializer
filter_class = RepositoryAuthorizationFilter
permission_classes = [
IsAuthenticated,
]


class RepositoryAuthorizationRoleViewSet(
MultipleFieldLookupMixin,
mixins.UpdateModelMixin,
GenericViewSet):
queryset = RepositoryAuthorization.objects.exclude(
role=RepositoryAuthorization.ROLE_NOT_SETTED)
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


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)
18 changes: 18 additions & 0 deletions bothub/common/migrations/0011_repositoryauthorization_role.py
Original file line number Diff line number Diff line change
@@ -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'),
),
]
Loading

0 comments on commit 02a29de

Please sign in to comment.