PKbIMQ)hrest_auth_toolkit/__init__.py"""Simple + flexible signup and login for Django APIs""" __version__ = '0.9' default_app_config = 'rest_auth_toolkit.app.RestAuthToolkitConfig' PKLL+w\\rest_auth_toolkit/admin.pyfrom django.contrib.auth.admin import UserAdmin from django.utils.translation import gettext_lazy as _ class BaseEmailUserAdmin(UserAdmin): list_display = ('email', 'first_name', 'last_name', 'is_active', 'is_staff') fieldsets = ( (None, {'fields': ('email', 'password')}), (_('Personal info'), {'fields': ('first_name', 'last_name')}), (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}), ) # add_form_template = None add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'password1', 'password2'), }), ) search_fields = ('first_name', 'last_name', 'email') ordering = ('email',) PKL uurest_auth_toolkit/app.pyfrom django.apps import AppConfig class RestAuthToolkitConfig(AppConfig): """Default app config for RATK. This installs a signal handler to set user.is_active when email_confirmed is emitted. """ name = 'rest_auth_toolkit' def ready(self): from .models import email_confirmed from .views import activate_user email_confirmed.connect(activate_user) class RestAuthToolkitMinimalConfig(AppConfig): """App config without signal handler setup. Use this when you don't want to set user.is_active when email_confirmed is emitted. """ name = 'rest_auth_toolkit' PKL>0rest_auth_toolkit/managers.pyfrom django.contrib.auth.models import BaseUserManager from django.db import models class BaseEmailUserManager(BaseUserManager): use_in_migrations = True def get_by_natural_key(self, email): return self.get(email=email) def _create_user(self, email, password, **extra_fields): email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(email, password, **extra_fields) def create_superuser(self, email, password, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if not extra_fields['is_staff']: raise ValueError('Superuser must have is_staff=True.') if not extra_fields['is_superuser']: raise ValueError('Superuser must have is_superuser=True.') return self._create_user(email, password, **extra_fields) class BaseAPITokenManager(models.Manager): def create_token(self, user): return self.create(user=user) PKLpؤ  rest_auth_toolkit/models.pyimport os import binascii from datetime import datetime from django.conf import settings from django.contrib.auth.models import AbstractUser from django.db import models from django.dispatch import Signal from django.utils.timezone import utc from django.utils.translation import gettext_lazy as _ from .managers import BaseEmailUserManager, BaseAPITokenManager from .utils import get_setting email_confirmed = Signal(providing_args=['user']) class BaseEmailUser(AbstractUser): """Custom user with email as login field. The autogenerated "id" field is used for admin URLs, foreign keys, etc. There is no "username" field, but subclasses can define one if desired. """ USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] email = models.EmailField(_('email address'), max_length=255, unique=True) username = None objects = BaseEmailUserManager() class Meta: abstract = True verbose_name = _('user') verbose_name_plural = _('users') def __str__(self): return self.email def get_short_name(self): return self.email def natural_key(self): return self.email class BaseEmailConfirmation(models.Model): """Abstract model for email confirmations. Subclass in your project to customize to your needs and make migrations easy. """ user = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_('user')) confirmed = models.DateTimeField(_('confirmed'), null=True, blank=True) class Meta: abstract = True class IsExpired(Exception): """Tried to confirm email after the confirmation expired.""" def __str__(self): return 'Confirmation for {}'.format(self.user) def confirm(self): """Mark this record as confirmed. The time period to confirm an email is configured with the setting email_confirmation_validity_period, which defaults to 60 minutes. """ now = datetime.now(utc) delay = (now - self.created).total_seconds() validity = get_setting('email_confirmation_validity_period', 60) * 60 if delay > validity: raise self.IsExpired self.confirmed = now self.save() email_confirmed.send(sender=self.__class__, user=self.user) class BaseAPIToken(models.Model): """Abstract model for API auth tokens. You can override generate_key in your concrete class to change the way the keys are created, or redefine the key field. Adapted from rest_framework.authtoken. """ key = models.CharField(_('key'), max_length=40, primary_key=True) user = models.ForeignKey( settings.AUTH_USER_MODEL, related_name='api_tokens', on_delete=models.CASCADE, verbose_name=_('user')) objects = BaseAPITokenManager() class Meta: abstract = True def __str__(self): return self.key def save(self, *args, **kwargs): if not self.key: self.key = self.generate_key() return super(BaseAPIToken, self).save(*args, **kwargs) def generate_key(self): return binascii.hexlify(os.urandom(20)).decode() PKdJMe rest_auth_toolkit/serializers.pyfrom django.conf import settings from django.contrib.auth import authenticate, get_user_model from django.utils.translation import gettext as _ from rest_framework import serializers from rest_framework.exceptions import ValidationError try: import facepy except ImportError: facepy = None User = get_user_model() class SignupDeserializer(serializers.ModelSerializer): """Deserializer to create users without username.""" class Meta: model = User fields = ('email', 'password') extra_kwargs = { 'password': {'style': {'input_type': 'password'}}, } def create(self, validated_data): return User.objects.create_user( email=validated_data['email'], password=validated_data['password'], is_active=False, ) class LoginDeserializer(serializers.Serializer): """Deserializer to find a user from credentials.""" email = serializers.EmailField() password = serializers.CharField(style={'input_type': 'password'}) def validate(self, data): user = authenticate(email=data['email'], password=data['password']) if user is None: msg = _('Invalid email or password') raise ValidationError({'errors': [msg]}) return {'user': user} class FacebookLoginDeserializer(serializers.Serializer): """Deserializer to create users from client-side Facebook auth response.""" signed_request = serializers.CharField() def validate(self, data): req = facepy.SignedRequest(data['signed_request'], settings.FACEBOOK_APP_SECRET_KEY, settings.FACEBOOK_APP_ID) # XXX handle facebook exceptions # for example, the user can refuse to share their email address graph = facepy.GraphAPI(req.user.oauth_token.token) data = graph.get('me?fields=email,first_name,last_name,third_party_id') extended_token = facepy.get_extended_access_token( req.user.oauth_token.token, settings.FACEBOOK_APP_ID, settings.FACEBOOK_APP_SECRET_KEY) user = User.objects.get_or_create_facebook_user(data, extended_token)[0] return {'user': user} PKLirest_auth_toolkit/utils.pyfrom django.conf import settings from django.utils.module_loading import import_string # TODO follow drf's pattern of having all default setting values in one place class MissingSetting(Exception): """Exception raised by get_setting and get_object_from_setting.""" def __init__(self, name): self.name = name def __str__(self): return 'setting REST_AUTH_TOOLKIT[{!r}] not found'.format(self.name) _NotGiven = object() def get_setting(name, default=_NotGiven): """Find setting in REST_AUTH_TOOLKIT dict. Return default is not found, or raise MissingSetting if no default. """ config = getattr(settings, 'REST_AUTH_TOOLKIT', {}) if name in config: return config[name] elif default is not _NotGiven: return default else: raise MissingSetting(name) def get_object_from_setting(name, default=_NotGiven): """Find and import a dotted path settting. default should be the real object, not a dotted path. MissingSetting is raised if setting is not found and there is no default. """ value = get_setting(name, default) if value is default: return default else: return import_string(value) PK[5M0nnrest_auth_toolkit/views.pyfrom django.contrib.auth import get_user_model from django.core.mail import send_mail from django.template.loader import render_to_string from django.urls import reverse from django.utils.translation import gettext as _ from rest_framework import generics, status, views from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from .serializers import FacebookLoginDeserializer, LoginDeserializer, SignupDeserializer from .utils import get_object_from_setting, get_setting, MissingSetting try: import facepy except ImportError: facepy = None User = get_user_model() Token = get_object_from_setting('api_token_class') class SignupView(generics.GenericAPIView): """Create a user and send a confirmation email. Response: 201 Created. """ authentication_classes = () permission_classes = () serializer_class = get_object_from_setting('signup_serializer_class', SignupDeserializer) email_confirmation_class = get_object_from_setting('email_confirmation_class', None) def post(self, request): """Handle the request. If the setting email_confirmation_send_email is true (default), the function send_email will be called. That function requires that your app define a route named app-auth:email-confirmation with an id parameter; the view for this route should get an email confirmation instance using the ID and call the confirm method. To use a field that's not named 'id', define the setting email_confirmation_lookup_param (this will change the URL pattern). """ deserializer = self.get_serializer(data=request.data) deserializer.is_valid(raise_exception=True) user = deserializer.save() if self.email_confirmation_class is None: raise MissingSetting('email_confirmation_string') confirmation = self.email_confirmation_class.objects.create(user=user) if get_setting('email_confirmation_send_email', True): email_field = user.get_email_field_name() send_email(request, user, getattr(user, email_field), confirmation) return Response(status=status.HTTP_201_CREATED) class LoginView(generics.GenericAPIView): """Authenticate a user, return an API auth token if valid. Response: ```json {"token": "string"} ``` """ authentication_classes = () permission_classes = () serializer_class = get_object_from_setting('login_serializer_class', LoginDeserializer) def post(self, request): deserializer = self.get_serializer(data=request.data) deserializer.is_valid(raise_exception=True) data = deserializer.validated_data token = Token.objects.create_token(**data) return Response({'token': token.key}) class FacebookLoginView(generics.GenericAPIView): """Create a user from a Facebook signed request (from auth response). The user will be active immediately, with an unusable password. Response: ```json {"token": "string"} ``` """ authentication_classes = () permission_classes = () serializer_class = get_object_from_setting('facebook_login_serializer_class', FacebookLoginDeserializer) @classmethod def as_view(cls, *args, **kwargs): if facepy is None: raise TypeError('install rest-framework-auth-toolkit[facebook] ' 'to enable Facebook logins') # TODO error if settings are missing return super(FacebookLoginView, cls).as_view(*args, **kwargs) def post(self, request): deserializer = self.get_serializer(data=request.data) deserializer.is_valid(raise_exception=True) data = deserializer.validated_data token = Token.objects.create_token(**data) return Response({'token': token.key}) class LogoutView(views.APIView): """Revoke current API auth token. Response: 200 OK. """ permission_classes = (IsAuthenticated,) def post(self, request): token = request.auth if isinstance(token, Token): token.revoke() return Response(status=status.HTTP_200_OK) def send_email(request, user, address, confirmation): """Send the confirmation email for a new user.""" subject = _('Confirm your email address') from_address = get_setting('email_confirmation_from') lookup_field = get_setting('email_confirmation_lookup_field', 'id') confirmation_url = request.build_absolute_uri( reverse('app-auth:email-confirmation', kwargs={lookup_field: getattr(confirmation, lookup_field)})) # The url template tag doesn't include scheme/domain/port, pass a helper base_url = request.build_absolute_uri('/')[:-1] context = {'base_url': base_url, 'confirmation_url': confirmation_url} txt_content = render_to_string('rest_auth_toolkit/email_confirmation.txt', context) html_content = render_to_string('rest_auth_toolkit/email_confirmation.html', context) send_mail(subject=subject, from_email=from_address, recipient_list=[address], message=txt_content, html_message=html_content, fail_silently=False) def activate_user(sender, **kwargs): """Mark user as active when a confirmation link is visited. This handler is connected to the email_confirmed signal in RestAuthToolkitConfig.ready. """ kwargs['user'].is_active = True kwargs['user'].save(update_fields=['is_active']) PKoL 1rest_auth_toolkit/locale/fr/LC_MESSAGES/django.mo | !(<eu    A%88q.      Confirm your email addressFollow this link to validate your email:Important datesInvalid email or passwordPermissionsPersonal infoconfirmedemail addresskeyuserusersProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Confirmez votre adresse électroniqueSuivez ce lien pour valider votre adresse électronique:Dates importantesAdresse électronique ou mot de passe invalidePermissionsInformations personnellesconfirméadresse électroniquecléutilisateurutilisateursPKoLEif1rest_auth_toolkit/locale/fr/LC_MESSAGES/django.po# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-06-06 16:41-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: admin.py:9 msgid "Personal info" msgstr "Informations personnelles" #: admin.py:10 msgid "Permissions" msgstr "Permissions" #: admin.py:12 msgid "Important dates" msgstr "Dates importantes" #: models.py:29 msgid "email address" msgstr "adresse électronique" #: models.py:36 models.py:56 models.py:98 msgid "user" msgstr "utilisateur" #: models.py:37 msgid "users" msgstr "utilisateurs" #: models.py:57 msgid "confirmed" msgstr "confirmé" #: models.py:95 msgid "key" msgstr "clé" #: serializers.py:42 msgid "Invalid email or password" msgstr "Adresse électronique ou mot de passe invalide" #: templates/rest_auth_toolkit/email_confirmation.html:11 #: templates/rest_auth_toolkit/email_confirmation.txt:4 msgid "Follow this link to validate your email:" msgstr "Suivez ce lien pour valider votre adresse électronique:" #: views.py:122 msgid "Confirm your email address" msgstr "Confirmez votre adresse électronique" PKLT*{^^Erest_auth_toolkit/templates/rest_auth_toolkit/email_confirmation.html{% load i18n %}

{% trans "Follow this link to validate your email:" %}
{{ confirmation_url }}

PKL^ىDrest_auth_toolkit/templates/rest_auth_toolkit/email_confirmation.txt{% autoescape off %} {% load i18n %} {% trans "Follow this link to validate your email:" %} {{ confirmation_url }} {% endautoescape %} PKLqjqDD1Rest_Framework_Auth_Toolkit-0.9.dist-info/LICENSEMIT License Copyright (c) 2017 Caravan Web Worker Cooperative, Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!Hd BUc/Rest_Framework_Auth_Toolkit-0.9.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,rzd&Y)r$[)T&UD"PK!H/ w2Rest_Framework_Auth_Toolkit-0.9.dist-info/METADATAVr5SU&`O$h:kyޕJ =W< #:vNhOy^Vl2:X\sAW-M*Ɲ_+NYtM#mS׸Q3:Н+Biza`|}ķ[zo]e ,-MkKclR8|'Np^#_~wy`MY4gd'&HU;Mo>e]*)h'[KF=n|F*sY-SsdXې,LNXϝJΕ9Tiw.K@J8='Lz"(ɵH%3sH/ /t:_E?=2E(mĎAV<t&}P g<-#D0Uݰ+vfQ'D^2CD{"G!lCg ݇@Q8uҳ[+/̈KV39}cQ]{N;G4A;y'>upO`׏^rp)x6FÌVdr@"FdK [^2R!^2%3>bTqKv֚2;3T\ѮV.{[q~b[N"^JX P^7uf  j$Į6b;\wSJ1~ !;ըZRjt#-@e9]{pF*]]s C nj'EiKn A<:F8⠰QKKCUJ*ׂtΛFD!T*14my숙A/I *bIfoя+JjaOx)$7Hot5r1h 'tPK!H*0Rest_Framework_Auth_Toolkit-0.9.dist-info/RECORDɎHE-8 L^`&c]܄ŒwZYRY.O:義HJ@YDxfj(!6ߡ;:\+8K_PcnK?ҺٙlooP֨B*t̜q(Fvc9])Hj4MeI(_ OߴTTue_q69[eFWY?|j˲b<"@/W|`& *I 4GZxEm eFzZ{%zd c6] W-}s6*bB ըRi35?)Znz=b#Ųx$藬4PNRau{*Ek't2>P6}E>/(=mxȀ;Ycu9R^ƣ8a˾@68[VQ ЂuZhP8ݺc=0f(]7TDpObGi` 3G5HӁJat)·HM{bLÐdZVCA69kHPۼƟj%9@{yFp^:kFmANWJK.%#y˒^ +*lGw~4}<{XgS߀cZo[BMޮmSN)AQs`[}'R`ĉ?eJٟq8AIՙnkܬ? X~ <ʳ.q"rBYC 1]0 rest_auth_toolkit/managers.pyPKLpؤ  b rest_auth_toolkit/models.pyPKdJMe rest_auth_toolkit/serializers.pyPKLi%"rest_auth_toolkit/utils.pyPK[5M0nn 'rest_auth_toolkit/views.pyPKoL 1=rest_auth_toolkit/locale/fr/LC_MESSAGES/django.moPKoLEif1+Brest_auth_toolkit/locale/fr/LC_MESSAGES/django.poPKLT*{^^EHrest_auth_toolkit/templates/rest_auth_toolkit/email_confirmation.htmlPKL^ىDSJrest_auth_toolkit/templates/rest_auth_toolkit/email_confirmation.txtPKLqjqDD1>KRest_Framework_Auth_Toolkit-0.9.dist-info/LICENSEPK!Hd BUc/ORest_Framework_Auth_Toolkit-0.9.dist-info/WHEELPK!H/ w2sPRest_Framework_Auth_Toolkit-0.9.dist-info/METADATAPK!H*0TRest_Framework_Auth_Toolkit-0.9.dist-info/RECORDPKhX