Merge pull request #5967 from FinnStutzenstein/loginErrorNonActive

Add login errors for inactive users
This commit is contained in:
Emanuel Schütze 2021-03-19 09:42:44 +01:00 committed by GitHub
commit 2e1690d2d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 17 deletions

View File

@ -99,18 +99,21 @@ STATICFILES_DIRS = [os.path.join(MODULE_DIR, "static")] + [
STATIC_ROOT = os.path.join(OPENSLIDES_USER_DATA_DIR, "collected-static") STATIC_ROOT = os.path.join(OPENSLIDES_USER_DATA_DIR, "collected-static")
# Files # Files
# https://docs.djangoproject.com/en/1.10/topics/files/ # https://docs.djangoproject.com/en/2.2/topics/files/
MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_DIR, "media", "") MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_DIR, "media", "")
MEDIA_URL = "/media/"
# Sessions and user authentication # Sessions and user authentication
# https://docs.djangoproject.com/en/1.10/topics/http/sessions/ # https://docs.djangoproject.com/en/2.2/topics/http/sessions/
# https://docs.djangoproject.com/en/1.10/topics/auth/ # https://docs.djangoproject.com/en/2.2/topics/auth/
AUTH_USER_MODEL = "users.User" AUTH_USER_MODEL = "users.User"
AUTH_GROUP_MODEL = "users.Group" AUTH_GROUP_MODEL = "users.Group"
AUTHENTICATION_BACKENDS = ["openslides.utils.auth_backend.ModelBackend"]
SESSION_COOKIE_NAME = "OpenSlidesSessionID" SESSION_COOKIE_NAME = "OpenSlidesSessionID"
SESSION_EXPIRE_AT_BROWSER_CLOSE = True SESSION_EXPIRE_AT_BROWSER_CLOSE = True
@ -127,12 +130,5 @@ PASSWORD_HASHERS = [
"django.contrib.auth.hashers.BCryptPasswordHasher", "django.contrib.auth.hashers.BCryptPasswordHasher",
] ]
# Files
# https://docs.djangoproject.com/en/1.10/topics/files/
MEDIA_URL = "/media/"
# Enable updating the last_login field for users on every login. # Enable updating the last_login field for users on every login.
ENABLE_LAST_LOGIN_FIELD = False ENABLE_LAST_LOGIN_FIELD = False

View File

@ -5,11 +5,11 @@ from typing import Iterable, List, Set, Union
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from django.conf import settings from django.conf import settings
from django.contrib.auth import ( from django.contrib.auth import (
authenticate as auth_authenticate,
login as auth_login, login as auth_login,
logout as auth_logout, logout as auth_logout,
update_session_auth_hash, update_session_auth_hash,
) )
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.auth.password_validation import validate_password from django.contrib.auth.password_validation import validate_password
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
@ -888,13 +888,19 @@ class UserLoginView(WhoAmIDataView):
raise ValidationError( raise ValidationError(
{"detail": "Cookies have to be enabled to use OpenSlides."} {"detail": "Cookies have to be enabled to use OpenSlides."}
) )
form = AuthenticationForm(self.request, data=self.request.data)
if not form.is_valid(): username = self.request.data.get("username")
password = self.request.data.get("password")
user = auth_authenticate(self.request, username=username, password=password)
if user is None:
raise ValidationError({"detail": "Username or password is not correct."}) raise ValidationError({"detail": "Username or password is not correct."})
self.user = form.get_user() elif not user.is_active:
if self.user.auth_type != "default": raise ValidationError({"detail": "You are not active."})
raise ValidationError({"detail": "Please login via your identity provider"}) elif user.auth_type != "default":
auth_login(self.request, self.user) raise ValidationError(
{"detail": "Please login via your identity provider."}
)
auth_login(self.request, user)
return super().post(*args, **kwargs) return super().post(*args, **kwargs)
def get_context_data(self, **context): def get_context_data(self, **context):

View File

@ -0,0 +1,14 @@
from typing import Any
from django.contrib.auth.backends import ModelBackend as _ModelBackend
class ModelBackend(_ModelBackend):
def user_can_authenticate(self, user: Any) -> bool:
"""
Overwrite the default check for is_active.
This allows us to do the check it later to distinguish between a user
have not the right credentials and having the right credentials but
not being active.
"""
return True

View File

@ -3,6 +3,7 @@ import json
from django.urls import reverse from django.urls import reverse
from rest_framework.test import APIClient from rest_framework.test import APIClient
from openslides.users.models import User
from tests.test_case import TestCase from tests.test_case import TestCase
@ -101,6 +102,8 @@ class TestUserLoginView(TestCase):
response = self.client.post(self.url) response = self.client.post(self.url)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
content = json.loads(response.content.decode())
self.assertEqual(content.get("detail"), "Username or password is not correct.")
def test_post_correct_data(self): def test_post_correct_data(self):
response = self.client.post( response = self.client.post(
@ -121,3 +124,41 @@ class TestUserLoginView(TestCase):
) )
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
content = json.loads(response.content.decode())
self.assertEqual(content.get("detail"), "Username or password is not correct.")
def test_user_inactive(self):
admin = User.objects.get()
admin.is_active = False
admin.save()
response = self.client.post(
self.url, {"username": "admin", "password": "admin"}
)
self.assertEqual(response.status_code, 400)
content = json.loads(response.content.decode())
self.assertEqual(content.get("detail"), "You are not active.")
def test_user_wrong_auth_type(self):
admin = User.objects.get()
admin.auth_type = "not default"
admin.save()
response = self.client.post(
self.url, {"username": "admin", "password": "admin"}
)
self.assertEqual(response.status_code, 400)
content = json.loads(response.content.decode())
self.assertEqual(
content.get("detail"), "Please login via your identity provider."
)
def test_no_cookies(self):
response = self.client.post(
self.url, {"username": "admin", "password": "admin", "cookies": False}
)
self.assertEqual(response.status_code, 400)
content = json.loads(response.content.decode())
self.assertEqual(
content.get("detail"), "Cookies have to be enabled to use OpenSlides."
)