Coverage for seedboxsync/front/views/auth/login.py: 87%

38 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-26 17:14 +0000

1# 

2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr> 

3# 

4# For the full copyright and license information, please view the LICENSE 

5# file that was distributed with this source code. 

6# 

7"""SeedboxSync Flask view for authentication handling.""" 

8 

9from flask import redirect, render_template, request, url_for 

10from flask_login import login_user 

11from werkzeug.wrappers.response import Response 

12from seedboxsync.core import current_app 

13from seedboxsync.core.database.models.user import User 

14from seedboxsync.front.babel import gettext as _ 

15from seedboxsync.front.forms import LoginForm 

16from seedboxsync.front.oauth2 import oauth 

17from seedboxsync.front.utils import is_safe_redirect_url, toast 

18from seedboxsync.front.views import bp_auth as bp 

19 

20 

21@bp.route("/login", methods=["GET", "POST"]) 

22def login() -> str | Response: 

23 """ 

24 Render and process the user login view. 

25 

26 Authenticates user credentials, logs in the user session upon successful 

27 validation, and redirects to the requested target URL or homepage. 

28 

29 Returns: 

30 str | Response: Rendered login template or HTTP redirect response. 

31 """ 

32 # Auto redirect to OAuth2 provider if OAuth is enabled and built-in authentication is disabled 

33 oauth_builtin_authentication_disabled = current_app.seedboxsync_config.get("oauth_builtin_authentication_disabled", False) 

34 oauth_enabled = current_app.seedboxsync_config.get("oauth_enabled", False) 

35 if oauth_builtin_authentication_disabled and oauth_enabled: 

36 return __authorize_redirect() 

37 

38 if request.args.get("provider") == "oauth2" and current_app.seedboxsync_config.get("oauth_enabled"): 

39 return __authorize_redirect() 

40 

41 form = LoginForm() 

42 

43 # Basic auth 

44 if form.validate_on_submit(): 

45 login = request.form.get("login") or "" 

46 password = request.form.get("password") or "" 

47 next_url = request.args.get("next") 

48 remember = request.form.get("remember") == "1" 

49 

50 user = User.authenticate(login, password) 

51 

52 # User is logged 

53 if user is not None: 

54 login_user(user, remember=remember) 

55 toast(_("Logged in successfully."), _("Login"), "success") 

56 

57 # Sanitization/Validation for SonarQube (Open Redirect protection) 

58 target_url = url_for("frontend.homepage") 

59 if next_url and is_safe_redirect_url(next_url): 

60 target_url = next_url 

61 

62 return redirect(target_url) 

63 

64 # User is not logged 

65 toast(_("Invalid username or password."), _("Login"), "danger") 

66 

67 return render_template("login.html", form=form) 

68 

69 

70def __authorize_redirect() -> Response: 

71 """ 

72 Redirect the user to the configured OAuth/OIDC provider's authorization URL. 

73 

74 Obtains the registered OAuth client name from application settings, builds 

75 the external redirect URI, and initiates the OIDC authorization flow. 

76 

77 Returns: 

78 Response: Flask redirect response object targeting the identity provider. 

79 """ 

80 # Retrieve OAuth client name and construct absolute callback URL 

81 oauth_name = current_app.seedboxsync_config.get("oauth_name") 

82 redirect_uri = url_for("auth.authorize", _external=True) 

83 

84 # Initiate authorization redirect via Authlib client 

85 return oauth.create_client(oauth_name).authorize_redirect(redirect_uri) # type: ignore[no-any-return]