Reputation: 369
In my application, I use a modified User
model with 3 more field that I need.
from django.contrib.auth.models import AbstractUser
from django.db import models
import ldapdb.models
class User(AbstractUser):
cotisant = models.BooleanField(default=False)
nbSessions = models.IntegerField(default=0)
tel = models.CharField(max_length=20, default="")
I want people to be able to change their accounts settings, (like their password, email, tel
,...).
To do so I have a serializer like this :
from rest_framework import serializers
from django.contrib.auth.hashers import make_password
from coreapp.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'cotisant', 'tel')
extra_kwargs = {
# Allow to set pwd, but disallow getting the hash from database
'password': {'write_only': True}
}
def validate_password(self, value: str):
return make_password(value)
And a view like this :
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
permission_classes = (IsSelfOrStaffPermission, TokenHasReadWriteScopeOrCreate,)
lookup_field = 'username'
def get_queryset(self):
current_user = self.request.user
if current_user.is_staff:
user_set = User.objects.all()
else:
user_set = User.objects.filter(username=current_user.username)
query = self.request.query_params.get('q', None)
if not query:
return user_set
return user_set.filter(
Q(username__icontains=query) |
Q(first_name__icontains=query) |
Q(last_name__icontains=query)
)
(This allow access to a user only to himself, unless he is staff)
The problem is, to update nbSessions
parameter, the user have to pay something on one of my apps.
How can I allow the app to set the parameter, but disallow user to update it directly ?
NOTE : I have other apps who use Password Credential Flow and are client side, so someone can get an app token through it.
Upvotes: 4
Views: 161
Reputation: 3378
You have to use a client secret to identify who is calling your api. You will have to generate api keys for your clients. The clients will send these api keys as query params to prove they are your approved clients.
for example you generated a token abcd
for your ios app. Your ios app will send the request as: https://your-endpoint.com/api/method/?token=abcd
To check if the token is correct you can create a custom permission class.
class IsApprovedClientPermission(permissions.BasePermission):
def has_permission(self, request, view):
token = request.query_params.get('token', None)
if request.method not in permissions.SAFE_METHODS:
# custom checks
return token in approved_tokens_list:
return True
You can apply this permission class to any view you want to verify before letting the request modify your data.
Upvotes: 1
Reputation: 17781
If I understood the question correctly, you want a certain field (nbSessions
) to be writable through the same API endpoint by certain third-party apps, but not by regular users.
Here's how I would do it: create two distinct serializers, one for third-party apps, another one for regular users.
class UserSerializer(serializers.ModelSerializer):
"""
Serializer used by third-party apps.
All fields, including nbSessions, are writeable.
"""
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'cotisant', 'nbSessions', 'tel')
extra_kwargs = {
'password': {'write_only': True}
}
# ...
class RestrictedUserSerializer(UserSerializer):
"""
Serializer used by regular users.
All fields except nbSessions are writeable.
It inherits from UserSerializer so that common code is not duplicated.
"""
class Meta(BaseUserSerializer.Meta):
read_only_fields = ('nbSessions',)
Here, as you can see, the only difference between the two serializers is the presence of the read_only_fields
attribute in RestrictedUserSerializer.Meta
.
Then in your view you can inspect your request to see what serializer class to use:
class UserViewSet(viewsets.ModelViewSet):
def get_serializer_class(self):
if is_third_party_app(self.request.user):
return UserSerializer
return RestrictedUserSerializer
# ...
You can expand this concept even further and have different serializer classes (possibly inheriting from the same base class) for regular users, third-party apps, staff members, admins, and so on. Each serializer can have their specific set of fields, validation rules and update logic.
Upvotes: 1