Add backdoor to login as other users (in debug mode only)

This commit is contained in:
Yohann D'ANELLO 2020-07-30 12:50:48 +02:00
parent b49db39080
commit fb775de923
6 changed files with 53 additions and 7 deletions

View File

@ -3,9 +3,12 @@
import hashlib import hashlib
from django.contrib.auth.hashers import PBKDF2PasswordHasher from django.conf import settings
from django.contrib.auth.hashers import PBKDF2PasswordHasher, BasePasswordHasher
from django.utils.crypto import constant_time_compare from django.utils.crypto import constant_time_compare
from note_kfet.middlewares import get_current_authenticated_user, get_current_session
class CustomNK15Hasher(PBKDF2PasswordHasher): class CustomNK15Hasher(PBKDF2PasswordHasher):
""" """
@ -20,8 +23,37 @@ class CustomNK15Hasher(PBKDF2PasswordHasher):
""" """
algorithm = "custom_nk15" algorithm = "custom_nk15"
def must_update(self, encoded):
if settings.DEBUG:
current_user = get_current_authenticated_user()
if current_user is not None and current_user.is_superuser:
return False
return True
def verify(self, password, encoded): def verify(self, password, encoded):
if settings.DEBUG:
current_user = get_current_authenticated_user()
if current_user is not None and current_user.is_superuser\
and get_current_session().get("permission_mask", -1) >= 42:
return True
if '|' in encoded: if '|' in encoded:
salt, db_hashed_pass = encoded.split('$')[2].split('|') salt, db_hashed_pass = encoded.split('$')[2].split('|')
return constant_time_compare(hashlib.sha256((salt + password).encode("utf-8")).hexdigest(), db_hashed_pass) return constant_time_compare(hashlib.sha256((salt + password).encode("utf-8")).hexdigest(), db_hashed_pass)
return super().verify(password, encoded) return super().verify(password, encoded)
class DebugSuperuserBackdoor(PBKDF2PasswordHasher):
"""
In debug mode and during the beta, superusers can login into other accounts for tests.
"""
def must_update(self, encoded):
return False
def verify(self, password, encoded):
if settings.DEBUG:
current_user = get_current_authenticated_user()
if current_user is not None and current_user.is_superuser\
and get_current_session().get("permission_mask", -1) >= 42:
return True
return super().verify(password, encoded)

View File

@ -6,6 +6,7 @@ from datetime import datetime, timedelta
from PIL import Image from PIL import Image
from django.conf import settings from django.conf import settings
from django.contrib.auth import logout
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
@ -21,6 +22,7 @@ from note.forms import ImageForm
from note.models import Alias, NoteUser from note.models import Alias, NoteUser
from note.models.transactions import Transaction, SpecialTransaction from note.models.transactions import Transaction, SpecialTransaction
from note.tables import HistoryTable, AliasTable from note.tables import HistoryTable, AliasTable
from note_kfet.middlewares import _set_current_user_and_ip
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from permission.models import Role from permission.models import Role
from permission.views import ProtectQuerysetMixin from permission.views import ProtectQuerysetMixin
@ -38,6 +40,8 @@ class CustomLoginView(LoginView):
form_class = CustomAuthenticationForm form_class = CustomAuthenticationForm
def form_valid(self, form): def form_valid(self, form):
logout(self.request)
_set_current_user_and_ip(form.get_user(), self.request.session, None)
self.request.session['permission_mask'] = form.cleaned_data['permission_mask'].rank self.request.session['permission_mask'] = form.cleaned_data['permission_mask'].rank
return super().form_valid(form) return super().form_valid(form)

View File

@ -1,7 +1,6 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User, AnonymousUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -137,7 +136,7 @@ class PermissionBackend(ModelBackend):
if sess is not None and sess.session_key is None: if sess is not None and sess.session_key is None:
return False return False
if user_obj.is_superuser and get_current_session().get("permission_mask", -1) >= 42: if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42:
return True return True
if obj is None: if obj is None:

View File

@ -4,6 +4,7 @@
from functools import lru_cache from functools import lru_cache
from time import time from time import time
from django.conf import settings
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from note_kfet.middlewares import get_current_session from note_kfet.middlewares import get_current_session
@ -32,6 +33,10 @@ def memoize(f):
sess_funs = new_sess_funs sess_funs = new_sess_funs
def func(*args, **kwargs): def func(*args, **kwargs):
if settings.DEBUG:
# Don't memoize in DEBUG mode
return f(*args, **kwargs)
nonlocal last_collect nonlocal last_collect
if time() - last_collect > 60: if time() - last_collect > 60:

View File

@ -1,6 +1,6 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_authenticated_user
@ -50,6 +50,10 @@ def pre_save_object(sender, instance, **kwargs):
# In the other case, we check if he/she has the right to change one field # In the other case, we check if he/she has the right to change one field
previous = qs.get() previous = qs.get()
if isinstance(instance, User) and instance.last_login != previous.last_login:
pass #return
for field in instance._meta.fields: for field in instance._meta.fields:
field_name = field.name field_name = field.name
old_value = getattr(previous, field.name) old_value = getattr(previous, field.name)

View File

@ -57,6 +57,8 @@ if "cas_server" in INSTALLED_APPS:
if "logs" in INSTALLED_APPS: if "logs" in INSTALLED_APPS:
MIDDLEWARE += ('note_kfet.middlewares.SessionMiddleware',) MIDDLEWARE += ('note_kfet.middlewares.SessionMiddleware',)
if "debug_toolbar" in INSTALLED_APPS: if DEBUG:
MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware") PASSWORD_HASHERS += ['member.hashers.DebugSuperuserBackdoor']
INTERNAL_IPS = ['127.0.0.1'] if "debug_toolbar" in INSTALLED_APPS:
MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware")
INTERNAL_IPS = ['127.0.0.1']