Skip to content

Commit fe80c75

Browse files
authored
Set is_canonical flag based on account email on test environments (#5087)
1 parent bb6675a commit fe80c75

2 files changed

Lines changed: 132 additions & 2 deletions

File tree

tests/login/tests_login_handler.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from pymacaroons import Macaroon
66
from webapp.app import create_app
77

8+
from unittest.mock import patch, MagicMock
9+
810

911
class LoginHandlerTest(TestCase):
1012
def setUp(self):
@@ -96,3 +98,114 @@ def test_login_connection_error(self):
9698
response = self.client.get(self.endpoint_url)
9799

98100
assert response.status_code == 502
101+
102+
103+
class AfterLoginHandlerTest(TestCase):
104+
def create_app(self):
105+
app = create_app(testing=True)
106+
107+
# set up a fake route for testing the after_login function
108+
# since it is decorated with @open_id.after_login
109+
@app.route("/_test_after_login")
110+
def _test_after_login():
111+
from webapp.login.views import after_login
112+
113+
return after_login(self.mock_resp)
114+
115+
return app
116+
117+
# creates a mocked responses for the get_account function
118+
# and the login response passed to after_login
119+
def prepare_mock_response(
120+
self, mock_get_account, email="test@test.com", groups=[]
121+
):
122+
self.mock_resp = MagicMock()
123+
self.mock_resp.nickname = "test"
124+
self.mock_resp.identity_url = "https://login.ubuntu.com/test"
125+
self.mock_resp.fullname = "Test"
126+
self.mock_resp.image = "test.png"
127+
self.mock_resp.email = email
128+
self.mock_resp.extensions = {
129+
"macaroon": MagicMock(discharge="some-discharge"),
130+
"lp": MagicMock(is_member=groups),
131+
}
132+
133+
mock_get_account.return_value = {
134+
"username": self.mock_resp.nickname,
135+
"displayname": self.mock_resp.fullname,
136+
"email": email,
137+
"stores": [],
138+
}
139+
140+
@patch("webapp.login.views.ENVIRONMENT", "staging")
141+
@patch("webapp.login.views.dashboard.get_stores", return_value=[])
142+
@patch("webapp.login.views.logic.get_stores", return_value=[])
143+
@patch(
144+
"webapp.login.views.dashboard.get_validation_sets", return_value=None
145+
)
146+
@patch("webapp.login.views.dashboard.get_account")
147+
def test_is_canonical_true_if_email_ends_with_canonical_on_staging(
148+
self,
149+
mock_get_account,
150+
*_,
151+
):
152+
# on test environments, we treat publisher's account as "canonical"
153+
# if their email is (at)canonical email
154+
self.prepare_mock_response(
155+
mock_get_account, email="test@canonical.com", groups=[]
156+
)
157+
158+
self.client.get("/_test_after_login")
159+
160+
with self.client.session_transaction() as s:
161+
publisher = s.get("publisher")
162+
assert publisher is not None
163+
assert publisher["is_canonical"] is True
164+
165+
@patch("webapp.login.views.ENVIRONMENT", "production")
166+
@patch("webapp.login.views.dashboard.get_stores", return_value=[])
167+
@patch("webapp.login.views.logic.get_stores", return_value=[])
168+
@patch(
169+
"webapp.login.views.dashboard.get_validation_sets", return_value=None
170+
)
171+
@patch("webapp.login.views.dashboard.get_account")
172+
def test_is_canonical_true_if_member_of_team_on_production(
173+
self,
174+
mock_get_account,
175+
*_,
176+
):
177+
# on production, we treat publisher's account as "canonical"
178+
# if they are a member of the canonical team
179+
self.prepare_mock_response(mock_get_account, groups=["canonical"])
180+
181+
self.client.get("/_test_after_login")
182+
183+
with self.client.session_transaction() as s:
184+
publisher = s.get("publisher")
185+
assert publisher is not None
186+
assert publisher["is_canonical"] is True
187+
188+
@patch("webapp.login.views.ENVIRONMENT", "production")
189+
@patch("webapp.login.views.dashboard.get_stores", return_value=[])
190+
@patch("webapp.login.views.logic.get_stores", return_value=[])
191+
@patch(
192+
"webapp.login.views.dashboard.get_validation_sets", return_value=None
193+
)
194+
@patch("webapp.login.views.dashboard.get_account")
195+
def test_is_canonical_false_if_not_member_of_team_on_production(
196+
self,
197+
mock_get_account,
198+
*_,
199+
):
200+
# on production, we treat publisher's account as "canonical"
201+
# only if they are a member of the canonical team, not based on email
202+
self.prepare_mock_response(
203+
mock_get_account, email="test@canonical.com", groups=[]
204+
)
205+
206+
self.client.get("/_test_after_login")
207+
208+
with self.client.session_transaction() as s:
209+
publisher = s.get("publisher")
210+
assert publisher is not None
211+
assert publisher["is_canonical"] is False

webapp/login/views.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
)
2121

2222
LOGIN_URL = os.getenv("LOGIN_URL", "https://login.ubuntu.com")
23+
ENVIRONMENT = os.getenv("ENVIRONMENT", "devel")
24+
25+
26+
# getter for ENVIRONMENT variable
27+
# this allows the value to be mocked in tests
28+
def get_environment():
29+
return ENVIRONMENT
30+
2331

2432
LP_CANONICAL_TEAM = "canonical"
2533

@@ -75,14 +83,23 @@ def after_login(resp):
7583
validation_sets = dashboard.get_validation_sets(flask.session)
7684

7785
if account:
86+
is_canonical = LP_CANONICAL_TEAM in resp.extensions["lp"].is_member
87+
88+
# in environments other than production, for testing purposes,
89+
# we detect if the user is Canonical by checking
90+
# if the email ends with @canonical.com
91+
if (not is_canonical) and get_environment() != "production":
92+
is_canonical = account["email"] and account["email"].endswith(
93+
"@canonical.com"
94+
)
95+
7896
flask.session["publisher"] = {
7997
"identity_url": resp.identity_url,
8098
"nickname": account["username"],
8199
"fullname": account["displayname"],
82100
"image": resp.image,
83101
"email": account["email"],
84-
"is_canonical": LP_CANONICAL_TEAM
85-
in resp.extensions["lp"].is_member,
102+
"is_canonical": is_canonical,
86103
}
87104

88105
if logic.get_stores(

0 commit comments

Comments
 (0)