djoser-0.4.0/ 0000755 0000765 0000024 00000000000 12602523477 013426 5 ustar haxoza staff 0000000 0000000 djoser-0.4.0/djoser/ 0000755 0000765 0000024 00000000000 12602523477 014714 5 ustar haxoza staff 0000000 0000000 djoser-0.4.0/djoser/__init__.py 0000644 0000765 0000024 00000000000 12475550727 017021 0 ustar haxoza staff 0000000 0000000 djoser-0.4.0/djoser/constants.py 0000644 0000765 0000024 00000000655 12545220315 017277 0 ustar haxoza staff 0000000 0000000 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.')
djoser-0.4.0/djoser/serializers.py 0000644 0000765 0000024 00000013034 12602516157 017620 0 ustar haxoza staff 0000000 0000000 from django.contrib.auth import authenticate, get_user_model
from rest_framework import serializers
from rest_framework.authtoken.models import Token
from . import constants, utils
User = get_user_model()
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 UserRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = User
fields = tuple(User.REQUIRED_FIELDS) + (
User.USERNAME_FIELD,
User._meta.pk.name,
'password',
)
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class LoginSerializer(serializers.Serializer):
password = serializers.CharField(required=False, style={'input_type': 'password'})
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.get(User.USERNAME_FIELD), password=attrs.get('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, value):
try:
uid = utils.decode_uid(value)
self.user = User.objects.get(pk=uid)
except (User.DoesNotExist, ValueError, TypeError, OverflowError) as error:
raise serializers.ValidationError(error)
return 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(style={'input_type': 'password'})
class PasswordRetypeSerializer(PasswordSerializer):
re_new_password = serializers.CharField(style={'input_type': 'password'})
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(style={'input_type': 'password'})
default_error_messages = {
'invalid_password': constants.INVALID_PASSWORD_ERROR,
}
def validate_current_password(self, value):
if not self.context['request'].user.check_password(value):
raise serializers.ValidationError(self.error_messages['invalid_password'])
return 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)
new_username = attrs[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',
)
djoser-0.4.0/djoser/settings.py 0000644 0000765 0000024 00000001020 12602516157 017114 0 ustar haxoza staff 0000000 0000000 from django.core.exceptions import ImproperlyConfigured
def get(key):
from django.conf import settings
defaults = {
'SEND_ACTIVATION_EMAIL': False,
'SET_PASSWORD_RETYPE': False,
'SET_USERNAME_RETYPE': False,
'PASSWORD_RESET_CONFIRM_RETYPE': False,
'ROOT_VIEW_URLS_MAPPING': {},
}
defaults.update(getattr(settings, 'DJOSER', {}))
try:
return defaults[key]
except KeyError:
raise ImproperlyConfigured('Missing settings: DJOSER[\'{}\']'.format(key)) djoser-0.4.0/djoser/signals.py 0000644 0000765 0000024 00000000343 12545220315 016715 0 ustar haxoza staff 0000000 0000000 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"])
djoser-0.4.0/djoser/templates/ 0000755 0000765 0000024 00000000000 12602523477 016712 5 ustar haxoza staff 0000000 0000000 djoser-0.4.0/djoser/templates/activation_email_body.txt 0000644 0000765 0000024 00000000644 12475550727 024012 0 ustar haxoza staff 0000000 0000000 {% 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 %}
djoser-0.4.0/djoser/templates/activation_email_subject.txt 0000644 0000765 0000024 00000000200 12475550727 024500 0 ustar haxoza staff 0000000 0000000 {% load i18n %}{% autoescape off %}
{% blocktrans %}Account activation on {{ site_name }}{% endblocktrans %}
{% endautoescape %} djoser-0.4.0/djoser/templates/password_reset_email_body.txt 0000644 0000765 0000024 00000001027 12475550727 024711 0 ustar haxoza staff 0000000 0000000 {% 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 %}
djoser-0.4.0/djoser/templates/password_reset_email_subject.txt 0000644 0000765 0000024 00000000174 12475550727 025415 0 ustar haxoza staff 0000000 0000000 {% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %}
{% endautoescape %} djoser-0.4.0/djoser/urls/ 0000755 0000765 0000024 00000000000 12602523477 015701 5 ustar haxoza staff 0000000 0000000 djoser-0.4.0/djoser/urls/__init__.py 0000644 0000765 0000024 00000000036 12602516157 020006 0 ustar haxoza staff 0000000 0000000 from .base import urlpatterns
djoser-0.4.0/djoser/urls/authtoken.py 0000644 0000765 0000024 00000000576 12602516157 020262 0 ustar haxoza staff 0000000 0000000 from django.conf.urls import patterns, url
from djoser import views
from . import base
urlpatterns = base.base_urlpatterns + patterns('',
url(r'^login/$', views.LoginView.as_view(), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
url(r'^$', views.RootView.as_view(urls_extra_mapping={'login': 'login', 'logout': 'logout'}), name='root'),
)
djoser-0.4.0/djoser/urls/base.py 0000644 0000765 0000024 00000001537 12602516157 017170 0 ustar haxoza staff 0000000 0000000 from django.conf.urls import patterns, url
from djoser import views
from django.contrib.auth import get_user_model
User = get_user_model()
base_urlpatterns = patterns('',
url(r'^me/$', views.UserView.as_view(), name='user'),
url(r'^register/$', views.RegistrationView.as_view(), name='register'),
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'),
)
urlpatterns = base_urlpatterns + patterns('', url(r'^$', views.RootView.as_view(), name='root'))
djoser-0.4.0/djoser/utils.py 0000644 0000765 0000024 00000007657 12602516157 016442 0 ustar haxoza staff 0000000 0000000 from django.conf import settings as django_settings
from django.core.mail import EmailMultiAlternatives, EmailMessage
from django.template import loader
from rest_framework import response, status
try:
from django.contrib.sites.shortcuts import get_current_site
except ImportError:
from django.contrib.sites.models import get_current_site
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):
token_generator = None
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',
}
djoser-0.4.0/djoser/views.py 0000644 0000765 0000024 00000016411 12602516157 016423 0 ustar haxoza staff 0000000 0000000 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.
"""
permission_classes = (
permissions.AllowAny,
)
urls_mapping = {
'me': 'user',
'register': 'register',
'activate': 'activate',
'change-' + User.USERNAME_FIELD: 'set_username',
'change-password': 'set_password',
'password-reset': 'password_reset',
'password-reset-confirm': 'password_reset_confirm',
}
urls_extra_mapping = None
def get_urls_mapping(self, **kwargs):
mapping = self.urls_mapping.copy()
mapping.update(kwargs)
if self.urls_extra_mapping:
mapping.update(self.urls_extra_mapping)
mapping.update(settings.get('ROOT_VIEW_URLS_MAPPING'))
return mapping
def get(self, request, format=None):
return Response(
dict([(key, reverse(url_name, request=request, format=format))
for key, url_name in self.get_urls_mapping().items()])
)
class RegistrationView(utils.SendEmailViewMixin, generics.CreateAPIView):
"""
Use this endpoint to register new user.
"""
serializer_class = serializers.UserRegistrationSerializer
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)
if settings.get('SEND_ACTIVATION_EMAIL'):
self.send_email(**self.get_send_email_kwargs(instance))
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)
return Response(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
djoser-0.4.0/djoser.egg-info/ 0000755 0000765 0000024 00000000000 12602523477 016406 5 ustar haxoza staff 0000000 0000000 djoser-0.4.0/djoser.egg-info/dependency_links.txt 0000644 0000765 0000024 00000000001 12602523477 022454 0 ustar haxoza staff 0000000 0000000
djoser-0.4.0/djoser.egg-info/PKG-INFO 0000644 0000765 0000024 00000047647 12602523477 017525 0 ustar haxoza staff 0000000 0000000 Metadata-Version: 1.1
Name: djoser
Version: 0.4.0
Summary: REST version of Django authentication system.
Home-page: https://github.com/sunscrapers/djoser
Author: SUNSCRAPERS
Author-email: info@sunscrapers.com
License: MIT
Description: djoser
======
|Build Status| |Coverage Status|
REST implementation of `Django `__
authentication system. **Djoser** library provides a set of `Django Rest
Framework `__ views to handle
basic actions such as registration, login, logout, password reset and
account activation. It works with `custom user
model `__.
Instead of reusing Django code (e.g. ``PasswordResetForm``), we
reimplemented few things to fit better into `Single Page
App `__
architecture.
Developed by `SUNSCRAPERS `__ with passion &
patience.
Features
--------
Here is a list of supported authentication backends:
- `HTTP Basic
Auth `__
(Default)
- `Token based authentication from Django Rest
Framework `__
Available endpoints:
- ``/me/``
- ``/register/``
- ``/login/``
- ``/logout/``
- ``/activate/``
- ``/{{ User.USERNAME_FIELD }}/``
- ``/password/``
- ``/password/reset/``
- ``/password/reset/confirm/``
Supported Python versions:
- Python 2.6
- Python 2.7
- Python 3.4
Supported Django versions:
- Django 1.5
- Django 1.6
- Django 1.7
- Django 1.8
Supported Django Rest Framework versions:
- Django Rest Framework 3.x
For Django Rest Framework 2.4 support check `djoser
0.3.2 `__.
Installation
------------
Use ``pip``:
::
$ pip install djoser
Quick Start
-----------
Configure ``INSTALLED_APPS``:
.. code:: python
INSTALLED_APPS = (
'django.contrib.auth',
(...),
'rest_framework',
'djoser',
(...),
)
Configure ``urls.py``:
.. code:: python
urlpatterns = patterns('',
(...),
url(r'^auth/', include('djoser.urls')),
)
HTTP Basic Auth strategy is assumed by default as Django Rest Framework
does it. However you may want to set it explicitly:
.. code:: python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
),
}
Run migrations (if you are using Django 1.7+ or South) - this step will
create tables for ``auth`` app:
::
$ ./manage.py migrate
Customizing authentication backend
----------------------------------
Token Based Authentication
~~~~~~~~~~~~~~~~~~~~~~~~~~
Add ``'rest_framework.authtoken'`` to ``INSTALLED_APPS``:
.. code:: python
INSTALLED_APPS = (
'django.contrib.auth',
(...),
'rest_framework',
'rest_framework.authtoken',
'djoser',
(...),
)
Configure ``urls.py``. Pay attention to ``djoser.url.authtoken`` module
path.
.. code:: python
urlpatterns = patterns('',
(...),
url(r'^auth/', include('djoser.urls.authtoken')),
)
Set ``TokenAuthentication`` as default Django Rest Framework
authentication strategy:
.. code:: python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
}
Run migrations (if you are using Django 1.7+ or South) - this step will
create tables for ``auth`` and ``authtoken`` apps:
::
$ ./manage.py migrate
Settings
--------
Optionally add ``DJOSER`` settings:
.. code:: python
DJOSER = {
'DOMAIN': 'frontend.com',
'SITE_NAME': 'Frontend',
'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
'ACTIVATION_URL': '#/activate/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
}
Check "Settings" section for more info.
Endpoints
---------
User
~~~~
Use this endpoint to retrieve/update user.
``GET``
^^^^^^^
URL: ``/me/``
Retrieve user.
- **response**
- status: ``HTTP_200_OK`` (success)
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User._meta.pk.name }}``
``{{ User.REQUIRED_FIELDS }}``
``PUT``
^^^^^^^
URL: ``/me/``
Update user.
- **request**
- data:
``{{ User.REQUIRED_FIELDS }}``
- **response**
- status: ``HTTP_200_OK`` (success)
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User._meta.pk.name }}``
``{{ User.REQUIRED_FIELDS }}``
Register
~~~~~~~~
Use this endpoint to register new user. Your user model manager should
implement
`create\_user `__
method and have
`USERNAME\_FIELD `__
and
`REQUIRED\_FIELDS `__
fields.
``POST``
^^^^^^^^
URL: ``/register/``
- **request**
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User.REQUIRED_FIELDS }}``
``password``
- **response**
- status: ``HTTP_201_CREATED`` (success)
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User._meta.pk.name }}``
``{{ User.REQUIRED_FIELDS }}``
Login
~~~~~
Use this endpoint to obtain user `authentication
token `__.
``POST``
^^^^^^^^
URL: ``/login/``
- **request**
- data:
``{{ User.USERNAME_FIELD }}``
``password``
- **response**
- status: ``HTTP_200_OK`` (success)
- data:
``auth_token``
Logout
~~~~~~
Use this endpoint to logout user (remove user authentication token).
``POST``
^^^^^^^^
URL: ``/logout/``
- **response**
- status: ``HTTP_200_OK`` (success)
Activate
~~~~~~~~
Use this endpoint to activate user account. This endpoint is not a URL
which will be directly exposed to your users - you should provide site
in your frontend application (configured by ``ACTIVATION_URL``) which
will send ``POST`` request to activate endpoint.
``POST``
^^^^^^^^
URL: ``/activate/``
- **request**
- data:
``uid``
``token``
- **response**
- status: ``HTTP_200_OK`` (success)
Set username
~~~~~~~~~~~~
Use this endpoint to change user username (``USERNAME_FIELD``).
``POST``
^^^^^^^^
URL: ``/{{ User.USERNAME_FIELD }}/``
- **request**
- data:
``new_{{ User.USERNAME_FIELD }}``
``re_new_{{ User.USERNAME_FIELD }}`` (if ``SET_USERNAME_RETYPE``
is ``True``)
``current_password``
- **response**
- status: ``HTTP_200_OK`` (success)
Set password
~~~~~~~~~~~~
Use this endpoint to change user password.
``POST``
^^^^^^^^
URL: ``/password/``
- **request**
- data:
``new_password``
``re_new_password`` (if ``SET_PASSWORD_RETYPE`` is ``True``)
``current_password``
- **response**
- status: ``HTTP_200_OK`` (success)
Reset password
~~~~~~~~~~~~~~
Use this endpoint to send email to user with password reset link. You
have to setup ``PASSWORD_RESET_CONFIRM_URL``.
``POST``
^^^^^^^^
URL: ``/password/reset/``
- **request**
- data:
``email``
- **response**
- status: ``HTTP_200_OK`` (success)
Reset password confirmation
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use this endpoint to finish reset password process. This endpoint is not
a URL which will be directly exposed to your users - you should provide
site in your frontend application (configured by
``PASSWORD_RESET_CONFIRM_URL``) which will send ``POST`` request to
reset password confirmation endpoint.
``POST``
^^^^^^^^
URL: ``/password/reset/confirm/``
- **request**
- data:
``uid``
``token``
``new_password``
``re_new_password`` (if ``PASSWORD_RESET_CONFIRM_RETYPE`` is
``True``)
- **response**
- status: ``HTTP_200_OK`` (success)
Settings
--------
DOMAIN
~~~~~~
Domain of your frontend app. If not provided, domain of current site
will be used.
**Required**: ``False``
SITE\_NAME
~~~~~~~~~~
Name of your frontend app. If not provided, name of current site will be
used.
**Required**: ``False``
PASSWORD\_RESET\_CONFIRM\_URL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
URL to your frontend password reset page. It should contain ``{uid}``
and ``{token}`` placeholders, e.g. ``#/password-reset/{uid}/{token}``.
You should pass ``uid`` and ``token`` to reset password confirmation
endpoint.
**Required**: ``True``
SEND\_ACTIVATION\_EMAIL
~~~~~~~~~~~~~~~~~~~~~~~
If ``True``, register endpoint will send activation email to user.
**Default**: ``False``
ACTIVATION\_URL
~~~~~~~~~~~~~~~
URL to your frontend activation page. It should contain ``{uid}`` and
``{token}`` placeholders, e.g. ``#/activate/{uid}/{token}``. You should
pass ``uid`` and ``token`` to activation endpoint.
**Required**: ``True``
SET\_USERNAME\_RETYPE
~~~~~~~~~~~~~~~~~~~~~
If ``True``, you need to pass ``re_new_{{ User.USERNAME_FIELD }}`` to
``/{{ User.USERNAME_FIELD }}/`` endpoint, to validate username equality.
**Default**: ``False``
SET\_PASSWORD\_RETYPE
~~~~~~~~~~~~~~~~~~~~~
If ``True``, you need to pass ``re_new_password`` to ``/password/``
endpoint, to validate password equality.
**Default**: ``False``
PASSWORD\_RESET\_CONFIRM\_RETYPE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If ``True``, you need to pass ``re_new_password`` to
``/password/reset/confirm/`` endpoint in order to validate password
equality.
**Default**: ``False``
Emails
------
There are few email templates which you may want to override:
- ``activation_email_body.txt``
- ``activation_email_subject.txt``
- ``password_reset_email_body.txt``
- ``password_reset_email_subject.txt``
All of them have following context:
- ``user``
- ``domain``
- ``site_name``
- ``url``
- ``uid``
- ``token``
- ``protocol``
Sample usage
------------
We provide a standalone test app for you to start easily, see how
everything works with basic settings. It might be useful before
integrating **djoser** into your backend application.
In this extremely short tutorial we are going to mimic the simplest
flow: register user, log in and log out. We will also check resource
access on each consecutive step. Let's go!
- Clone repository and install **djoser** to your virtualenv:
``$ git clone git@github.com:sunscrapers/djoser.git``
``$ cd djoser``
``$ pip install -e .``
- Go to the ``testproject`` directory, migrate the database and start
the development server:
``$ cd testproject``
``$ ./manage.py migrate``
``$ ./manage.py runserver 8088``
- Register a new user:
``$ curl -X POST http://127.0.0.1:8088/auth/register/ --data 'username=djoser&password=djoser'``
``{"email": "", "username": "djoser"}``
So far, so good. We have just created a new user using REST API.
- Let's access user's details:
``$ curl -X GET http://127.0.0.1:8088/auth/me/``
``{"detail": "Authentication credentials were not provided."}``
As we can see, we cannot access user profile without logging in.
Pretty obvious.
- Let's log in:
``curl -X POST http://127.0.0.1:8088/auth/login/ --data 'username=djoser&password=djoser'``
``{"auth_token": "b704c9fc3655635646356ac2950269f352ea1139"}``
We have just obtained an authorization token that we may use later in
order to retrieve specific resources.
- Let's access user's details again:
``$ curl -X GET http://127.0.0.1:8088/auth/me/``
``{"detail": "Authentication credentials were not provided."}``
Access is still forbidden but let's offer the token we obtained:
``$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'``
``{"email": "", "username": "djoser"}``
Yay, it works!
- Now let's log out:
``curl -X POST http://127.0.0.1:8088/auth/logout/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'``
And try access user profile again:
``$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'``
``{"detail": "Invalid token"}``
As we can see, user has been logged out successfully and the proper
token has been removed.
Customization
-------------
If you need to override some ``djoser`` behaviour, you could define your
custom view/serializer.
Define custom urls instead of reusing ``djoser.urls``:
.. code:: python
urlpatterns = patterns('',
(...),
url(r'^register/$', views.CustomRegistrationView.as_view()),
)
Define custom view/serializer (inherit from one of ``djoser`` class) and
override necessary method/field:
.. code:: python
class CustomRegistrationView(djoser.views.RegistrationView):
def send_email(self, *args, **kwargs):
your_custom_email_sender(*args, **kwargs)
You could check ``djoser`` API in source code:
- `djoser.views `__
- `djoser.serializers `__
Development
-----------
To start developing on **djoser**, clone the repository:
``$ git clone git@github.com:sunscrapers/djoser.git``
In order to run the tests create virtualenv, go to repo directory and
then:
``$ pip install -r requirements-test.txt``
``$ cd testproject``
``$ ./manage.py migrate``
``$ ./manage.py test``
Similar projects
----------------
List of projects related to Django, REST and authentication:
- `django-rest-auth `__
- `django-rest-framework-digestauth `__
- `django-oauth-toolkit `__
- `doac `__
- `django-rest-framework-jwt `__
- `django-rest-framework-httpsignature `__
- `hawkrest `__
.. |Build Status| image:: https://travis-ci.org/sunscrapers/djoser.svg?branch=master
:target: https://travis-ci.org/sunscrapers/djoser
.. |Coverage Status| image:: https://coveralls.io/repos/sunscrapers/djoser/badge.png?branch=master
:target: https://coveralls.io/r/sunscrapers/djoser?branch=master
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
djoser-0.4.0/djoser.egg-info/requires.txt 0000644 0000765 0000024 00000000047 12602523477 021007 0 ustar haxoza staff 0000000 0000000 Django>=1.5
djangorestframework>=3.0.0
djoser-0.4.0/djoser.egg-info/SOURCES.txt 0000644 0000765 0000024 00000001105 12602523477 020267 0 ustar haxoza staff 0000000 0000000 MANIFEST.in
README.md
requirements.txt
setup.py
djoser/__init__.py
djoser/constants.py
djoser/serializers.py
djoser/settings.py
djoser/signals.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
djoser/urls/__init__.py
djoser/urls/authtoken.py
djoser/urls/base.py djoser-0.4.0/djoser.egg-info/top_level.txt 0000644 0000765 0000024 00000000023 12602523477 021133 0 ustar haxoza staff 0000000 0000000 djoser
djoser/urls
djoser-0.4.0/MANIFEST.in 0000644 0000765 0000024 00000000106 12475550727 015167 0 ustar haxoza staff 0000000 0000000 include README.md
include djoser/templates/*
include requirements.txt
djoser-0.4.0/PKG-INFO 0000644 0000765 0000024 00000047647 12602523477 014545 0 ustar haxoza staff 0000000 0000000 Metadata-Version: 1.1
Name: djoser
Version: 0.4.0
Summary: REST version of Django authentication system.
Home-page: https://github.com/sunscrapers/djoser
Author: SUNSCRAPERS
Author-email: info@sunscrapers.com
License: MIT
Description: djoser
======
|Build Status| |Coverage Status|
REST implementation of `Django `__
authentication system. **Djoser** library provides a set of `Django Rest
Framework `__ views to handle
basic actions such as registration, login, logout, password reset and
account activation. It works with `custom user
model `__.
Instead of reusing Django code (e.g. ``PasswordResetForm``), we
reimplemented few things to fit better into `Single Page
App `__
architecture.
Developed by `SUNSCRAPERS `__ with passion &
patience.
Features
--------
Here is a list of supported authentication backends:
- `HTTP Basic
Auth `__
(Default)
- `Token based authentication from Django Rest
Framework `__
Available endpoints:
- ``/me/``
- ``/register/``
- ``/login/``
- ``/logout/``
- ``/activate/``
- ``/{{ User.USERNAME_FIELD }}/``
- ``/password/``
- ``/password/reset/``
- ``/password/reset/confirm/``
Supported Python versions:
- Python 2.6
- Python 2.7
- Python 3.4
Supported Django versions:
- Django 1.5
- Django 1.6
- Django 1.7
- Django 1.8
Supported Django Rest Framework versions:
- Django Rest Framework 3.x
For Django Rest Framework 2.4 support check `djoser
0.3.2 `__.
Installation
------------
Use ``pip``:
::
$ pip install djoser
Quick Start
-----------
Configure ``INSTALLED_APPS``:
.. code:: python
INSTALLED_APPS = (
'django.contrib.auth',
(...),
'rest_framework',
'djoser',
(...),
)
Configure ``urls.py``:
.. code:: python
urlpatterns = patterns('',
(...),
url(r'^auth/', include('djoser.urls')),
)
HTTP Basic Auth strategy is assumed by default as Django Rest Framework
does it. However you may want to set it explicitly:
.. code:: python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
),
}
Run migrations (if you are using Django 1.7+ or South) - this step will
create tables for ``auth`` app:
::
$ ./manage.py migrate
Customizing authentication backend
----------------------------------
Token Based Authentication
~~~~~~~~~~~~~~~~~~~~~~~~~~
Add ``'rest_framework.authtoken'`` to ``INSTALLED_APPS``:
.. code:: python
INSTALLED_APPS = (
'django.contrib.auth',
(...),
'rest_framework',
'rest_framework.authtoken',
'djoser',
(...),
)
Configure ``urls.py``. Pay attention to ``djoser.url.authtoken`` module
path.
.. code:: python
urlpatterns = patterns('',
(...),
url(r'^auth/', include('djoser.urls.authtoken')),
)
Set ``TokenAuthentication`` as default Django Rest Framework
authentication strategy:
.. code:: python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
}
Run migrations (if you are using Django 1.7+ or South) - this step will
create tables for ``auth`` and ``authtoken`` apps:
::
$ ./manage.py migrate
Settings
--------
Optionally add ``DJOSER`` settings:
.. code:: python
DJOSER = {
'DOMAIN': 'frontend.com',
'SITE_NAME': 'Frontend',
'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
'ACTIVATION_URL': '#/activate/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
}
Check "Settings" section for more info.
Endpoints
---------
User
~~~~
Use this endpoint to retrieve/update user.
``GET``
^^^^^^^
URL: ``/me/``
Retrieve user.
- **response**
- status: ``HTTP_200_OK`` (success)
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User._meta.pk.name }}``
``{{ User.REQUIRED_FIELDS }}``
``PUT``
^^^^^^^
URL: ``/me/``
Update user.
- **request**
- data:
``{{ User.REQUIRED_FIELDS }}``
- **response**
- status: ``HTTP_200_OK`` (success)
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User._meta.pk.name }}``
``{{ User.REQUIRED_FIELDS }}``
Register
~~~~~~~~
Use this endpoint to register new user. Your user model manager should
implement
`create\_user `__
method and have
`USERNAME\_FIELD `__
and
`REQUIRED\_FIELDS `__
fields.
``POST``
^^^^^^^^
URL: ``/register/``
- **request**
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User.REQUIRED_FIELDS }}``
``password``
- **response**
- status: ``HTTP_201_CREATED`` (success)
- data:
``{{ User.USERNAME_FIELD }}``
``{{ User._meta.pk.name }}``
``{{ User.REQUIRED_FIELDS }}``
Login
~~~~~
Use this endpoint to obtain user `authentication
token `__.
``POST``
^^^^^^^^
URL: ``/login/``
- **request**
- data:
``{{ User.USERNAME_FIELD }}``
``password``
- **response**
- status: ``HTTP_200_OK`` (success)
- data:
``auth_token``
Logout
~~~~~~
Use this endpoint to logout user (remove user authentication token).
``POST``
^^^^^^^^
URL: ``/logout/``
- **response**
- status: ``HTTP_200_OK`` (success)
Activate
~~~~~~~~
Use this endpoint to activate user account. This endpoint is not a URL
which will be directly exposed to your users - you should provide site
in your frontend application (configured by ``ACTIVATION_URL``) which
will send ``POST`` request to activate endpoint.
``POST``
^^^^^^^^
URL: ``/activate/``
- **request**
- data:
``uid``
``token``
- **response**
- status: ``HTTP_200_OK`` (success)
Set username
~~~~~~~~~~~~
Use this endpoint to change user username (``USERNAME_FIELD``).
``POST``
^^^^^^^^
URL: ``/{{ User.USERNAME_FIELD }}/``
- **request**
- data:
``new_{{ User.USERNAME_FIELD }}``
``re_new_{{ User.USERNAME_FIELD }}`` (if ``SET_USERNAME_RETYPE``
is ``True``)
``current_password``
- **response**
- status: ``HTTP_200_OK`` (success)
Set password
~~~~~~~~~~~~
Use this endpoint to change user password.
``POST``
^^^^^^^^
URL: ``/password/``
- **request**
- data:
``new_password``
``re_new_password`` (if ``SET_PASSWORD_RETYPE`` is ``True``)
``current_password``
- **response**
- status: ``HTTP_200_OK`` (success)
Reset password
~~~~~~~~~~~~~~
Use this endpoint to send email to user with password reset link. You
have to setup ``PASSWORD_RESET_CONFIRM_URL``.
``POST``
^^^^^^^^
URL: ``/password/reset/``
- **request**
- data:
``email``
- **response**
- status: ``HTTP_200_OK`` (success)
Reset password confirmation
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use this endpoint to finish reset password process. This endpoint is not
a URL which will be directly exposed to your users - you should provide
site in your frontend application (configured by
``PASSWORD_RESET_CONFIRM_URL``) which will send ``POST`` request to
reset password confirmation endpoint.
``POST``
^^^^^^^^
URL: ``/password/reset/confirm/``
- **request**
- data:
``uid``
``token``
``new_password``
``re_new_password`` (if ``PASSWORD_RESET_CONFIRM_RETYPE`` is
``True``)
- **response**
- status: ``HTTP_200_OK`` (success)
Settings
--------
DOMAIN
~~~~~~
Domain of your frontend app. If not provided, domain of current site
will be used.
**Required**: ``False``
SITE\_NAME
~~~~~~~~~~
Name of your frontend app. If not provided, name of current site will be
used.
**Required**: ``False``
PASSWORD\_RESET\_CONFIRM\_URL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
URL to your frontend password reset page. It should contain ``{uid}``
and ``{token}`` placeholders, e.g. ``#/password-reset/{uid}/{token}``.
You should pass ``uid`` and ``token`` to reset password confirmation
endpoint.
**Required**: ``True``
SEND\_ACTIVATION\_EMAIL
~~~~~~~~~~~~~~~~~~~~~~~
If ``True``, register endpoint will send activation email to user.
**Default**: ``False``
ACTIVATION\_URL
~~~~~~~~~~~~~~~
URL to your frontend activation page. It should contain ``{uid}`` and
``{token}`` placeholders, e.g. ``#/activate/{uid}/{token}``. You should
pass ``uid`` and ``token`` to activation endpoint.
**Required**: ``True``
SET\_USERNAME\_RETYPE
~~~~~~~~~~~~~~~~~~~~~
If ``True``, you need to pass ``re_new_{{ User.USERNAME_FIELD }}`` to
``/{{ User.USERNAME_FIELD }}/`` endpoint, to validate username equality.
**Default**: ``False``
SET\_PASSWORD\_RETYPE
~~~~~~~~~~~~~~~~~~~~~
If ``True``, you need to pass ``re_new_password`` to ``/password/``
endpoint, to validate password equality.
**Default**: ``False``
PASSWORD\_RESET\_CONFIRM\_RETYPE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If ``True``, you need to pass ``re_new_password`` to
``/password/reset/confirm/`` endpoint in order to validate password
equality.
**Default**: ``False``
Emails
------
There are few email templates which you may want to override:
- ``activation_email_body.txt``
- ``activation_email_subject.txt``
- ``password_reset_email_body.txt``
- ``password_reset_email_subject.txt``
All of them have following context:
- ``user``
- ``domain``
- ``site_name``
- ``url``
- ``uid``
- ``token``
- ``protocol``
Sample usage
------------
We provide a standalone test app for you to start easily, see how
everything works with basic settings. It might be useful before
integrating **djoser** into your backend application.
In this extremely short tutorial we are going to mimic the simplest
flow: register user, log in and log out. We will also check resource
access on each consecutive step. Let's go!
- Clone repository and install **djoser** to your virtualenv:
``$ git clone git@github.com:sunscrapers/djoser.git``
``$ cd djoser``
``$ pip install -e .``
- Go to the ``testproject`` directory, migrate the database and start
the development server:
``$ cd testproject``
``$ ./manage.py migrate``
``$ ./manage.py runserver 8088``
- Register a new user:
``$ curl -X POST http://127.0.0.1:8088/auth/register/ --data 'username=djoser&password=djoser'``
``{"email": "", "username": "djoser"}``
So far, so good. We have just created a new user using REST API.
- Let's access user's details:
``$ curl -X GET http://127.0.0.1:8088/auth/me/``
``{"detail": "Authentication credentials were not provided."}``
As we can see, we cannot access user profile without logging in.
Pretty obvious.
- Let's log in:
``curl -X POST http://127.0.0.1:8088/auth/login/ --data 'username=djoser&password=djoser'``
``{"auth_token": "b704c9fc3655635646356ac2950269f352ea1139"}``
We have just obtained an authorization token that we may use later in
order to retrieve specific resources.
- Let's access user's details again:
``$ curl -X GET http://127.0.0.1:8088/auth/me/``
``{"detail": "Authentication credentials were not provided."}``
Access is still forbidden but let's offer the token we obtained:
``$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'``
``{"email": "", "username": "djoser"}``
Yay, it works!
- Now let's log out:
``curl -X POST http://127.0.0.1:8088/auth/logout/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'``
And try access user profile again:
``$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'``
``{"detail": "Invalid token"}``
As we can see, user has been logged out successfully and the proper
token has been removed.
Customization
-------------
If you need to override some ``djoser`` behaviour, you could define your
custom view/serializer.
Define custom urls instead of reusing ``djoser.urls``:
.. code:: python
urlpatterns = patterns('',
(...),
url(r'^register/$', views.CustomRegistrationView.as_view()),
)
Define custom view/serializer (inherit from one of ``djoser`` class) and
override necessary method/field:
.. code:: python
class CustomRegistrationView(djoser.views.RegistrationView):
def send_email(self, *args, **kwargs):
your_custom_email_sender(*args, **kwargs)
You could check ``djoser`` API in source code:
- `djoser.views `__
- `djoser.serializers `__
Development
-----------
To start developing on **djoser**, clone the repository:
``$ git clone git@github.com:sunscrapers/djoser.git``
In order to run the tests create virtualenv, go to repo directory and
then:
``$ pip install -r requirements-test.txt``
``$ cd testproject``
``$ ./manage.py migrate``
``$ ./manage.py test``
Similar projects
----------------
List of projects related to Django, REST and authentication:
- `django-rest-auth `__
- `django-rest-framework-digestauth `__
- `django-oauth-toolkit `__
- `doac `__
- `django-rest-framework-jwt `__
- `django-rest-framework-httpsignature `__
- `hawkrest `__
.. |Build Status| image:: https://travis-ci.org/sunscrapers/djoser.svg?branch=master
:target: https://travis-ci.org/sunscrapers/djoser
.. |Coverage Status| image:: https://coveralls.io/repos/sunscrapers/djoser/badge.png?branch=master
:target: https://coveralls.io/r/sunscrapers/djoser?branch=master
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
djoser-0.4.0/README.md 0000644 0000765 0000024 00000032061 12602516322 014676 0 ustar haxoza staff 0000000 0000000 # djoser
[](https://travis-ci.org/sunscrapers/djoser)
[](https://coveralls.io/r/sunscrapers/djoser?branch=master)
REST implementation of [Django](https://www.djangoproject.com/) authentication
system. **Djoser** library provides a set of [Django Rest Framework](http://www.django-rest-framework.org/)
views to handle basic actions such as registration, login, logout, password
reset and account activation. It works with [custom user model](https://docs.djangoproject.com/en/dev/topics/auth/customizing/).
Instead of reusing Django code (e.g. `PasswordResetForm`), we reimplemented
few things to fit better into [Single Page App](http://en.wikipedia.org/wiki/Single-page_application)
architecture.
Developed by [SUNSCRAPERS](http://sunscrapers.com/) with passion & patience.
## Features
Here is a list of supported authentication backends:
* [HTTP Basic Auth](http://www.django-rest-framework.org/api-guide/authentication/#basicauthentication) (Default)
* [Token based authentication from Django Rest Framework](http://www.django-rest-framework.org/api-guide/authentication#tokenauthentication)
Available endpoints:
* `/me/`
* `/register/`
* `/login/`
* `/logout/`
* `/activate/`
* `/{{ User.USERNAME_FIELD }}/`
* `/password/`
* `/password/reset/`
* `/password/reset/confirm/`
Supported Python versions:
* Python 2.6
* Python 2.7
* Python 3.4
Supported Django versions:
* Django 1.5
* Django 1.6
* Django 1.7
* Django 1.8
Supported Django Rest Framework versions:
* Django Rest Framework 3.x
For Django Rest Framework 2.4 support check [djoser 0.3.2](https://github.com/sunscrapers/djoser/tree/0.3.2).
## Installation
Use `pip`:
$ pip install djoser
## Quick Start
Configure `INSTALLED_APPS`:
```python
INSTALLED_APPS = (
'django.contrib.auth',
(...),
'rest_framework',
'djoser',
(...),
)
```
Configure `urls.py`:
```python
urlpatterns = patterns('',
(...),
url(r'^auth/', include('djoser.urls')),
)
```
HTTP Basic Auth strategy is assumed by default as Django Rest Framework does it. However you may want to set it
explicitly:
```python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
),
}
```
Run migrations (if you are using Django 1.7+ or South) - this step will create tables for `auth` app:
$ ./manage.py migrate
## Customizing authentication backend
### Token Based Authentication
Add `'rest_framework.authtoken'` to `INSTALLED_APPS`:
```python
INSTALLED_APPS = (
'django.contrib.auth',
(...),
'rest_framework',
'rest_framework.authtoken',
'djoser',
(...),
)
```
Configure `urls.py`. Pay attention to `djoser.url.authtoken` module path.
```python
urlpatterns = patterns('',
(...),
url(r'^auth/', include('djoser.urls.authtoken')),
)
```
Set `TokenAuthentication` as default Django Rest Framework authentication strategy:
```python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
}
```
Run migrations (if you are using Django 1.7+ or South) - this step will create tables for `auth` and `authtoken` apps:
$ ./manage.py migrate
## Settings
Optionally add `DJOSER` settings:
```python
DJOSER = {
'DOMAIN': 'frontend.com',
'SITE_NAME': 'Frontend',
'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
'ACTIVATION_URL': '#/activate/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
}
```
Check "Settings" section for more info.
## Endpoints
### User
Use this endpoint to retrieve/update user.
#### `GET`
URL: `/me/`
Retrieve user.
* **response**
* status: `HTTP_200_OK` (success)
* data:
`{{ User.USERNAME_FIELD }}`
`{{ User._meta.pk.name }}`
`{{ User.REQUIRED_FIELDS }}`
#### `PUT`
URL: `/me/`
Update user.
* **request**
* data:
`{{ User.REQUIRED_FIELDS }}`
* **response**
* status: `HTTP_200_OK` (success)
* data:
`{{ User.USERNAME_FIELD }}`
`{{ User._meta.pk.name }}`
`{{ User.REQUIRED_FIELDS }}`
### Register
Use this endpoint to register new user. Your user model manager should
implement [create_user](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.UserManager.create_user)
method and have [USERNAME_FIELD](https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.CustomUser.USERNAME_FIELD)
and [REQUIRED_FIELDS](https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.CustomUser.REQUIRED_FIELDS)
fields.
#### `POST`
URL: `/register/`
* **request**
* data:
`{{ User.USERNAME_FIELD }}`
`{{ User.REQUIRED_FIELDS }}`
`password`
* **response**
* status: `HTTP_201_CREATED` (success)
* data:
`{{ User.USERNAME_FIELD }}`
`{{ User._meta.pk.name }}`
`{{ User.REQUIRED_FIELDS }}`
### Login
Use this endpoint to obtain user [authentication token](http://www.django-rest-framework.org/api-guide/authentication#tokenauthentication).
#### `POST`
URL: `/login/`
* **request**
* data:
`{{ User.USERNAME_FIELD }}`
`password`
* **response**
* status: `HTTP_200_OK` (success)
* data:
`auth_token`
### Logout
Use this endpoint to logout user (remove user authentication token).
#### `POST`
URL: `/logout/`
* **response**
* status: `HTTP_200_OK` (success)
### Activate
Use this endpoint to activate user account. This endpoint is not a URL which
will be directly exposed to your users - you should provide site in your
frontend application (configured by `ACTIVATION_URL`) which will send `POST`
request to activate endpoint.
#### `POST`
URL: `/activate/`
* **request**
* data:
`uid`
`token`
* **response**
* status: `HTTP_200_OK` (success)
### Set username
Use this endpoint to change user username (`USERNAME_FIELD`).
#### `POST`
URL: `/{{ User.USERNAME_FIELD }}/`
* **request**
* data:
`new_{{ User.USERNAME_FIELD }}`
`re_new_{{ User.USERNAME_FIELD }}` (if `SET_USERNAME_RETYPE` is `True`)
`current_password`
* **response**
* status: `HTTP_200_OK` (success)
### Set password
Use this endpoint to change user password.
#### `POST`
URL: `/password/`
* **request**
* data:
`new_password`
`re_new_password` (if `SET_PASSWORD_RETYPE` is `True`)
`current_password`
* **response**
* status: `HTTP_200_OK` (success)
### Reset password
Use this endpoint to send email to user with password reset link. You have to
setup `PASSWORD_RESET_CONFIRM_URL`.
#### `POST`
URL: `/password/reset/`
* **request**
* data:
`email`
* **response**
* status: `HTTP_200_OK` (success)
### Reset password confirmation
Use this endpoint to finish reset password process. This endpoint is not a URL
which will be directly exposed to your users - you should provide site in your
frontend application (configured by `PASSWORD_RESET_CONFIRM_URL`) which
will send `POST` request to reset password confirmation endpoint.
#### `POST`
URL: `/password/reset/confirm/`
* **request**
* data:
`uid`
`token`
`new_password`
`re_new_password` (if `PASSWORD_RESET_CONFIRM_RETYPE` is `True`)
* **response**
* status: `HTTP_200_OK` (success)
## Settings
### DOMAIN
Domain of your frontend app. If not provided, domain of current site will be
used.
**Required**: `False`
### SITE_NAME
Name of your frontend app. If not provided, name of current site will be
used.
**Required**: `False`
### PASSWORD_RESET_CONFIRM_URL
URL to your frontend password reset page. It should contain `{uid}` and
`{token}` placeholders, e.g. `#/password-reset/{uid}/{token}`. You should pass
`uid` and `token` to reset password confirmation endpoint.
**Required**: `True`
### SEND_ACTIVATION_EMAIL
If `True`, register endpoint will send activation email to user.
**Default**: `False`
### ACTIVATION_URL
URL to your frontend activation page. It should contain `{uid}` and `{token}`
placeholders, e.g. `#/activate/{uid}/{token}`. You should pass `uid` and
`token` to activation endpoint.
**Required**: `True`
### SET_USERNAME_RETYPE
If `True`, you need to pass `re_new_{{ User.USERNAME_FIELD }}` to
`/{{ User.USERNAME_FIELD }}/` endpoint, to validate username equality.
**Default**: `False`
### SET_PASSWORD_RETYPE
If `True`, you need to pass `re_new_password` to `/password/` endpoint, to
validate password equality.
**Default**: `False`
### PASSWORD_RESET_CONFIRM_RETYPE
If `True`, you need to pass `re_new_password` to `/password/reset/confirm/`
endpoint in order to validate password equality.
**Default**: `False`
## Emails
There are few email templates which you may want to override:
* `activation_email_body.txt`
* `activation_email_subject.txt`
* `password_reset_email_body.txt`
* `password_reset_email_subject.txt`
All of them have following context:
* `user`
* `domain`
* `site_name`
* `url`
* `uid`
* `token`
* `protocol`
## Sample usage
We provide a standalone test app for you to start easily, see how everything works with basic settings. It might be useful before integrating **djoser** into your backend application.
In this extremely short tutorial we are going to mimic the simplest flow: register user, log in and log out. We will also check resource access on each consecutive step. Let's go!
* Clone repository and install **djoser** to your virtualenv:
`$ git clone git@github.com:sunscrapers/djoser.git`
`$ cd djoser`
`$ pip install -e .`
* Go to the `testproject` directory, migrate the database and start the development server:
`$ cd testproject`
`$ ./manage.py migrate`
`$ ./manage.py runserver 8088`
* Register a new user:
`$ curl -X POST http://127.0.0.1:8088/auth/register/ --data 'username=djoser&password=djoser'`
`{"email": "", "username": "djoser"}`
So far, so good. We have just created a new user using REST API.
* Let's access user's details:
`$ curl -X GET http://127.0.0.1:8088/auth/me/`
`{"detail": "Authentication credentials were not provided."}`
As we can see, we cannot access user profile without logging in. Pretty obvious.
* Let's log in:
`curl -X POST http://127.0.0.1:8088/auth/login/ --data 'username=djoser&password=djoser'`
`{"auth_token": "b704c9fc3655635646356ac2950269f352ea1139"}`
We have just obtained an authorization token that we may use later in order to retrieve specific resources.
* Let's access user's details again:
`$ curl -X GET http://127.0.0.1:8088/auth/me/`
`{"detail": "Authentication credentials were not provided."}`
Access is still forbidden but let's offer the token we obtained:
`$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`
`{"email": "", "username": "djoser"}`
Yay, it works!
* Now let's log out:
`curl -X POST http://127.0.0.1:8088/auth/logout/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`
And try access user profile again:
`$ curl -X GET http://127.0.0.1:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`
`{"detail": "Invalid token"}`
As we can see, user has been logged out successfully and the proper token has been removed.
## Customization
If you need to override some `djoser` behaviour, you could define your custom view/serializer.
Define custom urls instead of reusing `djoser.urls`:
```python
urlpatterns = patterns('',
(...),
url(r'^register/$', views.CustomRegistrationView.as_view()),
)
```
Define custom view/serializer (inherit from one of `djoser` class) and override necessary method/field:
```python
class CustomRegistrationView(djoser.views.RegistrationView):
def send_email(self, *args, **kwargs):
your_custom_email_sender(*args, **kwargs)
```
You could check `djoser` API in source code:
* [djoser.views](https://github.com/sunscrapers/djoser/blob/master/djoser/views.py)
* [djoser.serializers](https://github.com/sunscrapers/djoser/blob/master/djoser/serializers.py)
## Development
To start developing on **djoser**, clone the repository:
`$ git clone git@github.com:sunscrapers/djoser.git`
In order to run the tests create virtualenv, go to repo directory and then:
`$ pip install -r requirements-test.txt`
`$ cd testproject`
`$ ./manage.py migrate`
`$ ./manage.py test`
## Similar projects
List of projects related to Django, REST and authentication:
- [django-rest-auth](https://github.com/Tivix/django-rest-auth)
- [django-rest-framework-digestauth](https://github.com/juanriaza/django-rest-framework-digestauth)
- [django-oauth-toolkit](https://github.com/evonove/django-oauth-toolkit)
- [doac](https://github.com/Rediker-Software/doac)
- [django-rest-framework-jwt](https://github.com/GetBlimp/django-rest-framework-jwt)
- [django-rest-framework-httpsignature](https://github.com/etoccalino/django-rest-framework-httpsignature)
- [hawkrest](https://github.com/kumar303/hawkrest)
djoser-0.4.0/requirements.txt 0000644 0000765 0000024 00000000047 12602516157 016710 0 ustar haxoza staff 0000000 0000000 Django>=1.5
djangorestframework>=3.0.0
djoser-0.4.0/setup.cfg 0000644 0000765 0000024 00000000073 12602523477 015247 0 ustar haxoza staff 0000000 0000000 [egg_info]
tag_svn_revision = 0
tag_date = 0
tag_build =
djoser-0.4.0/setup.py 0000644 0000765 0000024 00000002321 12602517515 015132 0 ustar haxoza staff 0000000 0000000 #!/usr/bin/env python
import os
from setuptools import setup
try:
import pypandoc
description = pypandoc.convert('README.md', 'rst')
except (IOError, ImportError):
description = open('README.md').read()
def get_packages(package):
return [dirpath
for dirpath, dirnames, filenames in os.walk(package)
if os.path.exists(os.path.join(dirpath, '__init__.py'))]
def get_requirements(file_name):
return [i.strip() for i in open(file_name).readlines()]
setup(
name='djoser',
version='0.4.0',
packages=get_packages('djoser'),
license='MIT',
author='SUNSCRAPERS',
description='REST version of Django authentication system.',
author_email='info@sunscrapers.com',
long_description=description,
install_requires=get_requirements('requirements.txt'),
include_package_data=True,
url='https://github.com/sunscrapers/djoser',
classifiers=[
'Development Status :: 3 - Alpha',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
]
)