andela/ah-infinity-stones

View on GitHub
authors/apps/authentication/serializers.py

Summary

Maintainability
A
0 mins
Test Coverage
from django.contrib.auth import authenticate

from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from .models import User


class RegistrationSerializer(serializers.ModelSerializer):
    """Serializers registration requests and creates a new user."""

    # Ensure passwords are at least 8 characters long, no longer than 128
    # characters, and can not be read by the client.
    password = serializers.RegexField(
        regex='^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@#$%^&+=*!]).{8,128}$',
        required=True,
        write_only=True,
        error_messages={
            'required':
            'A password is required',
            'invalid':
            'A password must be between 8-128 chars long and must'
            ' contain:'
            ' capital letter'
            ', number'
            ' and a special character'
        })
    # Ensure a user cannot re-use an email to register and must be valid
    email = serializers.EmailField(
        required=True,
        validators=[
            UniqueValidator(
                queryset=User.objects.all(),
                message='The email provided is already in use!',
            )
        ],
        error_messages={
            'required': 'An email is required',
            'invalid':
            "Invalid email: email must be in the format xxxx@xxxx.xx"
        })
    # ensure username meets the right criteria:
    # - must be unique
    # - must be a minimum of 4 characters and a max of 15 characters
    # - can only contain letters, numbers, hyphens (-), underscores (_) ,
    #   and periods (.)
    username = serializers.RegexField(
        regex='^(?=.*[a-z])[a-zA-Z0-9_.-]{4,15}$',
        required=True,
        validators=[
            UniqueValidator(
                queryset=User.objects.all(),
                message='Username provided already in use',
            )
        ],
        error_messages={
            'invalid':
            'Username must be between 4-15 chars,cannot have a space and can only contain'
            ' letters, numbers, -, _ and a .',
            'required':
            'A username is required',
        })

    token = serializers.SerializerMethodField()

    # The client should not be able to send a token along with a registration
    # request. Making `token` read-only handles that for us.

    class Meta:
        model = User
        # List all of the fields that could possibly be included in a request
        # or response, including fields specified explicitly above.
        fields = ['email', 'username', 'password', 'token']

    def create(self, validated_data):
        # Use the `create_user` method we wrote earlier to create a new user.
        return User.objects.create_user(**validated_data)


class LoginSerializer(serializers.Serializer):
    email = serializers.EmailField(max_length=255)
    username = serializers.CharField(max_length=255, read_only=True)
    password = serializers.CharField(max_length=128, write_only=True)
    token = serializers.CharField(max_length=255, read_only=True)

    def validate(self, data):
        # The `validate` method is where we make sure that the current
        # instance of `LoginSerializer` has "valid". In the case of logging a
        # user in, this means validating that they've provided an email
        # and password and that this combination matches one of the users in
        # our database.
        email = data.get('email', None)
        password = data.get('password', None)

        # As mentioned above, an email is required. Raise an exception if an
        # email is not provided.
        if email is None:
            raise serializers.ValidationError(
                'An email address is required to log in.')

        # As mentioned above, a password is required. Raise an exception if a
        # password is not provided.
        if password is None:
            raise serializers.ValidationError(
                'A password is required to log in.')

        # The `authenticate` method is provided by Django and handles checking
        # for a user that matches this email/password combination. Notice how
        # we pass `email` as the `username` value. Remember that, in our User
        # model, we set `USERNAME_FIELD` as `email`.
        
        user = authenticate(username=email, password=password)
        
        # If no user was found matching this email/password combination then
        # `authenticate` will return `None`. Raise an exception in this case.
        if user is None:
            raise serializers.ValidationError(
                'A user with this email and password was not found.')

        # Django provides a flag on our `User` model called `is_active`. The
        # purpose of this flag to tell us whether the user has been banned
        # or otherwise deactivated. This will almost never be the case, but
        # it is worth checking for. Raise an exception in this case.
        if not user.is_active:
            raise serializers.ValidationError(
                'This user has been deactivated.')

        # The `validate` method should return a dictionary of validated data.
        # This is the data that is passed to the `create` and `update` methods
        # that we will see later on.
        return {
            'email': user.email,
            'username': user.username,
            'token': user.token,
        }


class UserSerializer(serializers.ModelSerializer):
    """Handles serialization and deserialization of User objects."""

    # Passwords must be at least 8 characters, but no more than 128
    # characters. These values are the default provided by Django. We could
    # change them, but that would create extra work while introducing no real
    # benefit, so let's just stick with the defaults.
    password = serializers.CharField(
        max_length=128, min_length=8, write_only=True)

    class Meta:
        model = User
        fields = ('email', 'username', 'password')

        # The `read_only_fields` option is an alternative for explicitly
        # specifying the field with `read_only=True` like we did for password
        # above. The reason we want to use `read_only_fields` here is because
        # we don't need to specify anything else about the field. For the
        # password field, we needed to specify the `min_length` and
        # `max_length` properties too, but that isn't the case for the token
        # field.

    def update(self, instance, validated_data):
        """Performs an update on a User."""

        # Passwords should not be handled with `setattr`, unlike other fields.
        # This is because Django provides a function that handles hashing and
        # salting passwords, which is important for security. What that means
        # here is that we need to remove the password field from the
        # `validated_data` dictionary before iterating over it.
        password = validated_data.pop('password', None)

        for (key, value) in validated_data.items():
            # For the keys remaining in `validated_data`, we will set them on
            # the current `User` instance one at a time.
            setattr(instance, key, value)

        if password is not None:
            # `.set_password()` is the method mentioned above. It handles all
            # of the security stuff that we shouldn't be concerned with.
            instance.set_password(password)

        # Finally, after everything has been updated, we must explicitly save
        # the model. It's worth pointing out that `.set_password()` does not
        # save the model.
        instance.save()

        return instance


class SocialAuthSerializer(serializers.Serializer):
    """ This class serializes keys and key_secrets google, twitter and \
    facebook. """
    provider = serializers.CharField(max_length=255, required=True)
    access_token = serializers.CharField(
        max_length=2048, required=True, trim_whitespace=True)
    access_token_secret = serializers.CharField(
        max_length=2048, allow_null=True, default=None, trim_whitespace=True)


class ResetQuestSerializer(serializers.Serializer):
    email = serializers.EmailField()

    @staticmethod
    def validate_email_data(data):
        email = data.get('email')

        print("serializer_email")
        # check if eny email exists in the data
        if email is None:
            raise serializers.ValidationError(
                'An email address is required to send request')
        elif User.objects.filter(email=email).exists():
            return {"email": email}

        raise serializers.ValidationError(
            'User with that email does not exist, Please enter your email')