diff --git a/config/api_urls.py b/config/api_urls.py index 5519cc63ba0ada49308626c23a84af56a5eba24c..4ed81d284f10c34426f50e778683c6fc0cdcddf5 100644 --- a/config/api_urls.py +++ b/config/api_urls.py @@ -20,6 +20,7 @@ urlpatterns += [ url(r'^search$', views.Search.as_view(), name='search'), url(r'^radios/', include('funkwhale_api.radios.urls', namespace='radios')), url(r'^history/', include('funkwhale_api.history.urls', namespace='history')), + url(r'^users/', include('funkwhale_api.users.api_urls', namespace='users')), url(r'^token/', 'rest_framework_jwt.views.obtain_jwt_token'), url(r'^token/refresh/', 'rest_framework_jwt.views.refresh_jwt_token'), ] diff --git a/funkwhale_api/users/api_urls.py b/funkwhale_api/users/api_urls.py new file mode 100644 index 0000000000000000000000000000000000000000..8aba7f1a8b2777e970a260f2a4dd80805214e4e1 --- /dev/null +++ b/funkwhale_api/users/api_urls.py @@ -0,0 +1,7 @@ +from rest_framework import routers +from . import views + +router = routers.SimpleRouter() +router.register(r'users', views.UserViewSet, 'users') + +urlpatterns = router.urls diff --git a/funkwhale_api/users/models.py b/funkwhale_api/users/models.py index 447bc077b2a309b111a73151ac7804096fcde85b..1abbbb51ffdb5ee99d50681a82d145796638adb6 100644 --- a/funkwhale_api/users/models.py +++ b/funkwhale_api/users/models.py @@ -15,6 +15,14 @@ class User(AbstractUser): # around the globe. name = models.CharField(_("Name of User"), blank=True, max_length=255) + # permissions that are used for API access and that worth serializing + relevant_permissions = { + # internal_codename : {external_codename} + 'music.add_importbatch': { + 'external_codename': 'import.launch' + } + } + def __str__(self): return self.username diff --git a/funkwhale_api/users/serializers.py b/funkwhale_api/users/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..0bc5c5fa7de3b69d2a43d9c4b897690a68959695 --- /dev/null +++ b/funkwhale_api/users/serializers.py @@ -0,0 +1,28 @@ +from rest_framework import serializers + +from . import models + + +class UserSerializer(serializers.ModelSerializer): + + permissions = serializers.SerializerMethodField() + + class Meta: + model = models.User + fields = [ + 'id', + 'username', + 'name', + 'email', + 'is_staff', + 'is_superuser', + 'permissions', + ] + + def get_permissions(self, o): + perms = {} + for internal_codename, conf in o.relevant_permissions.items(): + perms[conf['external_codename']] = { + 'status': o.has_perm(internal_codename) + } + return perms diff --git a/funkwhale_api/users/tests/factories.py b/funkwhale_api/users/tests/factories.py index e2c967de77dda63b15bbdf88dd00bbcba5d6d1b5..351884ff4543a6625cb24dba904ff6c2b415bae3 100644 --- a/funkwhale_api/users/tests/factories.py +++ b/funkwhale_api/users/tests/factories.py @@ -1,5 +1,7 @@ import factory +from django.contrib.auth.models import Permission + class UserFactory(factory.django.DjangoModelFactory): username = factory.Sequence(lambda n: 'user-{0}'.format(n)) @@ -9,3 +11,20 @@ class UserFactory(factory.django.DjangoModelFactory): class Meta: model = 'users.User' django_get_or_create = ('username', ) + + @factory.post_generation + def perms(self, create, extracted, **kwargs): + if not create: + # Simple build, do nothing. + return + + if extracted: + perms = [ + Permission.objects.get( + content_type__app_label=p.split('.')[0], + codename=p.split('.')[1], + ) + for p in extracted + ] + # A list of permissions were passed in, use them + self.user_permissions.add(*perms) diff --git a/funkwhale_api/users/tests/test_views.py b/funkwhale_api/users/tests/test_views.py index 3840d73ac1027c15c5077f0f29fa0e2e73ef5981..5f8233bc6c74ecd3a128b7a3d498f864652b4738 100644 --- a/funkwhale_api/users/tests/test_views.py +++ b/funkwhale_api/users/tests/test_views.py @@ -1,8 +1,12 @@ +import json + from django.test import RequestFactory from test_plus.test import TestCase from funkwhale_api.users.models import User +from . factories import UserFactory + class UserTestCase(TestCase): @@ -36,3 +40,26 @@ class UserTestCase(TestCase): with self.settings(REGISTRATION_MODE="disabled"): response = self.client.post(url, data) self.assertEqual(response.status_code, 403) + + def test_can_fetch_data_from_api(self): + url = self.reverse('api:users:users-me') + response = self.client.get(url) + # login required + self.assertEqual(response.status_code, 401) + + user = UserFactory(is_staff=True, perms=['music.add_importbatch']) + self.assertTrue(user.has_perm('music.add_importbatch')) + self.login(user) + + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + payload = json.loads(response.content.decode('utf-8')) + + self.assertEqual(payload['username'], user.username) + self.assertEqual(payload['is_staff'], user.is_staff) + self.assertEqual(payload['is_superuser'], user.is_superuser) + self.assertEqual(payload['email'], user.email) + self.assertEqual(payload['name'], user.name) + self.assertEqual( + payload['permissions']['import.launch']['status'], True) diff --git a/funkwhale_api/users/views.py b/funkwhale_api/users/views.py index 5a7965a8f07a4bcf219b3d3dc0a37cc3c48eea6f..b7c1df28f9dba34739756c7c4b666b8cdca88929 100644 --- a/funkwhale_api/users/views.py +++ b/funkwhale_api/users/views.py @@ -1,7 +1,13 @@ from rest_framework.response import Response +from rest_framework import viewsets +from rest_framework.decorators import list_route + from rest_auth.registration.views import RegisterView as BaseRegisterView from allauth.account.adapter import get_adapter +from . import models +from . import serializers + class RegisterView(BaseRegisterView): @@ -15,3 +21,14 @@ class RegisterView(BaseRegisterView): def is_open_for_signup(self, request): return get_adapter().is_open_for_signup(request) + + +class UserViewSet(viewsets.GenericViewSet): + queryset = models.User.objects.all() + serializer_class = serializers.UserSerializer + + @list_route(methods=['get']) + def me(self, request, *args, **kwargs): + """Return information about the current user""" + serializer = self.serializer_class(request.user) + return Response(serializer.data)