djoser-0.4.0/0000755000076500000240000000000012602523477013426 5ustar haxozastaff00000000000000djoser-0.4.0/djoser/0000755000076500000240000000000012602523477014714 5ustar haxozastaff00000000000000djoser-0.4.0/djoser/__init__.py0000644000076500000240000000000012475550727017021 0ustar haxozastaff00000000000000djoser-0.4.0/djoser/constants.py0000644000076500000240000000065512545220315017277 0ustar haxozastaff00000000000000from 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.py0000644000076500000240000001303412602516157017620 0ustar haxozastaff00000000000000from 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.py0000644000076500000240000000102012602516157017114 0ustar haxozastaff00000000000000from 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.py0000644000076500000240000000034312545220315016715 0ustar haxozastaff00000000000000from 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/0000755000076500000240000000000012602523477016712 5ustar haxozastaff00000000000000djoser-0.4.0/djoser/templates/activation_email_body.txt0000644000076500000240000000064412475550727024012 0ustar haxozastaff00000000000000{% 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.txt0000644000076500000240000000020012475550727024500 0ustar haxozastaff00000000000000{% load i18n %}{% autoescape off %} {% blocktrans %}Account activation on {{ site_name }}{% endblocktrans %} {% endautoescape %}djoser-0.4.0/djoser/templates/password_reset_email_body.txt0000644000076500000240000000102712475550727024711 0ustar haxozastaff00000000000000{% 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.txt0000644000076500000240000000017412475550727025415 0ustar haxozastaff00000000000000{% load i18n %}{% autoescape off %} {% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %} {% endautoescape %}djoser-0.4.0/djoser/urls/0000755000076500000240000000000012602523477015701 5ustar haxozastaff00000000000000djoser-0.4.0/djoser/urls/__init__.py0000644000076500000240000000003612602516157020006 0ustar haxozastaff00000000000000from .base import urlpatterns djoser-0.4.0/djoser/urls/authtoken.py0000644000076500000240000000057612602516157020262 0ustar haxozastaff00000000000000from 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.py0000644000076500000240000000153712602516157017170 0ustar haxozastaff00000000000000from 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.py0000644000076500000240000000765712602516157016442 0ustar haxozastaff00000000000000from 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.py0000644000076500000240000001641112602516157016423 0ustar haxozastaff00000000000000from 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/0000755000076500000240000000000012602523477016406 5ustar haxozastaff00000000000000djoser-0.4.0/djoser.egg-info/dependency_links.txt0000644000076500000240000000000112602523477022454 0ustar haxozastaff00000000000000 djoser-0.4.0/djoser.egg-info/PKG-INFO0000644000076500000240000004764712602523477017525 0ustar haxozastaff00000000000000Metadata-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.txt0000644000076500000240000000004712602523477021007 0ustar haxozastaff00000000000000Django>=1.5 djangorestframework>=3.0.0 djoser-0.4.0/djoser.egg-info/SOURCES.txt0000644000076500000240000000110512602523477020267 0ustar haxozastaff00000000000000MANIFEST.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.pydjoser-0.4.0/djoser.egg-info/top_level.txt0000644000076500000240000000002312602523477021133 0ustar haxozastaff00000000000000djoser djoser/urls djoser-0.4.0/MANIFEST.in0000644000076500000240000000010612475550727015167 0ustar haxozastaff00000000000000include README.md include djoser/templates/* include requirements.txt djoser-0.4.0/PKG-INFO0000644000076500000240000004764712602523477014545 0ustar haxozastaff00000000000000Metadata-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.md0000644000076500000240000003206112602516322014676 0ustar haxozastaff00000000000000# djoser [![Build Status](https://travis-ci.org/sunscrapers/djoser.svg?branch=master)](https://travis-ci.org/sunscrapers/djoser) [![Coverage Status](https://coveralls.io/repos/sunscrapers/djoser/badge.png?branch=master)](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.txt0000644000076500000240000000004712602516157016710 0ustar haxozastaff00000000000000Django>=1.5 djangorestframework>=3.0.0 djoser-0.4.0/setup.cfg0000644000076500000240000000007312602523477015247 0ustar haxozastaff00000000000000[egg_info] tag_svn_revision = 0 tag_date = 0 tag_build = djoser-0.4.0/setup.py0000644000076500000240000000232112602517515015132 0ustar haxozastaff00000000000000#!/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', ] )