Commit a4955c68 authored by Jean-Baptiste Pasquier's avatar Jean-Baptiste Pasquier

Merge branch 'sib-245' into 'master'

feat: login with username or email (startinblox/applications/sib-app#245)

See merge request !29
parents 05f9fdba a269a07c
Pipeline #6167 passed with stage
in 27 seconds
......@@ -63,6 +63,14 @@ The workflow starts at : http://127.0.0.1:8000/accounts/register/
Then, override template by copying the directory `djangoldp_account/templates/django_registration/` to your project and modify them.
## Enabling login by email and username
Modify the `AUTHENTICATION_BACKENDS` variable in `settings.py` to replace
`'django.contrib.auth.backends.ModelBackend'` by
`'djangoldp_account.auth.backends.EmailOrUsernameAuthBackend'`.
Ensure `'djangoldp_account.auth.backends.EmailOrUsernameAuthBackend'` is
first in the list.
## Authenticate from an external provider
......
import json
from django.contrib.auth.backends import ModelBackend, UserModel
from django.core.exceptions import PermissionDenied
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.validators import validate_email
from jwkest import BadSyntax
from jwkest.jwt import JWT
from djangoldp_account.auth.solid import Solid
from djangoldp_account.errors import LDPLoginError
UserModel = get_user_model()
class ExternalUserBackend(ModelBackend):
class EmailOrUsernameAuthBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
validate_email(username)
user = UserModel.objects.get(email=username)
if user.check_password(password):
return user
except (ValidationError, UserModel.DoesNotExist):
return super().authenticate(request, username, password, **kwargs)
user = None
class ExternalUserBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if 'HTTP_AUTHORIZATION' in request.META:
jwt = request.META['HTTP_AUTHORIZATION']
if jwt.startswith("Bearer"):
jwt = jwt[7:]
username = kwargs.get(UserModel.USERNAME_FIELD)
_jwt = JWT()
try:
unpacked = json.loads(_jwt.unpack(jwt).part[1])
......@@ -29,8 +41,6 @@ class ExternalUserBackend(ModelBackend):
id_token = unpacked
try:
Solid.check_id_token_exp(id_token['exp'])
Solid.confirm_webid(id_token['sub'], id_token['iss'])
except LDPLoginError as e:
raise PermissionDenied(e.description)
......@@ -41,4 +51,3 @@ class ExternalUserBackend(ModelBackend):
if self.user_can_authenticate(user):
return user
......@@ -4,7 +4,6 @@ from django.utils.deprecation import MiddlewareMixin
class JWTUserMiddleware(MiddlewareMixin):
def process_request(self, request):
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, 'user'):
......
import validators
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.validators import validate_email
from django.contrib.auth.models import AbstractUser
from django.db import models
from importlib import import_module
......@@ -29,6 +29,7 @@ s_fields = []
s_fields.extend(user_fields)
s_fields.extend(user_nested_fields)
class LDPUser(AbstractUser, Model):
slug = models.SlugField(unique=True, blank=True, null=True)
......@@ -36,12 +37,11 @@ class LDPUser(AbstractUser, Model):
verbose_name = _('user')
verbose_name_plural = _('users')
rdf_type = 'foaf:user'
owner_field = 'id'
lookup_field = 'slug'
container_path = 'users'
owner_field = 'urlid'
permission_classes=getattr(settings, 'USER_PERMISSION_CLASSES', [LDPPermissions])
nested_fields=user_nested_fields
permission_classes = getattr(settings, 'USER_PERMISSION_CLASSES', [LDPPermissions])
nested_fields = user_nested_fields
serializer_fields = s_fields
anonymous_perms = getattr(settings, 'USER_ANONYMOUS_PERMISSIONS', ['view'])
authenticated_perms = getattr(settings, 'USER_AUTHENTICATED_PERMISSIONS', ['inherit'])
......
#content {
display: grid;
align-items: center;
justify-items: center;
}
#content .sib-login-title {
max-width: 800px;
}
#content .sib-login-forms {
max-width: 500px;
padding: 2rem;
border: 1px solid gray;
border-radius: 0.5rem;
}
hr.separator {
border: 0;
height: 1px;
background-image: linear-gradient(to right, rgba(0, 0, 0, 0), gray, rgba(0, 0, 0, 0));
}
#content .sib-login-forms .error {
color: red;
padding: 0;
margin: 0 0 2rem 0;
}
#content .sib-btn-link {
color: #80BCFF;
font-weight: bold;
padding: 1rem;
margin: 0;
text-decoration: none;
transition: color 0.5s;
}
#content .sib-btn-link:hover {
color: #99C9FF;
text-decoration: underline;
transition: color 0.5s;
}
#content .sib-login-forms .sib-form-table {
display: flex;
align-self: center;
}
#content .sib-login-form {
display: flex;
flex-direction: column;
}
#content .sib-validate {
margin-left: 0;
margin-right: 0;
}
\ No newline at end of file
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% block content %}
{% block title %}
{% trans "Login" %}
{% endblock %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% block supplementary_css %}
<link rel="stylesheet" href="{% static 'base.css' %}"/>
<link rel="stylesheet" href="{% static 'registration/login.css' %}"/>
{% endblock %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login TEST to see this page.</p>
{% block content %}
{% if next %}
<div class="sib-login-title">
{% if user.is_authenticated %}
<h2>
{% blocktrans %}
Your account doesn't have access to this page. To proceed,
please login with an account that has access.
{% endblocktrans %}
</h2>
{% else %}
<h2>{% trans "Please login to see this page." %}</h2>
{% endif %}
</div>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
<p> or login using another authentification provider</p>
<form method="post" action="{% url 'oidc_login' %}">
{% csrf_token %}
<table>
<tr>
<td><label for="id_subject">email, web-id, or provider url:</label></td>
<td><input type="text" name="subject" required id="id_subject"/></td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
<div class="sib-login-forms">
{% if form.errors %}
<p class="error">{% trans "Your username and password didn't match. Please try again." %}</p>
{% endif %}
<form class="sib-login-form" method="post" action="{% url 'login' %}">
{% csrf_token %}
<table class="sib-form-table">
<tr>
<td>{% trans "Username or email:" %}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input class="sib-validate" type="submit" value="{% trans 'login' %}"/>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
<a class="sib-btn-link" href="{% url 'password_reset' %}">{% trans "Lost password?" %}</a>
<hr class="separator"/>
<p>{% trans "or login using another authentification provider" %}</p>
<form class="sib-login-form" method="post" action="{% url 'oidc_login' %}">
{% csrf_token %}
<label for="id_subject">{% trans "email, web-id, or provider url:" %}</label>
<input type="text" name="subject" required id="id_subject"/>
<input class="sib-validate" type="submit" value="{% trans 'login' %}"/>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
</div>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment