Skip to content

Commit c59d8bb

Browse files
ewdurbindi
andauthored
(admin): Account recovery tooling (#16266)
* WIP * surface all available urls * add user observations * check links using JS via camo * update observation structure * handle projects with no releases * display (jankily) active recoveries on user detail * display account recoveries on admin page * add cancel/complete functionality for account recoveries * lint * support (but don't implement) alternate emails for verification * patch up easy to fix tests * allow overriding to email address for verification * overridable from! * fixup test coverage for account service and emails * test coverage for account_recovery_{cancel,complete} * coverage for GET of user_recover_account_initiate * flesh out tests for account_recovery.initiate * fix email templates * admin: splitting email form from userform * add an "is_support" role * remove TODO * admin js: optimize query selector for link checker * mikes visual linting * test_add_email_bypass_ratelimit: update test * indents * re-organize admin user-detail * fix closing tag on account initiate recovery * mildly repair admin ui for user detail * be good at js, per mikes visual linter --------- Co-authored-by: Dustin Ingram <[email protected]>
1 parent 2876c73 commit c59d8bb

File tree

27 files changed

+2128
-143
lines changed

27 files changed

+2128
-143
lines changed

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ services:
6464

6565
camo:
6666
image: pypa/warehouse-camo:2.0.0
67-
command: /bin/go-camo --listen=0.0.0.0:9000
67+
command: "/bin/go-camo --listen=0.0.0.0:9000 --header 'Access-Control-Allow-Origin: http://localhost'"
6868
ports:
6969
- "9000:9000"
7070
environment:

tests/unit/accounts/test_models.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,18 @@ def test_acl(self, db_session):
171171
(
172172
Permissions.AdminUsersRead,
173173
Permissions.AdminUsersWrite,
174+
Permissions.AdminUsersEmailWrite,
175+
Permissions.AdminUsersAccountRecoveryWrite,
176+
Permissions.AdminDashboardSidebarRead,
177+
),
178+
),
179+
(
180+
"Allow",
181+
"group:support",
182+
(
183+
Permissions.AdminUsersRead,
184+
Permissions.AdminUsersEmailWrite,
185+
Permissions.AdminUsersAccountRecoveryWrite,
174186
Permissions.AdminDashboardSidebarRead,
175187
),
176188
),
@@ -184,16 +196,18 @@ def test_acl(self, db_session):
184196
@pytest.mark.parametrize(
185197
(
186198
"is_superuser",
199+
"is_support",
187200
"is_moderator",
188201
"is_psf_staff",
189202
"expected",
190203
),
191204
[
192-
(False, False, False, []),
205+
(False, False, False, False, []),
193206
(
194207
True,
195208
False,
196209
False,
210+
False,
197211
[
198212
"group:admins",
199213
"group:moderators",
@@ -202,13 +216,25 @@ def test_acl(self, db_session):
202216
],
203217
),
204218
(
219+
False,
220+
True,
221+
False,
222+
False,
223+
[
224+
"group:support",
225+
"group:moderators",
226+
],
227+
),
228+
(
229+
False,
205230
False,
206231
True,
207232
False,
208233
["group:moderators"],
209234
),
210235
(
211236
True,
237+
False,
212238
True,
213239
False,
214240
[
@@ -219,12 +245,14 @@ def test_acl(self, db_session):
219245
],
220246
),
221247
(
248+
False,
222249
False,
223250
False,
224251
True,
225252
["group:psf_staff"],
226253
),
227254
(
255+
False,
228256
False,
229257
True,
230258
True,
@@ -235,13 +263,15 @@ def test_acl(self, db_session):
235263
def test_principals(
236264
self,
237265
is_superuser,
266+
is_support,
238267
is_moderator,
239268
is_psf_staff,
240269
expected,
241270
):
242271
user = User(
243272
id=uuid.uuid4(),
244273
is_superuser=is_superuser,
274+
is_support=is_support,
245275
is_moderator=is_moderator,
246276
is_psf_staff=is_psf_staff,
247277
)

tests/unit/accounts/test_services.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,24 @@ def test_add_email_rate_limited(self, user_service, metrics, remote_addr):
379379
)
380380
]
381381

382+
def test_add_email_bypass_ratelimit(self, user_service, metrics, remote_addr):
383+
resets = pretend.stub()
384+
limiter = pretend.stub(
385+
hit=pretend.call_recorder(lambda ip: None),
386+
test=pretend.call_recorder(lambda ip: False),
387+
resets_in=pretend.call_recorder(lambda ip: resets),
388+
)
389+
user_service.ratelimiters["email.add"] = limiter
390+
391+
user = UserFactory.create()
392+
new_email = user_service.add_email(user.id, "[email protected]", ratelimit=False)
393+
394+
assert new_email.email == "[email protected]"
395+
assert not new_email.verified
396+
assert limiter.test.calls == []
397+
assert limiter.resets_in.calls == []
398+
assert metrics.increment.calls == []
399+
382400
def test_update_user(self, user_service):
383401
user = UserFactory.create()
384402
new_name, password = "new username", "TestPa@@w0rd"

tests/unit/admin/test_routes.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ def test_includeme():
6262
factory="warehouse.accounts.models:UserFactory",
6363
traverse="/{username}",
6464
),
65+
pretend.call(
66+
"admin.user.submit_email",
67+
"/admin/users/{username}/emails/",
68+
domain=warehouse,
69+
factory="warehouse.accounts.models:UserFactory",
70+
traverse="/{username}",
71+
),
6572
pretend.call(
6673
"admin.user.add_email",
6774
"/admin/users/{username}/add_email/",
@@ -91,8 +98,22 @@ def test_includeme():
9198
traverse="/{username}",
9299
),
93100
pretend.call(
94-
"admin.user.wipe_factors",
95-
"/admin/users/{username}/wipe_factors/",
101+
"admin.user.account_recovery.initiate",
102+
"/admin/users/{username}/account_recovery/initiate/",
103+
domain=warehouse,
104+
factory="warehouse.accounts.models:UserFactory",
105+
traverse="/{username}",
106+
),
107+
pretend.call(
108+
"admin.user.account_recovery.cancel",
109+
"/admin/users/{username}/account_recovery/cancel/",
110+
domain=warehouse,
111+
factory="warehouse.accounts.models:UserFactory",
112+
traverse="/{username}",
113+
),
114+
pretend.call(
115+
"admin.user.account_recovery.complete",
116+
"/admin/users/{username}/account_recovery/complete/",
96117
domain=warehouse,
97118
factory="warehouse.accounts.models:UserFactory",
98119
traverse="/{username}",

0 commit comments

Comments
 (0)