Skip to content

user: disable the javascript requirement for login view #3830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 84 additions & 80 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
i18n_terms_filter,
or_terms_filter_by_criteria,
)
from .theme.forms import LoginForm
from .utils import TranslatedList, get_current_language

APP_THEME = ["bootstrap3"]
Expand Down Expand Up @@ -308,6 +309,8 @@ def _(x):
#: Email subjects for password reset
SECURITY_EMAIL_SUBJECT_PASSWORD_RESET = _("RERO ID password reset")
SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE = _("Your RERO ID password has been reset")
#: Custom login form to support username login
SECURITY_LOGIN_FORM = LoginForm
#: Redis session storage URL.
ACCOUNTS_SESSION_REDIS_URL = "redis://localhost:6379/1"
#: Enable session/user id request tracing. This feature will add X-Session-ID
Expand Down
79 changes: 0 additions & 79 deletions rero_ils/theme/assets/js/reroils/login.js

This file was deleted.

1 change: 0 additions & 1 deletion rero_ils/theme/assets/js/reroils/public.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import 'bootstrap';
import './tooltip';
import './toast';
import './login';
import './toggle';
import './ills_request';
import './show_more';
64 changes: 64 additions & 0 deletions rero_ils/theme/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2024 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""RERO ILS forms."""

from flask_security.confirmable import requires_confirmation
from flask_security.forms import Form
from flask_security.forms import LoginForm as BaseLoginForm
from flask_security.utils import get_message, hash_password, verify_and_update_password

from rero_ils.modules.users.api import User


class LoginForm(BaseLoginForm):
"""The login form (/signin)."""

def validate(self, extra_validators=None):
"""Validate the form.

Copied from invenio-flask-security.
"""
if not super(Form, self).validate(extra_validators=extra_validators):
return False

# uses our own manner to retrieve the user, the rest is identical to flask-security
self.user = None
user = User.get_by_username_or_email(self.email.data)
if user:
self.user = user.user

if self.user is None:
self.email.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
# Reduce timing variation between existing and non-existung users
hash_password(self.password.data)
return False
if not self.user.password:
self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
# Reduce timing variation between existing and non-existung users
hash_password(self.password.data)
return False
if not verify_and_update_password(self.password.data, self.user):
self.password.errors.append(get_message("INVALID_PASSWORD")[0])
return False
if requires_confirmation(self.user):
self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
return False
if not self.user.is_active:
self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
return False
return True
8 changes: 6 additions & 2 deletions rero_ils/theme/templates/rero_ils/login_user.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ <h3 class="card-title my-4">{{_('Log in to account') }}</h3>
</button>
</div>

<form action="{{ url_for_security('login') }}" data-action="/api/login" method="POST" id="login-user"
name="login_user_form">
<form
action="{{ url_for_security('login') }}"
method="POST"
id="login-user"
name="login_user_form"
>
{{form.hidden_tag()}}
{{form_errors(form)}}
{{ render_field(form.email, icon="fa fa-user", autofocus=True, errormsg=False, placeholder=_("Username or e-mail")) }}
Expand Down
43 changes: 43 additions & 0 deletions tests/ui/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import pytest
from flask import session, url_for
from flask_login import login_user, logout_user
from flask_security import url_for_security
from invenio_accounts.testutils import login_user_via_view
from utils import postdata

from rero_ils.modules.users.api import user_formatted_name
Expand Down Expand Up @@ -183,3 +185,44 @@ def test_google_analytics(client, app):
app.config["RERO_ILS_GOOGLE_ANALYTICS_TAG_ID"] = "GA-Foo"
result = client.get(url_for("rero_ils.index"))
assert "gtag" in result.text


def test_login(client, app, user_with_profile):
"""Testing the frontend login view."""

## bad password
# be sure that no one is logged
client.get(url_for_security("logout"))
res = login_user_via_view(
client=client, email=user_with_profile.username, password="bad password"
)
assert "Invalid user or password" in res.text
assert res.status_code == 200

## bad email
client.get(url_for_security("logout"))
res = login_user_via_view(
client=client,
email="[email protected]",
password=user_with_profile.password_plaintext,
)
assert "Invalid user or password" in res.text
assert res.status_code == 200

## login with email
client.get(url_for_security("logout"))
res = login_user_via_view(
client=client,
email=user_with_profile.email,
password=user_with_profile.password_plaintext,
)
assert res.status_code == 302

## login with username
client.get(url_for_security("logout"))
res = login_user_via_view(
client=client,
email=user_with_profile.username,
password=user_with_profile.password_plaintext,
)
assert res.status_code == 302