Coverage for webapp / authentication.py: 97%
29 statements
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-29 22:06 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-29 22:06 +0000
1import os
3from urllib.parse import urlparse
5from pymacaroons import Macaroon
6from webapp.api import sso
8LOGIN_URL = os.getenv("LOGIN_URL", "https://login.ubuntu.com")
10PERMISSIONS = [
11 "edit_account",
12 "package_access",
13 "package_manage",
14 "package_metrics",
15 "package_register",
16 "package_release",
17 "package_update",
18 "package_upload_request",
19 "store_admin",
20]
23SESSION_DATA_KEYS = [
24 "macaroons",
25 "macaroon_root",
26 "macaroon_discharge",
27 "publisher",
28 "github_auth_secret",
29 "developer_token",
30 "exchanged_developer_token",
31 "csrf_token",
32] # keys for data stored in the session that should be cleared on logout
35def get_authorization_header(root, discharge):
36 """
37 Bind root and discharge macaroons and return the authorization header.
38 """
40 bound = Macaroon.deserialize(root).prepare_for_request(
41 Macaroon.deserialize(discharge)
42 )
44 return "macaroon root={}, discharge={}".format(root, bound.serialize())
47def get_publishergw_authorization_header(developer_token):
48 return {"Authorization ": f"Macaroon {developer_token}"}
51def is_authenticated(session):
52 """
53 Checks if the user is authenticated from the session
54 Returns True if the user is authenticated
55 """
56 return (
57 "publisher" in session
58 and "macaroon_discharge" in session
59 and "macaroon_root" in session
60 ) or ("publisher" in session and "macaroons" in session)
63def empty_session(session):
64 """
65 Empty the session, used to logout.
66 """
67 for key in SESSION_DATA_KEYS:
68 session.pop(key, None)
71def get_caveat_id(root):
72 """
73 Returns the caveat_id generated by the SSO
74 """
75 location = urlparse(LOGIN_URL).hostname
76 (caveat,) = [
77 c
78 for c in Macaroon.deserialize(root).third_party_caveats()
79 if c.location == location
80 ]
82 return caveat.caveat_id
85def request_macaroon():
86 """
87 Request a macaroon from dashboard.
88 Returns the macaroon.
89 """
90 response = sso.post_macaroon({"permissions": PERMISSIONS})
92 return response["macaroon"]
95def get_refreshed_discharge(discharge):
96 """
97 Get a refresh macaroon if the macaroon is not valid anymore.
98 Returns the new discharge macaroon.
99 """
100 response = sso.get_refreshed_discharge({"discharge_macaroon": discharge})
102 return response["discharge_macaroon"]
105def is_macaroon_expired(headers):
106 """
107 Returns True if the macaroon needs to be refreshed from
108 the header response.
109 """
110 return headers.get("WWW-Authenticate") == ("Macaroon needs_refresh=1")