From ecffe6efada3d7dab4a69bba7a8d6f3b4cbf995a Mon Sep 17 00:00:00 2001 From: emil farisan <emil.farisan@gmail.com> Date: Tue, 26 Nov 2019 22:14:57 +0700 Subject: [PATCH] spiked in custom passwordless auth backend --- accounts/authentication.py | 23 ++++++++++++++ accounts/models.py | 34 +++++++++++++++++++++ accounts/templates/login_email_sent.html | 7 +++++ accounts/views.py | 38 ++++++++++++++++++++++++ superlists/settings.py | 14 ++++++++- superlists/urls.py | 23 ++++---------- 6 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 accounts/authentication.py create mode 100644 accounts/models.py create mode 100644 accounts/templates/login_email_sent.html create mode 100644 accounts/views.py diff --git a/accounts/authentication.py b/accounts/authentication.py new file mode 100644 index 0000000..a274bb2 --- /dev/null +++ b/accounts/authentication.py @@ -0,0 +1,23 @@ +import sys +from accounts.models import ListUser, Token + +class PasswordlessAuthenticationBackend(object): + + def authenticate(self, uid): + print('uid', uid, file=sys.stderr) + if not Token.objects.filter(uid=uid).exists(): + print('no token found', file=sys.stderr) + return None + token = Token.objects.get(uid=uid) + print('got token', file=sys.stderr) + try: + user = ListUser.objects.get(email=token.email) + print('got user', file=sys.stderr) + return user + except ListUser.DoesNotExist: + print('new user', file=sys.stderr) + return ListUser.objects.create(email=token.email) + + + def get_user(self, email): + return ListUser.objects.get(email=email) \ No newline at end of file diff --git a/accounts/models.py b/accounts/models.py new file mode 100644 index 0000000..5ce27f7 --- /dev/null +++ b/accounts/models.py @@ -0,0 +1,34 @@ +from django.db import models +from django.contrib.auth.models import ( + AbstractBaseUser, BaseUserManager, PermissionsMixin +) + + +class Token(models.Model): + email = models.EmailField() + uid = models.CharField(max_length=255) + + +class ListUser(AbstractBaseUser, PermissionsMixin): + email = models.EmailField(primary_key=True) + USERNAME_FIELD = 'email' + #REQUIRED_FIELDS = ['email', 'height'] + + objects = ListUserManager() + + @property + def is_staff(self): + return self.email == 'harry.percival@example.com' + + @property + def is_active(self): + return True + + +class ListUserManager(BaseUserManager): + + def create_user(self, email): + ListUser.objects.create(email=email) + + def create_superuser(self, email, password): + self.create_user(email) diff --git a/accounts/templates/login_email_sent.html b/accounts/templates/login_email_sent.html new file mode 100644 index 0000000..d02890e --- /dev/null +++ b/accounts/templates/login_email_sent.html @@ -0,0 +1,7 @@ +<html> +<h1>Email sent</h1> + +<p>Check your email, you'll find a message with a link that will log you into +the site.</p> + +</html> diff --git a/accounts/views.py b/accounts/views.py new file mode 100644 index 0000000..a86d5a8 --- /dev/null +++ b/accounts/views.py @@ -0,0 +1,38 @@ +import uuid +import sys +from django.contrib.auth import authenticate +from django.contrib.auth import login as auth_login +from django.core.mail import send_mail +from django.shortcuts import redirect, render +from django.contrib.auth import login as auth_login, logout as auth_logout + +from accounts.models import Token + + +def login(request): + print('login view', file=sys.stderr) + uid = request.GET.get('uid') + user = authenticate(uid=uid) + if user is not None: + auth_login(request, user) + return redirect('/') + + +def logout(request): + auth_logout(request) + return redirect('/') + + +def send_login_email(request): + email = request.POST['email'] + uid = str(uuid.uuid4()) + Token.objects.create(email=email, uid=uid) + print('saving uid', uid, 'for email', email, file=sys.stderr) + url = request.build_absolute_uri(f'/accounts/login?uid={uid}') + send_mail( + 'Your login link for Superlists', + f'Use this link to log in:\n\n{url}', + 'noreply@superlists', + [email], + ) + return render(request, 'login_email_sent.html') \ No newline at end of file diff --git a/superlists/settings.py b/superlists/settings.py index a443f8d..aab948f 100644 --- a/superlists/settings.py +++ b/superlists/settings.py @@ -31,13 +31,19 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', + #'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'lists', + 'accounts', +] + +AUTH_USER_MODEL = 'accounts.ListUser' +AUTHENTICATION_BACKENDS = [ + 'accounts.authentication.PasswordlessAuthenticationBackend', ] MIDDLEWARE = [ @@ -119,3 +125,9 @@ USE_TZ = True # https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = '/static/' + +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_HOST_USER = 'emil.farisan@gmail.com' +EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_PASSWORD') +EMAIL_PORT = 587 +EMAIL_USE_TLS = True diff --git a/superlists/urls.py b/superlists/urls.py index b7683ae..10f0e24 100644 --- a/superlists/urls.py +++ b/superlists/urls.py @@ -1,21 +1,8 @@ -"""superlists URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/2.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" from django.conf.urls import url -from lists import views +from accounts import views urlpatterns = [ - url(r'^$', views.home_page, name='home'), -] + url(r'^send_email$', views.send_login_email, name='send_login_email'), + url(r'^login$', views.login, name='login'), + url(r'^logout$', views.logout, name='logout'), +] \ No newline at end of file -- GitLab