Projet

Général

Profil

0003-api-add-a-user-profile-management-endpoint-58554.patch

Paul Marillonnet, 07 février 2022 17:31

Télécharger (10,3 ko)

Voir les différences:

Subject: [PATCH 3/3] api: add a user profile management endpoint (#58554)

 src/authentic2/api_urls.py  |   5 ++
 src/authentic2/api_views.py | 100 +++++++++++++++++++++++++++++++++++-
 tests/test_api.py           |  90 ++++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 1 deletion(-)
src/authentic2/api_urls.py
32 32
        api_views.role_membership,
33 33
        name='a2-api-role-member',
34 34
    ),
35
    url(
36
        r'^users/(?P<user_uuid>[\w+]*)/profiles/(?P<profile_type_slug>[^/]+)/$',
37
        api_views.user_profiles,
38
        name='a2-api-user-profiles',
39
    ),
35 40
    url(
36 41
        r'^roles/(?P<role_uuid>[\w+]*)/relationships/members/$',
37 42
        api_views.role_memberships,
src/authentic2/api_views.py
57 57
from . import api_mixins, app_settings, decorators, hooks
58 58
from .a2_rbac.models import OrganizationalUnit, Role
59 59
from .a2_rbac.utils import get_default_ou
60
from .custom_user.models import User
60
from .custom_user.models import Profile, ProfileType, User
61 61
from .journal_event_types import UserLogin, UserRegistration
62 62
from .models import Attribute, PasswordReset, Service
63 63
from .passwords import get_password_checker
......
953 953
roles_members = RolesMembersAPI.as_view({'get': 'list'})
954 954

  
955 955

  
956
class ProfileSerializer(serializers.ModelSerializer):
957
    data = serializers.JSONField(binary=False)
958

  
959
    def create(self, validated_data):
960
        return Profile(**validated_data)
961

  
962
    def update(self, validated_data):
963
        # not supported yet
964
        pass
965

  
966
    class Meta:
967
        model = Profile
968
        fields = ('data',)
969

  
970

  
971
class UserProfilesAPI(ExceptionHandlerMixin, APIView):
972
    permission_classes = (permissions.IsAuthenticated,)
973
    serializer_class = ProfileSerializer
974

  
975
    def initial(self, request, *args, **kwargs):
976
        super().initial(request, *args, **kwargs)
977
        User = get_user_model()
978
        self.profile_type = get_object_or_404(ProfileType, slug=kwargs['profile_type_slug'])
979
        self.user = get_object_or_404(User, uuid=kwargs['user_uuid'])
980

  
981
    def get(self, request, *args, **kwargs):
982
        profile = get_object_or_404(Profile, user=self.user, profile_type=self.profile_type)
983
        if not request.user.has_perm('custom_user.view_profile', obj=profile):
984
            raise PermissionDenied('User not allowed to view user profiles')
985
        return Response(self.serializer_class(profile).data)
986

  
987
    def post(self, request, *args, **kwargs):
988
        if not request.user.has_perm('custom_user.create_profile'):
989
            raise PermissionDenied('User not allowed to create user profiles')
990
        serializer = self.serializer_class(data=request.data)
991
        if not serializer.is_valid():
992
            response = {'data': [], 'err': 1, 'err_desc': serializer.errors}
993
            return Response(response, status.HTTP_400_BAD_REQUEST)
994
        try:
995
            profile = Profile.objects.get(user=self.user, profile_type=self.profile_type)
996
        except Profile.DoesNotExist:
997
            profile = serializer.save()
998
            profile.profile_type = self.profile_type
999
            profile.user = self.user
1000
            profile.save()  # fixme double db access
1001
            request.journal.record(
1002
                'user.profile.add',
1003
                user=request.user,
1004
                profile_type=self.profile_type,
1005
                object_user=self.user,
1006
                api=True,
1007
            )
1008
            return Response(
1009
                {'result': 1, 'detail': _('Profile successfully assigned to user')}, status=status.HTTP_200_OK
1010
            )
1011

  
1012
    def patch(self, request, *args, **kwargs):
1013
        serializer = self.serializer_class(data=request.data)
1014
        if not serializer.is_valid():
1015
            response = {'data': [], 'err': 1, 'err_desc': serializer.errors}
1016
            return Response(response, status.HTTP_400_BAD_REQUEST)
1017
        profile = get_object_or_404(Profile, user=self.user, profile_type=self.profile_type)
1018
        if not request.user.has_perm('custom_user.change_profile', obj=profile):
1019
            raise PermissionDenied('User not allowed to change user profiles')
1020
        profile.data = request.data.get('data', {})
1021
        profile.save()
1022
        request.journal.record(
1023
            'user.profile.update',
1024
            user=request.user,
1025
            profile_type=self.profile_type,
1026
            object_user=self.user,
1027
            api=True,
1028
        )
1029
        return Response({'result': 1, 'detail': _('Profile successfully updated')}, status=status.HTTP_200_OK)
1030

  
1031
    def put(self, request, *args, **kwargs):
1032
        return self.patch(request, *args, **kwargs)
1033

  
1034
    def delete(self, request, *args, **kwargs):
1035
        profile = get_object_or_404(Profile, user=self.user, profile_type=self.profile_type)
1036
        if not request.user.has_perm('custom_user.delete_profile', obj=profile):
1037
            raise PermissionDenied('User not allowed to delete user profiles')
1038
        request.journal.record(
1039
            'user.profile.delete',
1040
            user=request.user,
1041
            profile_type=self.profile_type,
1042
            object_user=self.user,
1043
            api=True,
1044
        )
1045
        profile.delete()
1046
        return Response(
1047
            {'result': 1, 'detail': _('Profile successfully removed from user')}, status=status.HTTP_200_OK
1048
        )
1049

  
1050

  
1051
user_profiles = UserProfilesAPI.as_view()
1052

  
1053

  
956 1054
class RoleMembershipAPI(ExceptionHandlerMixin, APIView):
957 1055
    permission_classes = (permissions.IsAuthenticated,)
958 1056

  
tests/test_api.py
36 36
from authentic2.a2_rbac.models import Role
37 37
from authentic2.a2_rbac.utils import get_default_ou
38 38
from authentic2.apps.journal.models import Event, EventType
39
from authentic2.custom_user.models import Profile, ProfileType
39 40
from authentic2.models import Attribute, AttributeValue, AuthorizedRole, PasswordReset, Service
40 41
from authentic2.utils.misc import good_next_url
41 42
from django_rbac.models import SEARCH_OP
......
2617 2618

  
2618 2619
    resp = app.get('/api/users/?limit=1')
2619 2620
    assert len(resp.json['results']) == 1
2621

  
2622

  
2623
def test_user_profile_get(app, superuser):
2624
    app.authorization = ('Basic', (superuser.username, superuser.username))
2625

  
2626
    user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
2627

  
2628
    # wrong type
2629
    resp = app.get('/api/users/%s/profiles/non-existant-slug/' % user.uuid, status=404)
2630

  
2631
    profile_type = ProfileType.objects.create(slug='one-type', name='One Type')
2632

  
2633
    # wrong user
2634
    resp = app.get('/api/users/1234/profiles/one-type/', status=404)
2635

  
2636
    Profile.objects.create(profile_type=profile_type, user=user, data={'foo': 'bar'})
2637
    resp = app.get('/api/users/%s/profiles/one-type/' % user.uuid)
2638
    assert resp.json == {'data': {'foo': 'bar'}}
2639

  
2640

  
2641
def test_user_profile_put(app, superuser):
2642
    app.authorization = ('Basic', (superuser.username, superuser.username))
2643

  
2644
    user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
2645

  
2646
    # wrong type
2647
    resp = app.put('/api/users/%s/profiles/non-existant-slug/' % user.uuid, status=404)
2648

  
2649
    profile_type = ProfileType.objects.create(slug='one-type', name='One Type')
2650

  
2651
    # wrong user
2652
    resp = app.put('/api/users/1234/profiles/one-type/', status=404)
2653

  
2654
    # missing profile
2655
    resp = app.put_json(
2656
        '/api/users/%s/profiles/one-type/' % user.uuid, params={'data': {'foo': 'bar'}}, status=404
2657
    )
2658

  
2659
    Profile.objects.create(user=user, profile_type=profile_type, data={})
2660

  
2661
    resp = app.put_json('/api/users/%s/profiles/one-type/' % user.uuid, params={'data': {'foo': 'bar'}})
2662
    assert resp.json == {'result': 1, 'detail': 'Profile successfully updated'}
2663
    assert len(user.subprofiles.all()) == 1
2664
    assert user.subprofiles.last().data == {'foo': 'bar'}
2665

  
2666
    # attempt at overwriting profile data
2667
    resp = app.put_json('/api/users/%s/profiles/one-type/' % user.uuid, params={'data': {'baz': 'bob'}})
2668

  
2669
    # profile has been updated, no extra profile has been created
2670
    assert resp.json == {'result': 1, 'detail': 'Profile successfully updated'}
2671
    assert len(user.subprofiles.all()) == 1
2672
    assert user.subprofiles.last().data == {'baz': 'bob'}
2673

  
2674

  
2675
def test_user_profile_post(app, superuser):
2676
    app.authorization = ('Basic', (superuser.username, superuser.username))
2677

  
2678
    user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
2679

  
2680
    # wrong type
2681
    resp = app.get('/api/users/%s/profiles/non-existant-slug/' % user.uuid, status=404)
2682

  
2683
    ProfileType.objects.create(slug='one-type', name='One Type')
2684

  
2685
    # wrong user
2686
    resp = app.get('/api/users/1234/profiles/one-type/', status=404)
2687

  
2688
    assert len(user.subprofiles.all()) == 0
2689

  
2690
    resp = app.post_json('/api/users/%s/profiles/one-type/' % user.uuid, params={'data': {'baz': 'bob'}})
2691
    assert resp.json == {'result': 1, 'detail': 'Profile successfully assigned to user'}
2692
    assert len(user.subprofiles.all()) == 1
2693
    assert user.subprofiles.last().data == {'baz': 'bob'}
2694

  
2695

  
2696
def test_user_profile_delete(app, superuser):
2697

  
2698
    app.authorization = ('Basic', (superuser.username, superuser.username))
2699

  
2700
    user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
2701
    profile_type = ProfileType.objects.create(slug='one-type', name='One Type')
2702
    Profile.objects.create(user=user, profile_type=profile_type)
2703

  
2704
    app.delete('/api/users/%s/profiles/non-existant-slug/' % user.uuid, status=404)
2705
    app.delete('/api/users/%s/profiles/one-type/' % user.uuid)
2706

  
2707
    assert not Profile.objects.filter(user=user, profile_type=profile_type)
2708

  
2709
    app.delete('/api/users/%s/profiles/one-type/' % user.uuid, status=404)
2620
-