diff --git a/program/serializers.py b/program/serializers.py index 30adfc823d96af25cc92e22cc80f8c0b1969ba86..f4cd9b2cfba9c16dc16f1eb656fa6e4b57b4c8b4 100644 --- a/program/serializers.py +++ b/program/serializers.py @@ -134,7 +134,6 @@ class UserSerializer(serializers.ModelSerializer): @staticmethod def get_is_privileged(obj: User) -> bool: - # return obj.groups.filter(name=settings.PRIVILEGED_GROUP).exists() return obj.is_superuser def create(self, validated_data): @@ -387,37 +386,27 @@ class HostSerializer(serializers.ModelSerializer): return host def update(self, instance, validated_data): - """ - Update and return an existing Host instance, given the validated data. - - A `PermissionDenied` exception will be raised if the user is not privileged or the owner of - the host and has the permissions to edit the fields. - """ + """Update and return an existing Host instance, given the validated data.""" user = self.context.get("request").user - # user_is_privileged = user.groups.filter(name=settings.PRIVILEGED_GROUP).exists() user_is_owner = user in instance.owners.all() - user_edit_permissions = [ + user_permissions = set( permission.split("__")[-1] for permission in user.get_all_permissions() if permission.startswith("program.edit__host") - ] + ) + update_fields = set(validated_data.keys()) - # Only superusers and owners of a host with edit permissions are allowed to update it - # Being a superuser overrides the ownership - if not (user.is_superuser or (user_is_owner and len(user_edit_permissions) > 0)): + # having the update_host permission overrides the ownership + if not (user.has_perm("program.update_host") or (user_is_owner and user_permissions)): raise exceptions.PermissionDenied(detail="You are not allowed to update this host.") - # Only users with edit permissions are allowed to edit these fields - if "biography" in validated_data and "biography" not in user_edit_permissions: - raise exceptions.PermissionDenied( - detail="You are not allowed to edit the host’s biography." - ) - - if "name" in validated_data and "name" not in user_edit_permissions: - raise exceptions.PermissionDenied( - detail="You are not allowed to edit the host’s name." - ) + # without the update_host permission, fields without edit permission are not allowed + if not user.has_perm("program.update_host") and ( + not_allowed := update_fields.difference(user_permissions) + ): + detail = {field: "You are not allowed to edit this field" for field in not_allowed} + raise exceptions.PermissionDenied(detail=detail) if "biography" in validated_data: instance.biography = validated_data.get("biography") @@ -425,27 +414,25 @@ class HostSerializer(serializers.ModelSerializer): if "name" in validated_data: instance.name = validated_data.get("name") - # Only update these fields if the user superuser, ignore otherwise - if user.is_superuser: - if "email" in validated_data: - instance.email = validated_data.get("email") + if "email" in validated_data: + instance.email = validated_data.get("email") - if "image" in validated_data: - instance.image = validated_data.get("image") + if "image" in validated_data: + instance.image = validated_data.get("image") - if "is_active" in validated_data: - instance.is_active = validated_data.get("is_active") + if "is_active" in validated_data: + instance.is_active = validated_data.get("is_active") - # optional nested objects - if links_data := validated_data.get("links"): - instance = delete_links(instance) + # optional many-to-many + if "owners" in validated_data: + instance.owners.set(validated_data.get("owners")) - for link_data in links_data: - HostLink.objects.create(host=instance, **link_data) + # optional nested objects + if "links" in validated_data: + instance = delete_links(instance) - # optional many-to-many - if "owners" in validated_data.get("owners"): - instance.owners.set(validated_data.get("owners", [])) + for link_data in validated_data.get("links"): + HostLink.objects.create(host=instance, **link_data) instance.updated_by = self.context.get("request").user.username @@ -610,45 +597,27 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer): return show def update(self, instance, validated_data): - """ - Update and return an existing Show instance, given the validated data. - - A `PermissionDenied` exception will be raised if the user is not privileged or the owner of - a show and has the permissions to edit the fields. - """ + """Update and return an existing Show instance, given the validated data.""" user = self.context.get("request").user - # user_is_privileged = user.groups.filter(name=settings.PRIVILEGED_GROUP).exists() - user_is_owner = instance in user.shows.all() - user_edit_permissions = [ + user_is_owner = user in instance.owners.all() + user_permissions = set( permission.split("__")[-1] for permission in user.get_all_permissions() if permission.startswith("program.edit__show") - ] + ) + update_fields = set(validated_data.keys()) - # Only superusers and owners of a show with edit permissions are allowed to update it - # Being a superuser overrides the ownership - if not (user.is_superuser or (user_is_owner and len(user_edit_permissions) > 0)): + # having update_show permission overrides the ownership + if not (user.has_perm("program.update_show") or (user_is_owner and user_permissions)): raise exceptions.PermissionDenied(detail="You are not allowed to update this show.") - # Only users with edit permissions are allowed to update these fields - if "description" in validated_data and "description" not in user_edit_permissions: - raise exceptions.PermissionDenied( - detail="You are not allowed to edit the show’s description." - ) - - if "name" in validated_data and "name" not in user_edit_permissions: - raise exceptions.PermissionDenied( - detail="You are not allowed to edit the show’s name." - ) - - if ( - "short_description" in validated_data - and "short_description" not in user_edit_permissions + # without the update_show permission, fields without edit permission are not allowed + if not user.has_perm("update_show") and ( + not_allowed := update_fields.difference(user_permissions) ): - raise exceptions.PermissionDenied( - detail="You are not allowed to edit the show’s short description." - ) + detail = {field: "You are not allowed to edit this field" for field in not_allowed} + raise exceptions.PermissionDenied(detail=detail) if "description" in validated_data: instance.description = validated_data.get("description") @@ -659,69 +628,67 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer): if "short_description" in validated_data: instance.short_description = validated_data.get("short_description") - # Only update these fields if the user is superuser, ignore otherwise - if user.is_superuser: - if "cba_series_id" in validated_data: - instance.cba_series_id = validated_data.get("cba_series_id") + if "cba_series_id" in validated_data: + instance.cba_series_id = validated_data.get("cba_series_id") - if "default_playlist_id" in validated_data: - instance.default_playlist_id = validated_data.get("default_playlist_id") + if "default_playlist_id" in validated_data: + instance.default_playlist_id = validated_data.get("default_playlist_id") - if "email" in validated_data: - instance.email = validated_data.get("email") + if "email" in validated_data: + instance.email = validated_data.get("email") - if "funding_category" in validated_data: - instance.funding_category = validated_data.get("funding_category") + if "funding_category" in validated_data: + instance.funding_category = validated_data.get("funding_category") - if "image" in validated_data: - instance.image = validated_data.get("image") + if "image" in validated_data: + instance.image = validated_data.get("image") - if "internal_note" in validated_data: - instance.internal_note = validated_data.get("internal_note") + if "internal_note" in validated_data: + instance.internal_note = validated_data.get("internal_note") - if "is_active" in validated_data: - instance.is_active = validated_data.get("is_active") + if "is_active" in validated_data: + instance.is_active = validated_data.get("is_active") - if "is_public" in validated_data: - instance.is_public = validated_data.get("is_public") + if "is_public" in validated_data: + instance.is_public = validated_data.get("is_public") - if "logo" in validated_data: - instance.logo = validated_data.get("logo") + if "logo" in validated_data: + instance.logo = validated_data.get("logo") - if "predecessor" in validated_data: - instance.predecessor = validated_data.get("predecessor") + if "predecessor" in validated_data: + instance.predecessor = validated_data.get("predecessor") - if "slug" in validated_data: - instance.slug = validated_data.get("slug") + if "slug" in validated_data: + instance.slug = validated_data.get("slug") - if "type" in validated_data: - instance.type = validated_data.get("type") + if "type" in validated_data: + instance.type = validated_data.get("type") - # optional many-to-many - if "category" in validated_data: - instance.category.set(validated_data.get("category", [])) + # optional many-to-many + if "category" in validated_data: + instance.category.set(validated_data.get("category", [])) - if "hosts" in validated_data: - instance.hosts.set(validated_data.get("hosts", [])) + if "hosts" in validated_data: + instance.hosts.set(validated_data.get("hosts", [])) - if "language" in validated_data: - instance.language.set(validated_data.get("language", [])) + if "language" in validated_data: + instance.language.set(validated_data.get("language", [])) - if "music_focus" in validated_data: - instance.music_focus.set(validated_data.get("music_focus", [])) + if "music_focus" in validated_data: + instance.music_focus.set(validated_data.get("music_focus", [])) - if "owners" in validated_data: - instance.owners.set(validated_data.get("owners", [])) + if "owners" in validated_data: + instance.owners.set(validated_data.get("owners", [])) - if "topic" in validated_data: - instance.topic.set(validated_data.get("topic", [])) + if "topic" in validated_data: + instance.topic.set(validated_data.get("topic", [])) - # optional nested objects - if links_data := validated_data.get("links"): - instance = delete_links(instance) + # optional nested objects + if "links" in validated_data: + instance = delete_links(instance) - for link_data in links_data: - ShowLink.objects.create(show=instance, **link_data) + for link_data in validated_data.get("links"): + ShowLink.objects.create(host=instance, **link_data) instance.updated_by = self.context.get("request").user.username @@ -1006,11 +973,7 @@ class NoteSerializer(serializers.ModelSerializer): ) + read_only_fields def create(self, validated_data): - """Create and return a new Note instance, given the validated data. - - A `PermissionDenied` exception will be raised if the user is not privileged or the owner of - the show. - """ + """Create and return a new Note instance, given the validated data.""" links_data = validated_data.pop("links", []) @@ -1022,15 +985,11 @@ class NoteSerializer(serializers.ModelSerializer): show = validated_data["timeslot"].schedule.show user = self.context.get("request").user - # user_is_privileged = user.groups.filter(name=settings.PRIVILEGED_GROUP).exists() user_is_owner = user in show.owners.all() - # Only superusers and owners of a show are allowed to create a note - # Being a superuser overrides the ownership - if not (user.is_superuser or user_is_owner): - raise exceptions.PermissionDenied( - detail="You are not allowed to create a note for this show." - ) + # Having the create_note permission overrides the ownership + if not (user.has_perm("program.create_note") or user_is_owner): + raise exceptions.PermissionDenied(detail="You are not allowed to create this note.") # we derive `contributors`, `language` and `topic` from the Show's values if not set contributors = validated_data.pop("contributors", show.hosts.values_list("id", flat=True)) @@ -1058,19 +1017,13 @@ class NoteSerializer(serializers.ModelSerializer): return note def update(self, instance, validated_data): - """Update and return an existing Note instance, given the validated data. - - A `PermissionDenied` exception will be raised if the user is not privileged or the owner of - a show. - """ + """Update and return an existing Note instance, given the validated data.""" user = self.context.get("request").user - # user_is_privileged = user.groups.filter(name=settings.PRIVILEGED_GROUP).exists() user_is_owner = user in instance.timeslot.schedule.show.owners.all() - # Only superusers and owners of a show are allowed to update a note - # Being a superuser overrides the ownership - if not (user.is_superuser or user_is_owner): + # Having the update_note permission overrides the ownership + if not (user.has_perm("program.update_note") or user_is_owner): raise exceptions.PermissionDenied(detail="You are not allowed to update this note.") if "cba_id" in validated_data: @@ -1101,15 +1054,15 @@ class NoteSerializer(serializers.ModelSerializer): if "language" in validated_data: instance.language.set(validated_data.get("language", [])) - # Only update this field if the user is superuser, ignore otherwise - if "topic" in validated_data and user.is_superuser: + # Only update this field if the user has the update_note permission, ignore otherwise + if "topic" in validated_data and user.has_perm("program.update_note"): instance.topic.set(validated_data.get("topic", [])) # optional nested objects - if links_data := validated_data.get("links"): + if "links" in validated_data: instance = delete_links(instance) - for link_data in links_data: + for link_data in validated_data.get("links"): NoteLink.objects.create(note=instance, **link_data) instance.updated_by = self.context.get("request").user.username