How to Hash Django user password in Django Rest Framework? - python

I'm trying to create an API for my user registration using Django Rest Framework. I created a serializer by following the step from the api-guide
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
However, I keep getting the Invalid password format or unknown hashing algorithm. for my newly created user. I've tried to use make_password from django.contrib.auth.hashers, but I still can't resolve this issue.
Thanks

You can try it in this way
from django.contrib.auth.hashers import make_password
user = User.objects.create(
email=validated_data['email'],
username=validated_data['username'],
password = make_password(validated_data['password'])
)

You can overwrite the perform_create method in CreateAPIView
from rest_framework.generics import CreateAPIView
class SignUpView(CreateAPIView):
serializer_class = SignUpSerializers
def perform_create(self, serializer):
instance = serializer.save()
instance.set_password(instance.password)
instance.save()

You could also use a field validation function for the password field by adding a validate_password method to your serializer and make it return the hash.
from rest_framework.serializers import ModelSerializer
from django.contrib.auth.hashers import make_password
class UserSerializer(ModelSerializer):
class Meta:
model = backend.models.User
fields = ('username', 'email', 'password',)
validate_password = make_password

In the serializer redefine the function create with this:
from django.contrib.auth.hashers import make_password
class UserSerializer(ModelSerializer):
def create(self, validated_data):
validated_data['password'] = make_password(validated_data['password'])
return super(UserSerializer, self).create(validated_data)
And this all! :D

You can do this in the views perform_create method:
from django.contrib.auth.hashers import make_password
def perform_create(self, instance):
current_user = self.request.user
user_exists = CustomUser.objects.filter(
email=self.request.data['email']).first()
if user_exists:
raise MethodNotAllowed
else:
instance.save(is_active=False, is_confirmed=False,
password=make_password(self.request.data['password']))

Another simple solution is to use User.objects.create_user() in your create method like below
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user

I know it's an old thread but i got this question today and i like to share my solution here.
You can define your serializer simple and like below:
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
on the other hand in your view you can override perform_create method as below:
class UserView(ViewSets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserCreateSerializer
def perform_create(self , serializer):
new_user =
User.objects.create(username=self.request.data.get("username"))
new_user.set_password(self.request.data.get("password"))
serializer.save(password=user.password)
in this way, you can pass extra information to serializer to save.

Related

django rest framework email verification using generic view

I am done with the registration as you can see. Now I want to send email verification so users can confirm. So once a user register, he/she gets a mail for confirmation.
how do I send email verification using "ListCreateAPIView"?
Do I need a third party package?
Can someone help me out? Thanks
Here is my view
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
My serializer.py
class UserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
username = serializers.CharField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
password = serializers.CharField(min_length=8, style={'input_type': 'password', 'placeholder': 'Password'})
def create(self, validated_data):
user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password'])
return user
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
from django.core.mail import send_mail
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def perform_create(self, serializer):
created_object = serializer.save()
send_mail('Subject here','Here is the message.','from#example.com',
[created_object.email], fail_silently=False,)
send email when object has been created
for create a link and verify follow this tutorial doc
You can try django-djoser link.

DRF how to make register api view only available for my react app

So I can't figure out how to make this registration view can only accept registrations from my react app. Currently, anybody can put values in my fields and then my API will accept it. I would like to make it restricted in some way so that it only accepts values through the apps that I allow.
serializers.py
# Serializer for user info for the registration API
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User # for the User model, use get_user_model for custom
fields = ('id', 'username', 'password', 'email', 'first_name', 'last_name',)
extra_kwargs = {'password': {'write_only': True}}
read_only_fields = ('id',)
# override create method
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name']
)
user.set_password(validated_data['password'])
user.save()
return user
views.py
class RegisterUserView(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny, ]
serializer_class = UserSerializer
queryset = ''

How do I encode the password for my user registration viewset in Django REST Framework?

I'm trying to add user registration functionality to my Django REST application.
Here is my serializer:
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
class Meta:
model = User
fields = ('url', 'id', 'username', 'password', 'email', 'snippets')
Here is my views:
from snippets.serializers import UserSerializer
from django.contrib.auth.models import User
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list` and `detail` actions.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
Right now it's storing the password in plain text. How can I encode the password?
I would prefer to continue using the ViewSet classes as they're very clean and convenient.
class UserCreateSerializer(ModelSerializer):
def create(self, validated_data):
instance = User.objects.create_user(**validated_data)
return instance
class Meta:
model = User
fields = ('username', 'email', 'password')
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserCreateSerializer
permission_classes = (IsAuthenticated)
def get_permissions(self):
if self.action in ('create',):
self.permission_classes = [AllowAny, ]
return super(self.__class__, self).get_permissions()
def create(self, request, *args, **kwargs):
serializer = UserCreateSerializer(data=request.data)
if serializer.is_valid():
user = serializer.create(serializer.validated_data)
return Response('success')
else:
return Response(serializer.errors)
User.objects.create_user() will encode your password with django default encryption algorithm(PBKDF2).And you can use make_password to change your origin password to encoded one,or you can use user.set_password(request.data['pwd_new'])
from django.contrib.auth.hashers import make_password
more info here

Django Rest not creating extended UserProfile

I'm using Django Rest Framework and I've created an extended UserProfile model as follows:
class UserProfile(models.Model):
user = models.OneToOneField(User)
#Some Fields for UserProfile
def user_profile_url(self):
return reverse('user_profile', args=(self.user.id, "{}-{}".format(self.user.first_name, self.user.last_name)))
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
However, on signing up using rest_auth's /registration endpoint: http://django-rest-auth.readthedocs.org/en/latest/api_endpoints.html#registration, the UserProfile is not being created even though the User is created. In my serializers.py, I've done the following for users who sign up
class UserSignUpSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email',)
def create(self, validated_data):
user = User(email=validated_data['email'], username=validated_data['email'])
user.set_password(validated_data['password'])
user.save()
profile = UserProfile(user=user)
profile.save()
return user
Where am I going wrong?
Because request goes here https://github.com/Tivix/django-rest-auth/blob/master/rest_auth/registration/views.py#L38 and doesn't call serializer.create() actually.
Try to override signup form as suggested in the docs:
ACCOUNT_FORMS = {
'signup': 'path.to.custom.SignupForm'
}
example of the profile form:
https://djangosnippets.org/snippets/2081/

Why isn't my Django User Model's Password Hashed?

I am using the Django REST Framework (DRF) to create an endpoint with which I can register new users. However, when I hit the creation endpoint with a POST, the new user is saved via a serializer, but the password is saved in cleartext in the database. The code for my serializer is as follows:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ['password', 'username', 'first_name', 'last_name', 'email']
read_only_fields = ['is_staff', 'is_superuser']
write_only_fields = ['password']
Please note that I am using the default User model from the Django auth package, and that I am very new to working with DRF! Additionally, I have found this question which provides a solution, but this appears to require two database interactions -- I do not believe that this is efficient, but that might be an incorrect assumption on my part.
The issue is DRF will simply set the field values onto the model. Therefore, the password is set on the password field, and saved in the database. But to properly set a password, you need to call the set_password() method, that will do the hashing.
There are several ways to do this, but the best way on rest framework v3 is to override the update() and create() methods on your Serializer.
class UserSerializer(serializers.ModelSerializer):
# <Your other UserSerializer stuff here>
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
Two things here:
we user self.Meta.model, so if the model is changed on the
serializer, it still works (as long as it has a set_password
method of course).
we iterate on validated_data items and not
the fields, to account for optionally excludeed fields.
Also, this version of create does not save M2M relations. Not needed in your example, but it could be added if required. You would need to pop those from the dict, save the model and set them afterwards.
FWIW, I thereby make all python code in this answer public domain worldwide. It is distributed without any warranty.
This worked for me.
class UserSerializer(serializers.ModelSerializer):
def create(self, *args, **kwargs):
user = super().create(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
def update(self, *args, **kwargs):
user = super().update(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
class Meta:
model = get_user_model()
fields = "__all__"
just override the create and update methods of the serializer:
def create(self, validated_data):
user = get_user_model(**validated_data)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
for f in UserSerializer.Meta.fields + UserSerializer.Meta.write_only_fields:
set_attr(instance, f, validated_data[f])
instance.set_password(validated_data['password'])
instance.save()
return instance
Here is an alternative to accepted answer.
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User.objects.create_user(
email=validated_data['email'],
username=validated_data['username'],
password=validated_data['password'],
)
user.save()
return user
create_user function is defined in UserManager and it uses set_password(), we don't need to use it explicitly. I have found many answers and articles which suggest to use set_password but after trying many things I figured the above and it works with CustomUserManager too.
Suppose phone number and password is required to register a user. So our CustomUserManager will look something like this and CreateUserSerializer will handle this too with no changes.
class CustomUserManager(BaseUserManager):
def create_user(self, phone_number, password):
if not phone_number:
raise ValueError('Phone Number must be set')
user = self.model(phone_number=phone_number)
user.set_password(password)
user.save(using=self._db)
return user

Categories

Resources