diff --git a/fet2020/authentications/authentications.py b/fet2020/authentications/authentications.py
index cd09d144..959cdcce 100644
--- a/fet2020/authentications/authentications.py
+++ b/fet2020/authentications/authentications.py
@@ -11,109 +11,6 @@ host = "ldap://juri.fet.htu.tuwien.ac.at"
port = 389
-def authentication(username, password):
- # no empty passwords
- if password is None or password.strip() == "":
- return None
-
- server = Server(host, port=port)
- userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
-
- firstname = ""
- surname = ""
- mail = ""
-
- try:
- c = Connection(server, user=userdn, password=password, auto_bind=True)
-
- if not c.extend.standard.who_am_i():
- logger.info("Username '%s' is not in the list.", username)
- return None
-
- # get member infos from ldap
- c.search(
- search_base=userdn,
- search_filter=f"(uid={username})",
- search_scope="SUBTREE",
- attributes=["givenName", "sn", "mail"],
- )
-
- firstname = c.response[0]["attributes"]["givenName"][0]
- surname = c.response[0]["attributes"]["sn"][0]
- mail = c.response[0]["attributes"]["mail"][0]
-
- except LDAPBindError as e:
- logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
- return None
-
- except Exception as e:
- logger.info("Auth exception from username '%s'. Error: %s", username, e)
- return None
-
- # get member or if not then create a new member
- try:
- member = Member.objects.get(mailaccount=mail)
-
- # set username if not equal
- if member.username != username:
- member.username = username
- logger.info("User '%s' saved.", username)
- member.save()
-
- except Member.DoesNotExist:
- member = Member()
- member.firstname = firstname
- member.surname = surname
- member.username = username
- member.nickname = username
- member.mailaccount = mail
- logger.info("Member '%s' created.", username)
- member.save()
-
- logger.info("User '%s' logged in.", username)
- return username
-
-
-def get_finance_perm(username, password):
- # no empty passwords
- if password is None or password.strip() == "":
- return None
-
- server = Server(host, port=port)
- userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
-
- finance_perm = None
-
- try:
- c = Connection(server, user=userdn, password=password, auto_bind=True)
-
- if not c.extend.standard.who_am_i():
- logger.info("Username '%s' is not in the list.", username)
- return None
-
- # check if member has finance permission
- c.search(
- search_base="CN=finance,OU=Groups,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at",
- search_filter="(objectClass=posixGroup)",
- search_scope="SUBTREE",
- attributes=["memberUid"],
- )
-
- if username in c.response[0]["attributes"]["memberUid"]:
- logger.info("User '%s' has finance permission.", username)
- finance_perm = True
-
- except LDAPBindError as e:
- logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
- return None
-
- except Exception as e:
- logger.info("Auth exception from username '%s'. Error: %s", username, e)
- return None
-
- return finance_perm
-
-
def change_password(username, old_password, new_password):
server = Server(host, port=port, use_ssl=True)
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
diff --git a/fet2020/authentications/backends.py b/fet2020/authentications/backends.py
new file mode 100644
index 00000000..e7d14b3c
--- /dev/null
+++ b/fet2020/authentications/backends.py
@@ -0,0 +1,162 @@
+import logging
+
+from django.contrib.auth.models import Group, User
+from django.contrib.auth.backends import ModelBackend
+
+from ldap3 import Connection, Server
+from ldap3.core.exceptions import LDAPBindError
+
+from members.models import Member
+
+logger = logging.getLogger(__name__)
+host = "ldap://juri.fet.htu.tuwien.ac.at"
+port = 389
+
+
+class LdapBackend(ModelBackend):
+ def _check_ldap_user(self, username: str, password: str) -> bool:
+ server = Server(host, port=port)
+ userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
+
+ try:
+ c = Connection(server, user=userdn, password=password, auto_bind=True)
+
+ if not c.extend.standard.who_am_i():
+ logger.info("Username '%s' is not in the list.", username)
+ return False
+ except LDAPBindError as e:
+ logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
+ return False
+ except Exception as e:
+ logger.info("Auth exception from username '%s'. Error: %s", username, e)
+ return False
+
+ return True
+
+ def _check_fet_member(self, username: str, password: str) -> bool:
+ server = Server(host, port=port)
+ userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
+
+ try:
+ c = Connection(server, user=userdn, password=password, auto_bind=True)
+
+ # Get member infos from ldap
+ c.search(
+ search_base=userdn,
+ search_filter=f"(uid={username})",
+ search_scope="SUBTREE",
+ attributes=["givenName", "sn", "mail"],
+ )
+
+ firstname = c.response[0]["attributes"]["givenName"][0]
+ surname = c.response[0]["attributes"]["sn"][0]
+ mail = c.response[0]["attributes"]["mail"][0]
+ except LDAPBindError as e:
+ logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
+ return False
+ except Exception as e:
+ logger.info("Auth exception from username '%s'. Error: %s", username, e)
+ return False
+
+ # Try to get the member. If it not exists, then create a new member.
+ try:
+ member = Member.objects.get(mailaccount=mail)
+
+ # Set username if not equal
+ if member.username != username:
+ member.username = username
+ logger.info("Member '%s' saved.", username)
+ member.save()
+ except Member.DoesNotExist:
+ member = Member()
+ member.firstname = firstname
+ member.surname = surname
+ member.username = username
+ member.nickname = username
+ member.mailaccount = mail
+ logger.info("Member '%s' created.", username)
+ member.save()
+
+ return True
+
+ def _check_finance_perm(self, username: str, password: str) -> bool:
+ server = Server(host, port=port)
+ userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
+
+ try:
+ c = Connection(server, user=userdn, password=password, auto_bind=True)
+
+ # Check if member has finance permission
+ c.search(
+ search_base="CN=finance,OU=Groups,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at",
+ search_filter="(objectClass=posixGroup)",
+ search_scope="SUBTREE",
+ attributes=["memberUid"],
+ )
+
+ if username in c.response[0]["attributes"]["memberUid"]:
+ logger.info("User '%s' has finance permission.", username)
+ return True
+
+ except LDAPBindError as e:
+ logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
+ return False
+
+ except Exception as e:
+ logger.info("Auth exception from username '%s'. Error: %s", username, e)
+ return False
+
+ return False
+
+ def authenticate(self, request, username=None, password=None):
+ if username is None or password is None:
+ return
+
+ # Set username to lower because fet2020 can only work with lowercase letters.
+ username = username.lower()
+
+ if not self._check_ldap_user(username, password):
+ return
+
+ if not self._check_fet_member(username, password):
+ return
+
+ try:
+ user = User.objects.get(username)
+ except User.DoesNotExist:
+ user = User.objects.create_user(username)
+ finally:
+ if not self.user_can_authenticate(user):
+ logger.info("User '%s' is inactive.", user.get_username())
+ return
+
+ # Add user to all groups
+ for elem in Group.objects.all():
+ elem.user_set.add(user)
+
+ # Delete finance group if no permission
+ if not self._check_finance_perm(username, password):
+ finance_group = Group.objects.get(name="finance")
+ finance_group.user_set.remove(user)
+
+ return user
+
+
+class DebugBackend(ModelBackend):
+ def authenticate(self, request, username, password, **kwargs):
+ if (user := super().authenticate(request, username, password, **kwargs)) is None:
+ logger.info("User '%s' can't login. The user may not be a superuser.", username)
+ return None
+
+ # Try to get the member. If it not exists, then create a new member.
+ try:
+ member = Member.objects.get(mailaccount=user.email)
+ except Member.DoesNotExist:
+ member = Member()
+ member.username = username
+ member.nickname = username
+ member.mailaccount = user.email
+ logger.info("Member '%s' created.", username)
+ member.save()
+
+ return user
diff --git a/fet2020/authentications/forms.py b/fet2020/authentications/forms.py
index 718d716e..a2d43bd8 100644
--- a/fet2020/authentications/forms.py
+++ b/fet2020/authentications/forms.py
@@ -1,54 +1,17 @@
import logging
-from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
-from django.contrib.auth.models import Group, User
+from django.contrib.auth.forms import PasswordChangeForm
from django.core.exceptions import ValidationError
-from .authentications import authentication, change_password, get_finance_perm
+from .authentications import change_password
logger = logging.getLogger(__name__)
-class LoginForm(AuthenticationForm):
- def clean(self):
- username = self.cleaned_data.get("username").lower()
- password = self.cleaned_data.get("password")
-
- if username is not None and password:
- if (auth_user := authentication(username, password)) is None:
- raise ValidationError(
- "Bitte Benutzername und Passwort korrekt eingeben.",
- code="invalid_login",
- )
-
- try:
- self.user_cache = User.objects.get(username=auth_user.lower())
- except User.DoesNotExist:
- self.user_cache = User.objects.create_user(auth_user.lower())
- finally:
- self.confirm_login_allowed(self.user_cache)
-
- # add user to all groups
- for elem in Group.objects.all():
- elem.user_set.add(self.user_cache)
-
- # delete finance group if no permission
- if not get_finance_perm(username, password):
- finance_group = Group.objects.get(name="finance")
- finance_group.user_set.remove(self.user_cache)
-
- return self.cleaned_data
-
-
+# TODO: fix me
class LdapPasswordChangeForm(PasswordChangeForm):
def clean_old_password(self):
old_password = self.cleaned_data.get("old_password")
- if not authentication(self.user, old_password):
- raise ValidationError(
- self.error_messages["password_incorrect"],
- code="password_incorrect",
- )
-
return old_password
def save(self):
diff --git a/fet2020/authentications/views.py b/fet2020/authentications/views.py
index 998921ab..e83e2338 100644
--- a/fet2020/authentications/views.py
+++ b/fet2020/authentications/views.py
@@ -10,11 +10,10 @@ from django.urls import reverse_lazy
from documents.etherpadlib import del_ep_cookie
from .decorators import authenticated_user
-from .forms import LdapPasswordChangeForm, LoginForm
+from .forms import LdapPasswordChangeForm
class AuthLoginView(LoginView):
- authentication_form = LoginForm
redirect_authenticated_user = True
template_name = "authentications/login.html"
diff --git a/fet2020/fet2020/settings.py b/fet2020/fet2020/settings.py
index f0ebc4d4..c82517b5 100644
--- a/fet2020/fet2020/settings.py
+++ b/fet2020/fet2020/settings.py
@@ -5,7 +5,8 @@ import environ
env = environ.Env(
# set casting, default value
- DEBUG=(bool, True),
+ DEBUG=(str, "True"),
+ LDAP=(str, "False"),
MYSQL_HOST=(str, "mysql"),
MYSQL_PORT=(int, 3306),
MYSQL_DATABASE=(str, "fet2020db"),
@@ -23,7 +24,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# DEBUGGING
-DEBUG = env("DEBUG")
+DEBUG = (env("DEBUG").lower() == "true")
+LDAP = (env("LDAP").lower() == "true")
# MODELS
@@ -63,9 +65,14 @@ INSTALLED_APPS = [
# AUTHENTICATIONS
-AUTHENTICATION_BACKENDS = [
- "django.contrib.auth.backends.ModelBackend",
-]
+if not DEBUG and LDAP:
+ AUTHENTICATION_BACKENDS = [
+ "authentications.backends.LdapBackend",
+ ]
+else:
+ AUTHENTICATION_BACKENDS = [
+ "authentications.backends.DebugBackend",
+ ]
LOGIN_REDIRECT_URL = "home"
LOGIN_URL = "/auth/login"
diff --git a/fet2020/templates/admin/base.html b/fet2020/templates/admin/base.html
index d7df2347..6262be3c 100644
--- a/fet2020/templates/admin/base.html
+++ b/fet2020/templates/admin/base.html
@@ -17,7 +17,7 @@
{% if site_url %}
Zurück zur FET Homepage
{% endif %}
- Passwort ändern
+
{% translate 'Log out' %}
{% endblock %}