From 619e27eb525920be5d2017fddd226d5fb02d8ecb Mon Sep 17 00:00:00 2001 From: Skia Date: Wed, 18 Nov 2015 17:09:06 +0100 Subject: [PATCH] Basic auth system --- core/forms.py | 40 ++++++++++++- core/migrations/0001_initial.py | 34 +++++------ core/models.py | 97 +++++++++++++++++++++++++++++-- core/templates/core/base.html | 30 ++++++++++ core/templates/core/index.html | 1 + core/templates/core/login.html | 14 +++++ core/templates/core/register.html | 13 +++++ core/templates/sith/register.html | 13 ----- core/urls.py | 4 +- core/views.py | 41 ++++++++----- sith/urls.py | 2 +- 11 files changed, 232 insertions(+), 57 deletions(-) create mode 100644 core/templates/core/base.html create mode 100644 core/templates/core/index.html create mode 100644 core/templates/core/login.html create mode 100644 core/templates/core/register.html delete mode 100644 core/templates/sith/register.html diff --git a/core/forms.py b/core/forms.py index ae0e537d..1ac601e2 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,4 +1,8 @@ -from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm +from django import forms +from django.contrib.auth import logout, login, authenticate +import logging + from .models import User class RegisteringForm(UserCreationForm): @@ -6,4 +10,36 @@ class RegisteringForm(UserCreationForm): required_css_class = 'required' class Meta: model = User - fields = ('username', 'email',) + fields = ('first_name', 'last_name', 'email') + + def save(self, commit=True): + user = super(RegisteringForm, self).save(commit=False) + user.set_password(self.cleaned_data["password1"]) + user.generate_username() + if commit: + user.save() + return user + +class LoginForm(AuthenticationForm): + def login(self): + u = authenticate(username=self.request.POST['username'], + password=self.request.POST['password']) + if u is not None: + if u.is_active: + login(self.request, u) + logging.debug("Logging in "+u) + else: + raise forms.ValidationError( + self.error_messages['invalid_login'], + code='inactive', + params={'username': self.username_field.verbose_name}, + ) + else: + logging.debug("Login failed") + raise forms.ValidationError( + self.error_messages['invalid_login'], + code='invalid_login', + params={'username': self.username_field.verbose_name}, + ) + + diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py index b46ffff2..f024ebcb 100644 --- a/core/migrations/0001_initial.py +++ b/core/migrations/0001_initial.py @@ -2,9 +2,9 @@ from __future__ import unicode_literals from django.db import migrations, models +import django.utils.timezone import django.core.validators import django.contrib.auth.models -import django.utils.timezone class Migration(migrations.Migration): @@ -17,25 +17,25 @@ class Migration(migrations.Migration): migrations.CreateModel( name='User', fields=[ - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)), - ('is_superuser', models.BooleanField(verbose_name='superuser status', default=False, help_text='Designates that this user has all permissions without explicitly assigning them.')), - ('username', models.CharField(unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], verbose_name='username', max_length=30, error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.')), - ('first_name', models.CharField(max_length=30, blank=True, verbose_name='first name')), - ('last_name', models.CharField(max_length=30, blank=True, verbose_name='last name')), - ('email', models.EmailField(max_length=254, blank=True, verbose_name='email address')), - ('is_staff', models.BooleanField(verbose_name='staff status', default=False, help_text='Designates whether the user can log into this admin site.')), - ('is_active', models.BooleanField(verbose_name='active', default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.')), - ('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)), - ('nick_name', models.CharField(max_length=30)), - ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', verbose_name='groups', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.')), - ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', verbose_name='user permissions', to='auth.Permission', blank=True, help_text='Specific permissions for this user.')), + ('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)), + ('password', models.CharField(verbose_name='password', max_length=128)), + ('last_login', models.DateTimeField(null=True, blank=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], help_text='Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.', error_messages={'unique': 'A user with that username already exists.'}, verbose_name='username', unique=True, max_length=254)), + ('first_name', models.CharField(verbose_name='first name', max_length=30)), + ('last_name', models.CharField(verbose_name='last name', max_length=30)), + ('email', models.EmailField(verbose_name='email address', unique=True, max_length=254)), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('date_of_birth', models.DateTimeField(verbose_name='date of birth')), + ('nick_name', models.CharField(blank=True, max_length=30)), + ('groups', models.ManyToManyField(related_query_name='user', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', blank=True, to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(related_query_name='user', help_text='Specific permissions for this user.', related_name='user_set', blank=True, to='auth.Permission', verbose_name='user permissions')), ], options={ - 'verbose_name': 'user', 'verbose_name_plural': 'users', - 'abstract': False, + 'verbose_name': 'user', }, managers=[ ('objects', django.contrib.auth.models.UserManager()), diff --git a/core/models.py b/core/models.py index 3b0de951..13f8005a 100644 --- a/core/models.py +++ b/core/models.py @@ -1,15 +1,100 @@ from django.db import models -from django.contrib.auth.models import AbstractUser +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager +from django.utils.translation import ugettext_lazy as _ +from django.core import validators +from django.utils import timezone -import logging +class User(AbstractBaseUser, PermissionsMixin): + """ + Defines the base user class, useable in every app -logging.basicConfig(level=logging.DEBUG) + This is almost the same as the auth module AbstractUser since it inherits from it, + but some fields are required, and the username is generated automatically with the + name of the user (see generate_username()). -class User(AbstractUser): - nick_name = models.CharField(max_length=30) + Added field: nick_name + Required fields: email, first_name, last_name, date_of_birth + """ + username = models.CharField( + _('username'), + max_length=254, + unique=True, + help_text=_('Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.'), + validators=[ + validators.RegexValidator( + r'^[\w.@+-]+$', + _('Enter a valid username. This value may contain only ' + 'letters, numbers ' 'and @/./+/-/_ characters.') + ), + ], + error_messages={ + 'unique': _("A user with that username already exists."), + }, + ) + first_name = models.CharField(_('first name'), max_length=30) + last_name = models.CharField(_('last name'), max_length=30) + email = models.EmailField(_('email address'), unique=True) + date_of_birth = models.DateTimeField(_('date of birth'), default="1970-01-01") + nick_name = models.CharField(max_length=30, blank=True) + is_staff = models.BooleanField( + _('staff status'), + default=False, + help_text=_('Designates whether the user can log into this admin site.'), + ) + is_active = models.BooleanField( + _('active'), + default=True, + help_text=_( + 'Designates whether this user should be treated as active. ' + 'Unselect this instead of deleting accounts.' + ), + ) + date_joined = models.DateTimeField(_('date joined'), default=timezone.now) + + objects = UserManager() + + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email', 'first_name', 'last_name'] + + class Meta: + verbose_name = _('user') + verbose_name_plural = _('users') def __str__(self): - return self.get_username() + return self.username + + def get_full_name(self): + """ + Returns the first_name plus the last_name, with a space in between. + """ + full_name = '%s %s' % (self.first_name, self.last_name) + return full_name.strip() + + def get_short_name(self): + "Returns the short name for the user." + return self.first_name + + def email_user(self, subject, message, from_email=None, **kwargs): + """ + Sends an email to this User. + """ + send_mail(subject, message, from_email, [self.email], **kwargs) + + def generate_username(self): + """ + Generates a unique username based on the first and last names. + For example: Guy Carlier gives gcarlier, and gcarlier1 if the first one exists + Returns the generated username + """ + user_name = str(self.first_name[0]+self.last_name).lower() + un_set = [u.username for u in User.objects.all()] + if user_name in un_set: + i = 1 + while user_name+str(i) in un_set: + i += 1 + user_name += str(i) + self.username = user_name + return user_name class Page: pass diff --git a/core/templates/core/base.html b/core/templates/core/base.html new file mode 100644 index 00000000..e9573f29 --- /dev/null +++ b/core/templates/core/base.html @@ -0,0 +1,30 @@ + + + + + {% block title %}Bienvenue sur le Sith de l'AE!{% endblock %} + + + +
+ {% block header %} + {% if user %}Hello, {{ user.username }}!{% endif %} + + {% endblock %} +
+ +
+ {% block content %}{% endblock %} +
+ + + + diff --git a/core/templates/core/index.html b/core/templates/core/index.html new file mode 100644 index 00000000..e5249eb1 --- /dev/null +++ b/core/templates/core/index.html @@ -0,0 +1 @@ +{% extends "sith/base.html" %} diff --git a/core/templates/core/login.html b/core/templates/core/login.html new file mode 100644 index 00000000..97d731b0 --- /dev/null +++ b/core/templates/core/login.html @@ -0,0 +1,14 @@ +{% extends "core/base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +

{{ title }}

+ +
+ {% csrf_token %} + {{ form }} +

+
+{% endblock %} + diff --git a/core/templates/core/register.html b/core/templates/core/register.html new file mode 100644 index 00000000..4ca1300a --- /dev/null +++ b/core/templates/core/register.html @@ -0,0 +1,13 @@ +{% extends "core/base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +

{{ title }}

+ +
+ {% csrf_token %} + {{ form }} +

+
+{% endblock %} diff --git a/core/templates/sith/register.html b/core/templates/sith/register.html deleted file mode 100644 index 2d55ef84..00000000 --- a/core/templates/sith/register.html +++ /dev/null @@ -1,13 +0,0 @@ -

Register a user

- -{% if username %} -You registered successfully, {{ username }}! -{% endif %} - -
- {% csrf_token %} - -
diff --git a/core/urls.py b/core/urls.py index f7de3ca3..36856df9 100644 --- a/core/urls.py +++ b/core/urls.py @@ -3,9 +3,9 @@ from django.conf.urls import url from . import views urlpatterns = [ - url(r'^$', views.index, name='core_index'), + url(r'^$', views.index, name='index'), url(r'^login$', views.login, name='login'), + url(r'^logout$', views.logout, name='logout'), url(r'^register$', views.register, name='register'), - url(r'^guy$', views.guy, name='guy'), ] diff --git a/core/views.py b/core/views.py index 008ea9c7..cc863ad9 100644 --- a/core/views.py +++ b/core/views.py @@ -1,8 +1,9 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect from django.http import HttpResponse +from django.contrib.auth import logout as auth_logout from .models import User -from .forms import RegisteringForm +from .forms import RegisteringForm, LoginForm import logging @@ -11,22 +12,30 @@ logging.basicConfig(level=logging.DEBUG) def index(request): return HttpResponse("Hello, world. You're at the core index.") -def login(request): - return HttpResponse("Login page") - def register(request): if request.method == 'POST': - logging.debug("Registering "+request.POST['username']) form = RegisteringForm(request.POST) if form.is_valid(): - u = User(username=request.POST['username'], password=request.POST['password1'], email="guy@plop.guy") - u.save() - return render(request, "sith/register.html", {'username': u.get_username(), - 'form': RegisteringForm().as_ul()}) - else: - return render(request, "sith/register.html", {'form': form.as_ul()}) - form = RegisteringForm() - return render(request, "sith/register.html", {'form': form.as_ul()}) + logging.debug("Registering "+form.cleaned_data['first_name']+form.cleaned_data['last_name']) + u = form.save() + form = RegisteringForm() + else: + form = RegisteringForm() + return render(request, "core/register.html", {'title': 'Register a user', 'form': form.as_p()}) -def guy(request): - return HttpResponse("Guyuguyguygé") +def login(request): + if request.method == 'POST': + try: + form = LoginForm(request) + form.login() + # TODO redirect to profile when done + return redirect('index') + except Exception as e: + logging.debug(e) + else: + form = LoginForm() + return render(request, "core/login.html", {'title': 'Login', 'form': form.as_p()}) + +def logout(request): + auth_logout(request) + return redirect('core:index') diff --git a/sith/urls.py b/sith/urls.py index 919f62d2..aa47ef80 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -17,6 +17,6 @@ from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ - url(r'^', include('core.urls', namespace="sith")), + url(r'^', include('core.urls', namespace="core")), url(r'^admin/', include(admin.site.urls)), ]