Commit cf00f564 authored by Michael Fladischer's avatar Michael Fladischer

Import djoser_0.5.1.orig.tar.gz

parent cf83d7a8
This diff is collapsed.
......@@ -44,6 +44,7 @@ Supported Django versions:
* Django 1.7
* Django 1.8
* Django 1.9
* Django 1.10
Supported Django Rest Framework versions:
......@@ -91,7 +92,7 @@ REST_FRAMEWORK = {
}
```
Run migrations (if you are using Django 1.7+ or South) - this step will create tables for `auth` app:
Run migrations this step will create tables for `auth` app:
$ ./manage.py migrate
......@@ -131,7 +132,7 @@ REST_FRAMEWORK = {
}
```
Run migrations (if you are using Django 1.7+ or South) - this step will create tables for `auth` and `authtoken` apps:
Run migrations this step will create tables for `auth` and `authtoken` apps:
$ ./manage.py migrate
......@@ -271,7 +272,7 @@ URL: `/logout/`
* **response**
* status: `HTTP_200_OK` (success)
* status: `HTTP_204_NO_CONTENT` (success)
### Activate
......@@ -294,7 +295,7 @@ URL: `/activate/`
* **response**
* status: `HTTP_200_OK` (success)
* status: `HTTP_204_NO_CONTENT` (success)
### Set username
......@@ -316,7 +317,7 @@ URL: `/{{ User.USERNAME_FIELD }}/`
* **response**
* status: `HTTP_200_OK` (success)
* status: `HTTP_204_NO_CONTENT` (success)
### Set password
......@@ -338,7 +339,7 @@ URL: `/password/`
* **response**
* status: `HTTP_200_OK` (success)
* status: `HTTP_204_NO_CONTENT` (success)
### Reset password
......@@ -357,7 +358,8 @@ URL: `/password/reset/`
* **response**
* status: `HTTP_200_OK` (success)
* status: `HTTP_204_NO_CONTENT` (success), if `PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND` is `False` (default); or
* status: `HTTP_400_BAD_REQUEST`, if `PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND` is `True` and `email` does not exists in the database.
### Reset password confirmation
......@@ -384,7 +386,7 @@ URL: `/password/reset/confirm/`
* **response**
* status: `HTTP_200_OK` (success)
* status: `HTTP_204_NO_CONTENT` (success)
## Settings
......@@ -416,6 +418,12 @@ If `True`, register endpoint will send activation email to user.
**Default**: `False`
### SEND_CONFIRMATION_EMAIL
If `True`, register or activation endpoint will send confirmation email to user.
**Default**: `False`
### ACTIVATION_URL
URL to your frontend activation page. It should contain `{uid}` and `{token}`
......@@ -445,6 +453,26 @@ endpoint in order to validate password equality.
**Default**: `False`
### LOGOUT_ON_PASSWORD_CHANGE
If `True`, setting new password will logout the user.
**Default**: `False`
### PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND
If `True`, posting a non-existent `email` to `/password/reset/` will return
a `HTTP_400_BAD_REQUEST` response with an `EMAIL_NOT_FOUND` error message
('User with given email does not exist.').
If `False` (default), the `/password/reset/` endpoint will always return
a `HTTP_204_NO_CONTENT` response.
Please note that setting this to `True` will expose information whether
an email is registered in the system.
**Default**: `False`
### PASSWORD_VALIDATORS
List containing [REST Framework Validator](http://www.django-rest-framework.org/api-guide/validators/) functions.
......@@ -529,7 +557,7 @@ In this extremely short tutorial we are going to mimic the simplest flow: regist
`$ curl -X POST http://127.0.0.1:8088/auth/register/ --data 'username=djoser&password=djoser'`
`{"email": "", "username": "djoser"}`
`{"email": "", "username": "djoser", "id":1}`
So far, so good. We have just created a new user using REST API.
......@@ -559,7 +587,7 @@ In this extremely short tutorial we are going to mimic the simplest flow: regist
`$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`
`{"email": "", "username": "djoser"}`
`{"email": "", "username": "djoser", "id": 1}`
Yay, it works!
......
This diff is collapsed.
......@@ -16,6 +16,8 @@ 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/confirmation_email_body.txt
djoser/templates/confirmation_email_subject.txt
djoser/templates/password_reset_email_body.txt
djoser/templates/password_reset_email_subject.txt
djoser/urls/__init__.py
......
......@@ -8,3 +8,4 @@ STALE_TOKEN_ERROR = _('Stale 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.')
EMAIL_NOT_FOUND = _('User with given email does not exist.')
from django.contrib.auth import authenticate, get_user_model
from django.db import transaction
from django.utils import six
from django.utils.module_loading import import_string
from rest_framework import exceptions, serializers
from rest_framework.authtoken.models import Token
......@@ -37,10 +39,13 @@ class UserRegistrationSerializer(serializers.ModelSerializer):
)
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
if settings.get('SEND_ACTIVATION_EMAIL'):
user.is_active = False
user.save(update_fields=['is_active'])
with transaction.atomic():
user = User.objects.create_user(**validated_data)
user.is_active = False
user.save(update_fields=['is_active'])
else:
user = User.objects.create_user(**validated_data)
return user
......@@ -70,6 +75,16 @@ class LoginSerializer(serializers.Serializer):
class PasswordResetSerializer(serializers.Serializer):
email = serializers.EmailField()
default_error_messages = {
'email_not_found': constants.EMAIL_NOT_FOUND
}
def validate_email(self, value):
if settings.get('PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND') and \
not self.context['view'].get_users(value):
raise serializers.ValidationError(self.error_messages['email_not_found'])
return value
class UidAndTokenSerializer(serializers.Serializer):
uid = serializers.CharField()
......
......@@ -6,9 +6,11 @@ from django.conf import settings
default_settings = {
'SEND_ACTIVATION_EMAIL': False,
'SEND_CONFIRMATION_EMAIL': False,
'SET_PASSWORD_RETYPE': False,
'SET_USERNAME_RETYPE': False,
'PASSWORD_RESET_CONFIRM_RETYPE': False,
'PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND': False,
'ROOT_VIEW_URLS_MAPPING': {},
'PASSWORD_VALIDATORS': [],
'SERIALIZERS': {
......@@ -25,6 +27,7 @@ default_settings = {
'user': 'djoser.serializers.UserSerializer',
'token': 'djoser.serializers.TokenSerializer',
},
'LOGOUT_ON_PASSWORD_CHANGE': False,
}
......
{% load i18n %}{% autoescape off %}
{% trans "Your account has been created and is ready to use!" %}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}
\ No newline at end of file
{% load i18n %}{% autoescape off %}
{% blocktrans %}{{ site_name }} - Your account has been successfully created and activated!{% endblocktrans %}
{% endautoescape %}
from django.conf import settings as django_settings
from django.contrib.auth import user_logged_in, user_logged_out
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMultiAlternatives, EmailMessage
from django.template import loader
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_text
from rest_framework import response, status
from rest_framework import response, status, authtoken
from . import settings
def encode_uid(pk):
......@@ -15,24 +20,15 @@ def decode_uid(pk):
return force_text(urlsafe_base64_decode(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())
def login_user(request, user):
token, _ = authtoken.models.Token.objects.get_or_create(user=user)
user_logged_in.send(sender=user.__class__, request=request, user=user)
return token
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()
def logout_user(request):
authtoken.models.Token.objects.filter(user=request.user).delete()
user_logged_out.send(sender=request.user.__class__, request=request, user=request.user)
class ActionViewMixin(object):
......@@ -48,48 +44,79 @@ class ActionViewMixin(object):
)
class SendEmailViewMixin(object):
token_generator = None
class UserEmailFactoryBase(object):
token_generator = default_token_generator
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 __init__(self, from_email, user, protocol, domain, site_name):
self.from_email = from_email
self.user = user
self.domain = domain
self.site_name = site_name
self.protocol = protocol
@classmethod
def from_request(cls, request, user=None, from_email=None):
site = get_current_site(request)
return cls(
from_email=getattr(django_settings, 'DEFAULT_FROM_EMAIL', from_email),
user=user or request.user,
domain=django_settings.DJOSER.get('DOMAIN') or site.domain,
site_name=django_settings.DJOSER.get('SITE_NAME') or site.name,
protocol='https' if request.is_secure() else 'http',
)
def create(self,):
assert self.plain_body_template_name or self.html_body_template_name
context = self.get_context()
subject = loader.render_to_string(self.subject_template_name, context)
subject = ''.join(subject.splitlines())
if self.plain_body_template_name:
plain_body = loader.render_to_string(self.plain_body_template_name, context)
email_message = EmailMultiAlternatives(subject, plain_body, self.from_email, [self.user.email])
if self.html_body_template_name:
html_body = loader.render_to_string(self.html_body_template_name, context)
email_message.attach_alternative(html_body, 'text/html')
else:
html_body = loader.render_to_string(self.html_body_template_name, context)
email_message = EmailMessage(subject, html_body, self.from_email, [self.user.email])
email_message.content_subtype = 'html'
return email_message
def get_send_email_kwargs(self, user):
def get_context(self):
return {
'from_email': getattr(django_settings, 'DEFAULT_FROM_EMAIL', None),
'to_email': user.email,
'context': self.get_email_context(user),
'user': self.user,
'domain': self.domain,
'site_name': self.site_name,
'uid': encode_uid(self.user.pk),
'token': self.token_generator.make_token(self.user),
'protocol': self.protocol,
}
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
class UserActivationEmailFactory(UserEmailFactoryBase):
subject_template_name = 'activation_email_subject.txt'
plain_body_template_name = 'activation_email_body.txt'
def get_plain_body_template_name(self):
return self.plain_body_template_name
def get_context(self):
context = super(UserActivationEmailFactory, self).get_context()
context['url'] = settings.get('ACTIVATION_URL').format(**context)
return context
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)
domain = django_settings.DJOSER.get('DOMAIN') or get_current_site(self.request).domain
site_name = django_settings.DJOSER.get('SITE_NAME') or get_current_site(self.request).name
return {
'user': user,
'domain': domain,
'site_name': site_name,
'uid': uid,
'token': token,
'protocol': 'https' if self.request.is_secure() else 'http',
}
class UserPasswordResetEmailFactory(UserEmailFactoryBase):
subject_template_name = 'password_reset_email_subject.txt'
plain_body_template_name = 'password_reset_email_body.txt'
def get_context(self):
context = super(UserPasswordResetEmailFactory, self).get_context()
context['url'] = settings.get('PASSWORD_RESET_CONFIRM_URL').format(**context)
return context
class UserConfirmationEmailFactory(UserEmailFactoryBase):
subject_template_name = 'confirmation_email_subject.txt'
plain_body_template_name = 'confirmation_email_body.txt'
from django.contrib.auth import get_user_model, user_logged_in, user_logged_out
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth import get_user_model
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
......@@ -44,7 +44,7 @@ class RootView(views.APIView):
)
class RegistrationView(utils.SendEmailViewMixin, generics.CreateAPIView):
class RegistrationView(generics.CreateAPIView):
"""
Use this endpoint to register new user.
"""
......@@ -52,15 +52,24 @@ class RegistrationView(utils.SendEmailViewMixin, generics.CreateAPIView):
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 perform_create(self, serializer):
instance = serializer.save()
signals.user_registered.send(sender=self.__class__, user=instance, request=self.request)
user = serializer.save()
signals.user_registered.send(sender=self.__class__, user=user, request=self.request)
if settings.get('SEND_ACTIVATION_EMAIL'):
self.send_email(**self.get_send_email_kwargs(instance))
self.send_activation_email(user)
elif settings.get('SEND_CONFIRMATION_EMAIL'):
self.send_confirmation_email(user)
def send_activation_email(self, user):
email_factory = utils.UserActivationEmailFactory.from_request(self.request, user=user)
email = email_factory.create()
email.send()
def send_confirmation_email(self, user):
email_factory = utils.UserConfirmationEmailFactory.from_request(self.request, user=user)
email = email_factory.create()
email.send()
def get_email_context(self, user):
context = super(RegistrationView, self).get_email_context(user)
......@@ -78,9 +87,7 @@ class LoginView(utils.ActionViewMixin, generics.GenericAPIView):
)
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)
token = utils.login_user(self.request, serializer.user)
token_serializer_class = serializers.serializers_manager.get('token')
return Response(
data=token_serializer_class(token).data,
......@@ -97,12 +104,11 @@ class LogoutView(views.APIView):
)
def post(self, request):
Token.objects.filter(user=request.user).delete()
user_logged_out.send(sender=request.user.__class__, request=request, user=request.user)
utils.logout_user(request)
return response.Response(status=status.HTTP_204_NO_CONTENT)
class PasswordResetView(utils.ActionViewMixin, utils.SendEmailViewMixin, generics.GenericAPIView):
class PasswordResetView(utils.ActionViewMixin, generics.GenericAPIView):
"""
Use this endpoint to send email to user with password reset link.
"""
......@@ -110,26 +116,27 @@ class PasswordResetView(utils.ActionViewMixin, utils.SendEmailViewMixin, generic
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'
_users = None
def action(self, serializer):
for user in self.get_users(serializer.data['email']):
self.send_email(**self.get_send_email_kwargs(user))
self.send_password_reset_email(user)
return response.Response(status=status.HTTP_204_NO_CONTENT)
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())
if self._users is None:
active_users = User._default_manager.filter(
email__iexact=email,
is_active=True,
)
self._users = [u for u in active_users if u.has_usable_password()]
return self._users
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
def send_password_reset_email(self, user):
email_factory = utils.UserPasswordResetEmailFactory.from_request(self.request, user=user)
email = email_factory.create()
email.send()
class SetPasswordView(utils.ActionViewMixin, generics.GenericAPIView):
......@@ -148,6 +155,10 @@ class SetPasswordView(utils.ActionViewMixin, generics.GenericAPIView):
def action(self, serializer):
self.request.user.set_password(serializer.data['new_password'])
self.request.user.save()
if settings.get('LOGOUT_ON_PASSWORD_CHANGE'):
utils.logout_user(self.request)
return response.Response(status=status.HTTP_204_NO_CONTENT)
......@@ -186,6 +197,11 @@ class ActivationView(utils.ActionViewMixin, generics.GenericAPIView):
serializer.user.save()
signals.user_activated.send(
sender=self.__class__, user=serializer.user, request=self.request)
if settings.get('SEND_CONFIRMATION_EMAIL'):
email_factory = utils.UserConfirmationEmailFactory.from_request(
self.request, user=serializer.user)
email = email_factory.create()
email.send()
return Response(status=status.HTTP_204_NO_CONTENT)
......
......@@ -22,7 +22,7 @@ def get_requirements(file_name):
setup(
name='djoser',
version='0.5.0',
version='0.5.1',
packages=get_packages('djoser'),
license='MIT',
author='SUNSCRAPERS',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment