0

I am trying to do something which seems so straight-forward with Django Rest Framework and object permissions but there doesn't seem to be a simple way to go about it.

Here are the (simplified) models I am talking about:-

class Team(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

class Player(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.CASCADE)

class Invitation(models.Model):
    team = models.ForeignKey(Team, on_delete=models.CASCADE)

I want to:-

a) allow any authenticated user to create a Team but to prevent anyone but that user from changing or deleting that team b) allow any Player of that Team to create an Invitation for that Team but no-one else to be able to

I decided to go with DjangoObjectPermissions in order to implement a).

So my first problem seems to be that, in order to allow anyone to create a Team, I need to give ALL users the add_team permission which seems a bit unnecessary to me. Then I'll need to give any "normal" players I create the view_team permission on that Team but only the owner gets change_team and delete_team on that Team. Which is fine.

But the bigger problem is when I try and work out permissions for the Invitation model.

I can't give add_invitation permission to a user for a particular Team because there is no permission called add_invitation on the Team object. (By the way, what does add_invitation even mean when applied to a particular Invitation?!) So how do I go about restricting the users that can create invitations for a Team to those that are in that Team? Do I use the view_team permission and make the assumption that "if you've got view_team permissions, you can create an Invitation for that Team"?

That doesn't seem right to me but I can't figure out a better way.

bodger
  • 1,112
  • 6
  • 24

1 Answers1

0

Well, I don't know much about django-guardian, but if you want to

allow any authenticated user to create a Team

Create is not object-level permission because there's no object yet.

prevent anyone but that user from changing or deleting that team

Sounds like a normal DRF permissions to me. Use this for your permission_classes in the viewset.

class TeamPermission(BasePermission):
    def has_permission(self, request, view) -> bool:
        return request.user and request.user.is_authenticated

    def has_object_permission(self, request, view, obj) -> bool:
        if request.method in SAFE_METHODS:
            return True
        return obj.owner == request.user

allow any Player of that Team to create an Invitation for that Team but no-one else to be able to

That's something you do in your serializer.

class CreateInvitationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Invitation
        fields = ['id', 'team']
        
    def validate_team(self, team):
        request = self.context['request']
        user = request.user
        # even if it's working you will probably have to adjust a few things
        # but that's the way it can be done
        if not team.player_set.filter(user=user).exists():
            raise serializers.ValidationError("blah")
        return team
Tom Wojcik
  • 5,471
  • 4
  • 32
  • 44