diff --git a/config/docker/Dockerfile b/config/docker/Dockerfile index 2929ecf8ae..5e0b69ab8f 100644 --- a/config/docker/Dockerfile +++ b/config/docker/Dockerfile @@ -15,4 +15,4 @@ RUN apk update && \ pip3 install -Ir requirements-dev.txt && \ rm requirements.txt requirements-dev.txt -WORKDIR /ion +WORKDIR /ion \ No newline at end of file diff --git a/intranet/apps/auth/backends.py b/intranet/apps/auth/backends.py index 84eadd1767..18f6cbd0b2 100644 --- a/intranet/apps/auth/backends.py +++ b/intranet/apps/auth/backends.py @@ -22,6 +22,7 @@ class PamAuthenticationResult(enum.Enum): FAILURE = 0 # Authentication failed SUCCESS = 1 # Authentication succeeded EXPIRED = -1 # Password expired; needs reset + LOCKED = 6 # User locked out due to incorrect attempts class PamAuthenticationBackend: @@ -72,7 +73,7 @@ def pam_auth(username, password): realm = settings.CSL_REALM pam_authenticator = pam.pam() full_username = f"{username}@{realm}" - result = pam_authenticator.authenticate(full_username, password) + result = pam_authenticator.authenticate(full_username, password, service="ion-login") if result: result = PamAuthenticationResult.SUCCESS @@ -80,6 +81,8 @@ def pam_auth(username, password): else: logger.debug("PAM failed to authorize %s", username) result = PamAuthenticationResult.FAILURE + if pam_authenticator.code == 6: + result = PamAuthenticationResult.LOCKED if "authentication token is no longer valid" in pam_authenticator.reason.lower(): result = PamAuthenticationResult.EXPIRED logger.debug("Password for %s@%s expired, needs reset", username, realm) @@ -128,6 +131,10 @@ def authenticate(self, request, username=None, password=None): elif result == PamAuthenticationResult.EXPIRED: user, _ = get_user_model().objects.get_or_create(username="RESET_PASSWORD", user_type="service", id=999999) return user + elif result == PamAuthenticationResult.LOCKED: + if request is not None: + request.session["user_locked_out"] = 1 + return None else: pam_authenticate_failures.inc() return None diff --git a/intranet/apps/auth/views.py b/intranet/apps/auth/views.py index 7f684ca51a..5dc17ae3e0 100644 --- a/intranet/apps/auth/views.py +++ b/intranet/apps/auth/views.py @@ -151,6 +151,7 @@ def index_view(request, auth_form=None, force_login=False, added_context=None, h } schedule = schedule_context(request) data.update(schedule) + if added_context is not None: data.update(added_context) return render(request, "auth/login.html", data) @@ -172,13 +173,21 @@ def post(self, request): if re.search(r"^(\d{4})?[a-zA-Z]+\d?$", username) is None: return index_view(request, added_context={"auth_message": "Your username format is incorrect."}) - form = AuthenticateForm(data=request.POST) + form = AuthenticateForm(request, data=request.POST) if request.session.test_cookie_worked(): request.session.delete_test_cookie() else: logger.warning("No cookie support detected! This could cause problems.") + if request.session.get("user_locked_out", "") == 1: + request.session.pop("user_locked_out") + return index_view( + request, + auth_form=form, + added_context={"auth_message": "You are locked out due to too many incorrect logins. Please try again later."}, + ) + if form.is_valid(): reset_user, _ = get_user_model().objects.get_or_create(username="RESET_PASSWORD", user_type="service", id=999999) if form.get_user() == reset_user: