Skip to content

Commit 388042a

Browse files
authored
Merge pull request #842 from fao89/7311
Remove permissions associated with groups
2 parents 38042a3 + 0086ec2 commit 388042a

File tree

5 files changed

+265
-31
lines changed

5 files changed

+265
-31
lines changed

CHANGES/7310.feature

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Extended endpoint `/pulp/api/v3/groups/:pk/users` to add and remove users from a group.
2+
3+
NOTE: this endpoint is in tech-preview and may change in backwards incompatible ways in the future.

CHANGES/7311.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Extended endpoints `/pulp/api/v3/groups/:pk/model_permissions` and
2+
`/pulp/api/v3/groups/:pk/object_permissions` to add and remove permissions from a group.
3+
4+
NOTE: this endpoint is in tech-preview and may change in backwards incompatible ways in the future.

pulpcore/app/serializers/user.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from gettext import gettext as _
22

33
from django.contrib.auth import get_user_model
4-
from django.contrib.auth.models import Group
4+
from django.contrib.auth.models import Group, Permission
5+
from django.urls import reverse
6+
from guardian.models.models import GroupObjectPermission
57
from rest_framework import serializers
68

79
from pulpcore.app.serializers import IdentityField
@@ -27,15 +29,39 @@ def to_representation(self, obj):
2729
return serializer.data.get("pulp_href")
2830

2931

30-
class ObjectPermissionSerializer(serializers.Serializer):
31-
"""Serializer for user/group object permission."""
32+
class PermissionSerializer(serializers.Serializer):
33+
"""Serializer for User/Group object permission."""
3234

35+
pulp_href = serializers.SerializerMethodField(read_only=True)
36+
id = serializers.SerializerMethodField(read_only=True)
3337
permission = PermissionField(source="*", read_only=True)
3438
obj = ContentObjectField(help_text=_("Content object."), source="*", read_only=True)
3539

40+
def get_id(self, obj):
41+
"""Get model/object permission id."""
42+
return obj.id
43+
44+
def get_pulp_href(self, obj):
45+
"""Get model/object permission pulp_href."""
46+
group_pk = self.context.get("group_pk")
47+
48+
if group_pk and isinstance(obj, Permission):
49+
return reverse("model_permissions-detail", args=[group_pk, obj.pk])
50+
51+
if group_pk and isinstance(obj, GroupObjectPermission):
52+
return reverse("object_permissions-detail", args=[group_pk, obj.pk])
53+
54+
def to_representation(self, obj):
55+
representation = super().to_representation(obj)
56+
57+
if not self.context.get("group_pk"):
58+
representation.pop("pulp_href")
59+
60+
return representation
61+
3662

3763
class UserGroupSerializer(serializers.ModelSerializer):
38-
"""Serializer for user groups."""
64+
"""Serializer for Groups that belong to an User."""
3965

4066
name = serializers.CharField(help_text=_("Name."), max_length=150,)
4167
pulp_href = IdentityField(view_name="groups-detail")
@@ -46,7 +72,7 @@ class Meta:
4672

4773

4874
class UserSerializer(serializers.ModelSerializer):
49-
"""Serializer for user."""
75+
"""Serializer for User."""
5076

5177
pulp_href = IdentityField(view_name="users-detail")
5278
id = serializers.IntegerField(read_only=True)
@@ -83,7 +109,7 @@ class Meta:
83109

84110

85111
class GroupUserSerializer(serializers.ModelSerializer):
86-
"""Serializer for group users."""
112+
"""Serializer for Users that belong to a Group."""
87113

88114
username = serializers.CharField(
89115
help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."),
@@ -97,11 +123,11 @@ class Meta:
97123

98124

99125
class GroupSerializer(serializers.ModelSerializer):
100-
"""Serializer for group."""
126+
"""Serializer for Group."""
101127

102128
pulp_href = IdentityField(view_name="groups-detail")
103129
id = serializers.IntegerField(read_only=True)
104-
name = serializers.CharField(help_text=_("First name"), max_length=150)
130+
name = serializers.CharField(help_text=_("Name"), max_length=150)
105131

106132
class Meta:
107133
model = Group

pulpcore/app/viewsets/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,10 @@
4949
)
5050
from .task import TaskViewSet, TaskGroupViewSet, WorkerViewSet # noqa
5151
from .upload import UploadViewSet # noqa
52-
from .user import GroupViewSet, UserViewSet # noqa
52+
from .user import ( # noqa
53+
GroupViewSet,
54+
GroupUserViewSet,
55+
GroupModelPermissionViewSet,
56+
GroupObjectPermissionViewSet,
57+
UserViewSet,
58+
)

pulpcore/app/viewsets/user.py

Lines changed: 217 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1+
from gettext import gettext as _
2+
13
from django.contrib.auth import get_user_model
2-
from django.contrib.auth.models import Group
4+
from django.contrib.auth.models import Group, Permission
5+
from django.shortcuts import get_object_or_404
36
from drf_spectacular.utils import extend_schema
4-
from rest_framework import mixins
7+
from guardian.models.models import GroupObjectPermission
8+
from rest_framework import mixins, status
59
from rest_framework.decorators import action
610
from rest_framework.response import Response
11+
from rest_framework.serializers import ValidationError
712

813
from pulpcore.app.viewsets.base import NamedModelViewSet
914
from pulpcore.app.serializers.user import (
1015
GroupSerializer,
1116
GroupUserSerializer,
12-
ObjectPermissionSerializer,
17+
PermissionSerializer,
1318
UserSerializer,
1419
)
1520

@@ -29,16 +34,14 @@ class UserViewSet(
2934
queryset = get_user_model().objects.all()
3035

3136
@extend_schema(description="List user permissions.",)
32-
@action(detail=True, methods=["get"], serializer_class=ObjectPermissionSerializer)
37+
@action(detail=True, methods=["get"], serializer_class=PermissionSerializer)
3338
def permissions(self, request, pk):
3439
"""
3540
List user permissions.
3641
"""
3742
user = self.get_object()
38-
object_permission = ObjectPermissionSerializer(
39-
user.userobjectpermission_set.all(), many=True
40-
)
41-
permissions = ObjectPermissionSerializer(user.user_permissions.all(), many=True)
43+
object_permission = PermissionSerializer(user.userobjectpermission_set.all(), many=True)
44+
permissions = PermissionSerializer(user.user_permissions.all(), many=True)
4245
return Response(object_permission.data + permissions.data)
4346

4447

@@ -58,28 +61,220 @@ class GroupViewSet(
5861
"""
5962

6063
endpoint_name = "groups"
64+
router_lookup = "group"
6165
serializer_class = GroupSerializer
6266
queryset = Group.objects.all()
6367

64-
@extend_schema(description="List group permissions.",)
65-
@action(detail=True, methods=["get"], serializer_class=ObjectPermissionSerializer)
66-
def permissions(self, request, pk):
68+
69+
class GroupModelPermissionViewSet(NamedModelViewSet):
70+
"""
71+
ViewSet for Model Permissions that belongs to a Group.
72+
73+
NOTE: This API endpoint is in "tech preview" and subject to change
74+
75+
"""
76+
77+
endpoint_name = "model_permissions"
78+
nest_prefix = "groups"
79+
router_lookup = "model_permission"
80+
parent_viewset = GroupViewSet
81+
parent_lookup_kwargs = {"group_pk": "group__pk"}
82+
serializer_class = PermissionSerializer
83+
queryset = Permission.objects.all()
84+
85+
def get_model_permission(self, request):
86+
"""Get model permission"""
87+
data = {}
88+
for key, value in request.data.items():
89+
if key == "permission":
90+
data["codename"] = value.split(".")[-1]
91+
continue
92+
93+
if key == "pulp_href":
94+
if "id" in data.keys():
95+
continue
96+
data["id"] = value.strip("/").split("/")[-1]
97+
continue
98+
99+
data[key] = value
100+
101+
return get_object_or_404(Permission, **data)
102+
103+
@extend_schema(description="Retrieve a model permission from a group.")
104+
def retrieve(self, request, group_pk, pk):
105+
instance = get_object_or_404(Permission, pk=pk)
106+
serializer = PermissionSerializer(instance, context={"group_pk": group_pk})
107+
return Response(serializer.data)
108+
109+
@extend_schema(description="List group permissions.", responses={200: PermissionSerializer})
110+
def list(self, request, group_pk):
111+
"""
112+
List group model permissions.
113+
"""
114+
group = Group.objects.get(pk=group_pk)
115+
queryset = group.permissions.all()
116+
117+
page = self.paginate_queryset(queryset)
118+
if page is not None:
119+
serializer = PermissionSerializer(page, context={"group_pk": group_pk}, many=True)
120+
return self.get_paginated_response(serializer.data)
121+
122+
serializer = PermissionSerializer(queryset, context={"group_pk": group_pk}, many=True)
123+
return Response(serializer.data)
124+
125+
@extend_schema(
126+
description="Add a model permission to a group.", responses={201: PermissionSerializer},
127+
)
128+
def create(self, request, group_pk):
129+
"""
130+
Add a model permission to a group.
131+
"""
132+
group = Group.objects.get(pk=group_pk)
133+
permission = self.get_model_permission(request)
134+
group.permissions.add(permission)
135+
group.save()
136+
serializer = PermissionSerializer(permission, context={"group_pk": group_pk})
137+
return Response(serializer.data, status=status.HTTP_201_CREATED)
138+
139+
@extend_schema(description="Remove a model permission from a group.")
140+
def destroy(self, request, group_pk, pk):
141+
"""
142+
Remove a model permission from a group.
143+
"""
144+
group = Group.objects.get(pk=group_pk)
145+
permission = get_object_or_404(Permission, pk=pk)
146+
group.permissions.remove(permission)
147+
group.save()
148+
return Response(status=status.HTTP_204_NO_CONTENT)
149+
150+
151+
class GroupObjectPermissionViewSet(NamedModelViewSet):
152+
"""
153+
ViewSet for Object Permissions that belongs to a Group.
154+
155+
NOTE: This API endpoint is in "tech preview" and subject to change
156+
157+
"""
158+
159+
endpoint_name = "object_permissions"
160+
nest_prefix = "groups"
161+
router_lookup = "object_permission"
162+
parent_viewset = GroupViewSet
163+
parent_lookup_kwargs = {"group_pk": "group__pk"}
164+
serializer_class = PermissionSerializer
165+
queryset = Permission.objects.all()
166+
167+
def get_model_permission(self, request):
168+
"""Get model permission"""
169+
codename = request.data["permission"].split(".")[-1]
170+
return get_object_or_404(Permission, codename=codename)
171+
172+
@extend_schema(description="Retrieve a model permission from a group.")
173+
def retrieve(self, request, group_pk, pk):
174+
instance = get_object_or_404(GroupObjectPermission, pk=pk)
175+
serializer = PermissionSerializer(instance, context={"group_pk": group_pk})
176+
return Response(serializer.data)
177+
178+
@extend_schema(
179+
description="List group object permissions.", responses={200: PermissionSerializer}
180+
)
181+
def list(self, request, group_pk):
67182
"""
68-
List group permissions.
183+
List group object permissions.
69184
"""
70-
group = self.get_object()
71-
object_permission = ObjectPermissionSerializer(
72-
group.groupobjectpermission_set.all(), many=True
185+
group = Group.objects.get(pk=group_pk)
186+
queryset = group.groupobjectpermission_set.all()
187+
188+
page = self.paginate_queryset(queryset)
189+
if page is not None:
190+
serializer = PermissionSerializer(page, context={"group_pk": group_pk}, many=True)
191+
return self.get_paginated_response(serializer.data)
192+
193+
serializer = PermissionSerializer(queryset, context={"group_pk": group_pk}, many=True)
194+
return Response(serializer.data)
195+
196+
@extend_schema(
197+
description="Add an object permission to a group.", responses={201: PermissionSerializer},
198+
)
199+
def create(self, request, group_pk):
200+
"""
201+
Create an object permission to a group.
202+
"""
203+
group = Group.objects.get(pk=group_pk)
204+
permission = self.get_model_permission(request)
205+
object_pk = request.data["obj"].strip("/").split("/")[-1]
206+
object_permission = GroupObjectPermission(
207+
group=group,
208+
permission=permission,
209+
content_type_id=permission.content_type_id,
210+
object_pk=object_pk,
73211
)
74-
permissions = ObjectPermissionSerializer(group.permissions.all(), many=True)
75-
return Response(object_permission.data + permissions.data)
212+
object_permission.save()
213+
serializer = PermissionSerializer(object_permission, context={"group_pk": group_pk})
214+
return Response(serializer.data, status=status.HTTP_201_CREATED)
215+
216+
@extend_schema(description="Remove an object permission from a group.")
217+
def destroy(self, request, group_pk, pk):
218+
"""
219+
Delete an object permission from a group.
220+
"""
221+
object_permission = get_object_or_404(GroupObjectPermission, pk=pk, group_id=group_pk)
222+
object_permission.delete()
223+
return Response(status=status.HTTP_204_NO_CONTENT)
224+
76225

77-
@extend_schema(description="List group users.",)
78-
@action(detail=True, methods=["get"], serializer_class=GroupUserSerializer)
79-
def users(self, request, pk):
226+
class GroupUserViewSet(NamedModelViewSet):
227+
"""
228+
ViewSet for Users that belongs to a Group.
229+
230+
NOTE: This API endpoint is in "tech preview" and subject to change
231+
232+
"""
233+
234+
endpoint_name = "users"
235+
nest_prefix = "groups"
236+
router_lookup = "user"
237+
parent_viewset = GroupViewSet
238+
parent_lookup_kwargs = {"group_pk": "groups__pk"}
239+
serializer_class = GroupUserSerializer
240+
queryset = get_user_model().objects.all()
241+
242+
def list(self, request, group_pk):
80243
"""
81244
List group users.
82245
"""
83-
group = self.get_object()
84-
serializer = GroupUserSerializer(group.user_set.all(), many=True)
246+
group = Group.objects.get(pk=group_pk)
247+
queryset = group.user_set.all()
248+
249+
page = self.paginate_queryset(queryset)
250+
if page is not None:
251+
serializer = GroupUserSerializer(page, context={"request": None}, many=True)
252+
return self.get_paginated_response(serializer.data)
253+
254+
serializer = self.get_serializer(queryset, many=True)
85255
return Response(serializer.data)
256+
257+
def create(self, request, group_pk):
258+
"""
259+
Add a user to a group.
260+
"""
261+
group = Group.objects.get(pk=group_pk)
262+
if not request.data:
263+
raise ValidationError(
264+
_("Please provide one of the following values for User: 'pk', 'id', 'username'")
265+
)
266+
user = get_object_or_404(get_user_model(), **request.data)
267+
group.user_set.add(user)
268+
group.save()
269+
serializer = GroupUserSerializer(user, context={"request": None})
270+
return Response(serializer.data, status=status.HTTP_201_CREATED)
271+
272+
def destroy(self, request, group_pk, pk):
273+
"""
274+
Remove a user from a group.
275+
"""
276+
group = Group.objects.get(pk=group_pk)
277+
user = get_object_or_404(get_user_model(), pk=pk)
278+
group.user_set.remove(user)
279+
group.save()
280+
return Response(status=status.HTTP_204_NO_CONTENT)

0 commit comments

Comments
 (0)