Coverage for heritrace/routes/auth.py: 100%

61 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-08-01 22:12 +0000

1import os 

2from datetime import timedelta 

3 

4from flask import (Blueprint, current_app, flash, redirect, request, session, 

5 url_for) 

6from flask_babel import gettext 

7from flask_login import current_user, login_user, logout_user 

8from heritrace.apis.orcid import extract_orcid_id, is_orcid_url 

9from heritrace.models import User 

10from requests_oauthlib import OAuth2Session 

11 

12auth_bp = Blueprint("auth", __name__) 

13 

14 

15@auth_bp.route("/login") 

16def login(): 

17 if current_user.is_authenticated: 

18 return redirect(url_for("main.catalogue")) 

19 

20 if os.environ.get("FLASK_ENV") == "demo": 

21 user_id_from_env = os.environ.get("USER_ID", "demo_user") 

22 demo_uri = f"http://example.org/demo/{user_id_from_env}" 

23 user_name = f"Demo User ({user_id_from_env})" 

24 

25 user = User(id=demo_uri, name=user_name, orcid=demo_uri) 

26 session["user_name"] = user_name 

27 session.permanent = True 

28 current_app.permanent_session_lifetime = timedelta(days=30) 

29 login_user(user) 

30 

31 flash(gettext("Welcome! You've been automatically logged in to the demo"), "info") 

32 return redirect(url_for("main.catalogue")) 

33 

34 callback_url = url_for("auth.callback", _external=True, _scheme="https") 

35 orcid = OAuth2Session( 

36 current_app.config["ORCID_CLIENT_ID"], 

37 redirect_uri=callback_url, 

38 scope=[current_app.config["ORCID_SCOPE"], "openid"], 

39 ) 

40 authorization_url, state = orcid.authorization_url( 

41 current_app.config["ORCID_AUTHORIZE_URL"], 

42 prompt="login", # Forza il re-login 

43 nonce=os.urandom(16).hex(), # Aggiungiamo un nonce per sicurezza 

44 ) 

45 session["oauth_state"] = state 

46 return redirect(authorization_url) 

47 

48 

49@auth_bp.route("/callback") 

50def callback(): 

51 if request.url.startswith("http://"): 

52 secure_url = request.url.replace("http://", "https://", 1) 

53 else: 

54 secure_url = request.url 

55 

56 orcid = OAuth2Session( 

57 current_app.config["ORCID_CLIENT_ID"], state=session["oauth_state"] 

58 ) 

59 try: 

60 token = orcid.fetch_token( 

61 current_app.config["ORCID_TOKEN_URL"], 

62 client_secret=current_app.config["ORCID_CLIENT_SECRET"], 

63 authorization_response=secure_url, 

64 ) 

65 except Exception as e: 

66 flash( 

67 gettext("An error occurred during authentication. Please try again"), 

68 "danger", 

69 ) 

70 return redirect(url_for("auth.login")) 

71 orcid_id = token["orcid"] 

72 

73 whitelist = current_app.config["ORCID_WHITELIST"] 

74 if whitelist: 

75 normalized_whitelist = { 

76 extract_orcid_id(item) if is_orcid_url(item) else item for item in whitelist 

77 } 

78 if orcid_id not in normalized_whitelist: 

79 flash( 

80 gettext("Your ORCID is not authorized to access this application"), 

81 "danger", 

82 ) 

83 return redirect(url_for("auth.login")) 

84 

85 session["user_name"] = token["name"] 

86 user = User(id=orcid_id, name=token["name"], orcid=orcid_id) 

87 session.permanent = True 

88 current_app.permanent_session_lifetime = timedelta(days=30) 

89 login_user(user) 

90 flash(gettext("Welcome back %(name)s!", name=current_user.name), "success") 

91 return redirect(url_for("main.catalogue")) 

92 

93 

94@auth_bp.route("/logout") 

95def logout(): 

96 if not current_user.is_authenticated: 

97 return "", 401 

98 logout_user() 

99 flash(gettext("You have been logged out"), "info") 

100 return redirect(url_for("main.index"))