Commit f6479f5e authored by Michael Fladischer's avatar Michael Fladischer

Imported Upstream version 0.3.1

parents
include README.md
include djoser/templates/*
include requirements.txt
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
MANIFEST.in
README.md
requirements.txt
setup.py
djoser/__init__.py
djoser/constants.py
djoser/serializers.py
djoser/settings.py
djoser/signals.py
djoser/urls.py
djoser/utils.py
djoser/views.py
djoser.egg-info/PKG-INFO
djoser.egg-info/SOURCES.txt
djoser.egg-info/dependency_links.txt
djoser.egg-info/requires.txt
djoser.egg-info/top_level.txt
djoser/templates/activation_email_body.txt
djoser/templates/activation_email_subject.txt
djoser/templates/password_reset_email_body.txt
djoser/templates/password_reset_email_subject.txt
\ No newline at end of file
Django>=1.5
djangorestframework>=2.4.0
from django.utils.translation import ugettext_lazy as _
INVALID_CREDENTIALS_ERROR = _('Unable to login with provided credentials.')
INACTIVE_ACCOUNT_ERROR = _('User account is disabled.')
INVALID_TOKEN_ERROR = _('Invalid token for given user.')
PASSWORD_MISMATCH_ERROR = _('The two password fields didn\'t match.')
USERNAME_MISMATCH_ERROR = _('The two {0} fields didn\'t match.')
INVALID_PASSWORD_ERROR = _('Invalid password.')
from distutils import version
from django.contrib.auth import authenticate, get_user_model
from rest_framework import serializers
import rest_framework
from rest_framework.authtoken.models import Token
from . import constants, utils
User = get_user_model()
def create_username_field():
username_field = User._meta.get_field(User.USERNAME_FIELD)
if hasattr(serializers.ModelSerializer, 'field_mapping'): # DRF 2.x
mapping_dict = serializers.ModelSerializer.field_mapping
elif hasattr(serializers.ModelSerializer, '_field_mapping'): # DRF 3.0
mapping_dict = serializers.ModelSerializer._field_mapping.mapping
elif hasattr(serializers.ModelSerializer, 'serializer_field_mapping'): # DRF 3.1
mapping_dict = serializers.ModelSerializer.serializer_field_mapping
else:
raise AttributeError(
'serializers.ModelSerializer doesn\'t have any of these attributes: '
'field_mapping, _field_mapping, serializer_field_mapping '
)
field_class = mapping_dict[username_field.__class__]
return field_class()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = tuple(User.REQUIRED_FIELDS) + (
User._meta.pk.name,
User.USERNAME_FIELD,
)
read_only_fields = (
User.USERNAME_FIELD,
)
class AbstractUserRegistrationSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = tuple(User.REQUIRED_FIELDS) + (
User.USERNAME_FIELD,
User._meta.pk.name,
'password',
)
write_only_fields = (
'password',
)
if version.StrictVersion(rest_framework.VERSION) >= version.StrictVersion('3.0.0'):
class UserRegistrationSerializer(AbstractUserRegistrationSerializer):
def create(self, validated_data):
return User.objects.create_user(**validated_data)
else:
class UserRegistrationSerializer(AbstractUserRegistrationSerializer):
def restore_object(self, attrs, instance=None):
try:
return User.objects.get(**{User.USERNAME_FIELD: attrs[User.USERNAME_FIELD]})
except User.DoesNotExist:
return User.objects.create_user(**attrs)
def save_object(self, obj, **kwargs):
return obj
class UserRegistrationWithAuthTokenSerializer(UserRegistrationSerializer):
auth_token = serializers.SerializerMethodField(method_name='get_user_auth_token')
class Meta(UserRegistrationSerializer.Meta):
model = User
fields = UserRegistrationSerializer.Meta.fields + (
'auth_token',
)
def get_user_auth_token(self, obj):
return obj.auth_token.key
class LoginSerializer(serializers.Serializer):
password = serializers.CharField(required=False)
default_error_messages = {
'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
}
def __init__(self, *args, **kwargs):
super(LoginSerializer, self).__init__(*args, **kwargs)
self.user = None
self.fields[User.USERNAME_FIELD] = serializers.CharField(required=False)
def validate(self, attrs):
self.user = authenticate(username=attrs[User.USERNAME_FIELD], password=attrs['password'])
if self.user:
if not self.user.is_active:
raise serializers.ValidationError(self.error_messages['inactive_account'])
return attrs
else:
raise serializers.ValidationError(self.error_messages['invalid_credentials'])
class PasswordResetSerializer(serializers.Serializer):
email = serializers.EmailField()
class UidAndTokenSerializer(serializers.Serializer):
uid = serializers.CharField()
token = serializers.CharField()
default_error_messages = {
'invalid_token': constants.INVALID_TOKEN_ERROR
}
def validate_uid(self, attrs_or_value, source=None):
value = attrs_or_value[source] if source else attrs_or_value
try:
uid = utils.decode_uid(value)
self.user = User.objects.get(pk=uid)
except (User.DoesNotExist, ValueError, TypeError, ValueError, OverflowError) as error:
raise serializers.ValidationError(error)
return attrs_or_value
def validate(self, attrs):
attrs = super(UidAndTokenSerializer, self).validate(attrs)
if not self.context['view'].token_generator.check_token(self.user, attrs['token']):
raise serializers.ValidationError(self.error_messages['invalid_token'])
return attrs
class PasswordSerializer(serializers.Serializer):
new_password = serializers.CharField()
class PasswordRetypeSerializer(PasswordSerializer):
re_new_password = serializers.CharField()
default_error_messages = {
'password_mismatch': constants.PASSWORD_MISMATCH_ERROR,
}
def validate(self, attrs):
attrs = super(PasswordRetypeSerializer, self).validate(attrs)
if attrs['new_password'] != attrs['re_new_password']:
raise serializers.ValidationError(self.error_messages['password_mismatch'])
return attrs
class CurrentPasswordSerializer(serializers.Serializer):
current_password = serializers.CharField()
default_error_messages = {
'invalid_password': constants.INVALID_PASSWORD_ERROR,
}
def validate_current_password(self, attrs_or_value, source=None):
value = attrs_or_value[source] if source else attrs_or_value
if not self.context['request'].user.check_password(value):
raise serializers.ValidationError(self.error_messages['invalid_password'])
return attrs_or_value
class SetPasswordSerializer(PasswordSerializer, CurrentPasswordSerializer):
pass
class SetPasswordRetypeSerializer(PasswordRetypeSerializer, CurrentPasswordSerializer):
pass
class PasswordResetConfirmSerializer(UidAndTokenSerializer, PasswordSerializer):
pass
class PasswordResetConfirmRetypeSerializer(UidAndTokenSerializer, PasswordRetypeSerializer):
pass
class SetUsernameSerializer(serializers.ModelSerializer, CurrentPasswordSerializer):
class Meta(object):
model = User
fields = (
User.USERNAME_FIELD,
'current_password',
)
def __init__(self, *args, **kwargs):
super(SetUsernameSerializer, self).__init__(*args, **kwargs)
self.fields['new_' + User.USERNAME_FIELD] = self.fields[User.USERNAME_FIELD]
del self.fields[User.USERNAME_FIELD]
class SetUsernameRetypeSerializer(SetUsernameSerializer):
default_error_messages = {
'username_mismatch': constants.USERNAME_MISMATCH_ERROR.format(User.USERNAME_FIELD),
}
def __init__(self, *args, **kwargs):
super(SetUsernameRetypeSerializer, self).__init__(*args, **kwargs)
self.fields['re_new_' + User.USERNAME_FIELD] = serializers.CharField()
def validate(self, attrs):
attrs = super(SetUsernameRetypeSerializer, self).validate(attrs)
if User.USERNAME_FIELD in attrs:
new_username = attrs[User.USERNAME_FIELD]
else: # DRF 2.4
new_username = attrs['new_' + User.USERNAME_FIELD]
if new_username != attrs['re_new_' + User.USERNAME_FIELD]:
raise serializers.ValidationError(self.error_messages['username_mismatch'].format(User.USERNAME_FIELD))
return attrs
class TokenSerializer(serializers.ModelSerializer):
auth_token = serializers.CharField(source='key')
class Meta:
model = Token
fields = (
'auth_token',
)
from django.core.exceptions import ImproperlyConfigured
def get(key):
from django.conf import settings
defaults = {
'LOGIN_AFTER_REGISTRATION': False,
'LOGIN_AFTER_ACTIVATION': False,
'SEND_ACTIVATION_EMAIL': False,
'SET_PASSWORD_RETYPE': False,
'SET_USERNAME_RETYPE': False,
'PASSWORD_RESET_CONFIRM_RETYPE': False,
}
defaults.update(getattr(settings, 'DJOSER', {}))
try:
return defaults[key]
except KeyError:
raise ImproperlyConfigured('Missing settings: DJOSER[\'{}\']'.format(key))
\ No newline at end of file
from django.dispatch import Signal
# New user has registered.
user_registered = Signal(providing_args=["user", "request"])
# User has activated his or her account.
user_activated = Signal(providing_args=["user", "request"])
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you created an account on {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page to activate account:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}/{{ url }}
{% endblock %}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}
{% load i18n %}{% autoescape off %}
{% blocktrans %}Account activation on {{ site_name }}{% endblocktrans %}
{% endautoescape %}
\ No newline at end of file
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}/{{ url }}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}
{% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %}
{% endautoescape %}
\ No newline at end of file
from django.conf.urls import patterns, url
from . import views
from django.contrib.auth import get_user_model
User = get_user_model()
urlpatterns = patterns('',
url(r'^$', views.RootView.as_view(), name='root'),
url(r'^me/$', views.UserView.as_view(), name='user'),
url(r'^register/$', views.RegistrationView.as_view(), name='register'),
url(r'^login/$', views.LoginView.as_view(), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
url(r'^activate/$', views.ActivationView.as_view(), name='activate'),
url(r'^{0}/$'.format(User.USERNAME_FIELD), views.SetUsernameView.as_view(), name='set_username'),
url(r'^password/$', views.SetPasswordView.as_view(), name='set_password'),
url(r'^password/reset/$', views.PasswordResetView.as_view(), name='password_reset'),
url(r'^password/reset/confirm/$', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
)
from django.conf import settings as django_settings
from django.contrib.sites.models import get_current_site
from django.core.mail import EmailMultiAlternatives, EmailMessage
from django.template import loader
from rest_framework import response, status
def encode_uid(pk):
try:
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
return urlsafe_base64_encode(force_bytes(pk)).decode()
except ImportError:
from django.utils.http import int_to_base36
return int_to_base36(pk)
def decode_uid(pk):
try:
from django.utils.http import urlsafe_base64_decode
from django.utils.encoding import force_text
return force_text(urlsafe_base64_decode(pk))
except ImportError:
from django.utils.http import base36_to_int
return base36_to_int(pk)
def send_email(to_email, from_email, context, subject_template_name,
plain_body_template_name=None, html_body_template_name=None):
assert plain_body_template_name or html_body_template_name
subject = loader.render_to_string(subject_template_name, context)
subject = ''.join(subject.splitlines())
if plain_body_template_name:
plain_body = loader.render_to_string(plain_body_template_name, context)
email_message = EmailMultiAlternatives(subject, plain_body, from_email, [to_email])
if html_body_template_name:
html_body = loader.render_to_string(html_body_template_name, context)
email_message.attach_alternative(html_body, 'text/html')
else:
html_body = loader.render_to_string(html_body_template_name, context)
email_message = EmailMessage(subject, html_body, from_email, [to_email])
email_message.content_subtype = 'html'
email_message.send()
class ActionViewMixin(object):
def post(self, request):
serializer = self.get_serializer(data=request.DATA)
if serializer.is_valid():
return self.action(serializer)
else:
return response.Response(
data=serializer.errors,
status=status.HTTP_400_BAD_REQUEST,
)
class SendEmailViewMixin(object):
subject_template_name = None
plain_body_template_name = None
html_body_template_name = None
def send_email(self, to_email, from_email, context):
send_email(to_email, from_email, context, **self.get_send_email_extras())
def get_send_email_kwargs(self, user):
return {
'from_email': getattr(django_settings, 'DEFAULT_FROM_EMAIL', None),
'to_email': user.email,
'context': self.get_email_context(user),
}
def get_send_email_extras(self):
return {
'subject_template_name': self.get_subject_template_name(),
'plain_body_template_name': self.get_plain_body_template_name(),
'html_body_template_name': self.get_html_body_template_name(),
}
def get_subject_template_name(self):
return self.subject_template_name
def get_plain_body_template_name(self):
return self.plain_body_template_name
def get_html_body_template_name(self):
return self.html_body_template_name
def get_email_context(self, user):
token = self.token_generator.make_token(user)
uid = encode_uid(user.pk)
try:
domain = django_settings.DJOSER['DOMAIN']
site_name = django_settings.DJOSER['SITE_NAME']
except KeyError:
site = get_current_site(self.request)
domain, site_name = site.domain, site.name
return {
'user': user,
'domain': domain,
'site_name': site_name,
'uid': uid,
'token': token,
'protocol': 'https' if self.request.is_secure() else 'http',
}
from django.contrib.auth import get_user_model, user_logged_in, user_logged_out
from rest_framework import generics, permissions, status, response, views
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.reverse import reverse
from django.contrib.auth.tokens import default_token_generator
from . import serializers, settings, utils, signals
User = get_user_model()
class RootView(views.APIView):
"""
Root endpoint - use one of sub endpoints.
"""
def get(self, request, format=None):
urls_mapping = {
'me': 'user',
'register': 'register',
'login': 'login',
'logout': 'logout',
'activate': 'activate',
'change-' + User.USERNAME_FIELD: 'set_username',
'change-password': 'set_password',
'password-reset': 'password_reset',
'password-reset-confirm': 'password_reset_confirm',
}
return Response(
dict([(key, reverse(url_name, request=request, format=format))
for key, url_name in urls_mapping.items()])
)
class RegistrationView(utils.SendEmailViewMixin, generics.CreateAPIView):
"""
Use this endpoint to register new user.
"""
permission_classes = (
permissions.AllowAny,
)
token_generator = default_token_generator
subject_template_name = 'activation_email_subject.txt'
plain_body_template_name = 'activation_email_body.txt'
def get_serializer_class(self):
if settings.get('LOGIN_AFTER_REGISTRATION'):
return serializers.UserRegistrationWithAuthTokenSerializer
return serializers.UserRegistrationSerializer
def post_save(self, obj, created=False):
if settings.get('LOGIN_AFTER_REGISTRATION'):
Token.objects.get_or_create(user=obj)
if settings.get('SEND_ACTIVATION_EMAIL'):
self.send_email(**self.get_send_email_kwargs(obj))
def perform_create(self, serializer):
instance = serializer.save()
signals.user_registered.send(
sender=self.__class__, user=instance, request=self.request)
self.post_save(obj=instance, created=True)
def get_email_context(self, user):
context = super(RegistrationView, self).get_email_context(user)
context['url'] = settings.get('ACTIVATION_URL').format(**context)
return context
class LoginView(utils.ActionViewMixin, generics.GenericAPIView):
"""
Use this endpoint to obtain user authentication token.
"""
serializer_class = serializers.LoginSerializer
permission_classes = (
permissions.AllowAny,
)
def action(self, serializer):
user = serializer.user
token, _ = Token.objects.get_or_create(user=user)
user_logged_in.send(sender=user.__class__, request=self.request, user=user)
return Response(
data=serializers.TokenSerializer(token).data,
status=status.HTTP_200_OK,
)
class LogoutView(views.APIView):
"""
Use this endpoint to logout user (remove user authentication token).
"""
permission_classes = (
permissions.IsAuthenticated,
)
def post(self, request):
Token.objects.filter(user=request.user).delete()
user_logged_out.send(sender=request.user.__class__, request=request, user=request.user)
return response.Response(status=status.HTTP_200_OK)
class PasswordResetView(utils.ActionViewMixin, utils.SendEmailViewMixin, generics.GenericAPIView):
"""
Use this endpoint to send email to user with password reset link.
"""
serializer_class = serializers.PasswordResetSerializer
permission_classes = (
permissions.AllowAny,
)
token_generator = default_token_generator
subject_template_name = 'password_reset_email_subject.txt'
plain_body_template_name = 'password_reset_email_body.txt'
def action(self, serializer):
for user in self.get_users(serializer.data['email']):
self.send_email(**self.get_send_email_kwargs(user))
return response.Response(status=status.HTTP_200_OK)
def get_users(self, email):
active_users = User._default_manager.filter(
email__iexact=email,
is_active=True,
)
return (u for u in active_users if u.has_usable_password())
def get_email_context(self, user):
context = super(PasswordResetView, self).get_email_context(user)
context['url'] = settings.get('PASSWORD_RESET_CONFIRM_URL').format(**context)
return context
class SetPasswordView(utils.ActionViewMixin, generics.GenericAPIView):
"""
Use this endpoint to change user password.
"""
permission_classes = (
permissions.IsAuthenticated,
)
def get_serializer_class(self):
if settings.get('SET_PASSWORD_RETYPE'):
return serializers.SetPasswordRetypeSerializer
return serializers.SetPasswordSerializer
def action(self, serializer):
self.request.user.set_password(serializer.data['new_password'])
self.request.user.save()
return response.Response(status=status.HTTP_200_OK)
class PasswordResetConfirmView(utils.ActionViewMixin, generics.GenericAPIView):
"""
Use this endpoint to finish reset password process.
"""
permission_classes = (
permissions.AllowAny,
)
token_generator = default_token_generator
def get_serializer_class(self):
if settings.get('PASSWORD_RESET_CONFIRM_RETYPE'):
return serializers.PasswordResetConfirmRetypeSerializer
return serializers.PasswordResetConfirmSerializer
def action(self, serializer):
serializer.user.set_password(serializer.data['new_password'])
serializer.user.save()
return response.Response(status=status.HTTP_200_OK)
class ActivationView(utils.ActionViewMixin, generics.GenericAPIView):
"""
Use this endpoint to activate user account.
"""
serializer_class = serializers.UidAndTokenSerializer
permission_classes = (
permissions.AllowAny,
)
token_generator = default_token_generator
def action(self, serializer):
serializer.user.is_active = True
serializer.user.save()
signals.user_activated.send(
sender=self.__class__, user=serializer.user, request=self.request)
if settings.get('LOGIN_AFTER_ACTIVATION'):
token, _ = Token.objects.get_or_create(user=serializer.user)
data = serializers.TokenSerializer(token).data
else:
data = {}
return Response(data=data, status=status.HTTP_200_OK)
class SetUsernameView(utils.ActionViewMixin, generics.GenericAPIView):
"""
Use this endpoint to change user username.
"""
serializer_class = serializers.SetUsernameSerializer
permission_classes = (
permissions.IsAuthenticated,
)
def get_serializer_class(self):
if settings.get('SET_USERNAME_RETYPE'):
return serializers.SetUsernameRetypeSerializer
return serializers.SetUsernameSerializer
def action(self, serializer):
setattr(self.request.user, User.USERNAME_FIELD, serializer.data['new_' + User.USERNAME_FIELD])
self.request.user.save()
return response.Response(status=status.HTTP_200_OK)
class UserView(generics.RetrieveUpdateAPIView):
"""
Use this endpoint to retrieve/update user.
"""
model = User
serializer_class = serializers.UserSerializer
permission_classes = (
permissions.IsAuthenticated,
)
def get_object(self, *args, **kwargs):
return self.request.user
[egg_info]
tag_date = 0
tag_build =
tag_svn_revision = 0
#!/usr/bin/env python
from setuptools import setup
try:
import pypandoc
description = pypandoc.convert('README.md', 'rst')
except (IOError, ImportError):
description = open('README.md').read()
REQUIREMENTS = [i.strip() for i in open('requirements.txt').readlines()]
setup(
name='djoser',