Skip to content

Commit f1845c3

Browse files
committed
Add JournalEntries when adding/removing roles
1 parent 6c54f53 commit f1845c3

File tree

2 files changed

+123
-10
lines changed

2 files changed

+123
-10
lines changed

tests/unit/manage/test_views.py

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from warehouse.manage import views
2121
from warehouse.accounts.interfaces import IUserService
22-
from warehouse.packaging.models import Role
22+
from warehouse.packaging.models import JournalEntry, Role
2323

2424
from ...common.db.packaging import ProjectFactory, RoleFactory, UserFactory
2525

@@ -125,6 +125,8 @@ def test_post_new_role(self, db_request):
125125
)
126126
db_request.method = "POST"
127127
db_request.POST = pretend.stub()
128+
db_request.remote_addr = "10.10.10.10"
129+
db_request.user = UserFactory.create()
128130
form_obj = pretend.stub(
129131
validate=pretend.call_recorder(lambda: True),
130132
username=pretend.stub(data=user.username),
@@ -160,6 +162,13 @@ def test_post_new_role(self, db_request):
160162
"form": form_obj,
161163
}
162164

165+
entry = db_request.db.query(JournalEntry).one()
166+
167+
assert entry.name == project.name
168+
assert entry.action == "add Owner testuser"
169+
assert entry.submitted_by == db_request.user
170+
assert entry.submitted_from == db_request.remote_addr
171+
163172
def test_post_duplicate_role(self, db_request):
164173
project = ProjectFactory.create(name="foobar")
165174
user = UserFactory.create(username="testuser")
@@ -226,7 +235,8 @@ def test_change_role(self, db_request):
226235
new_role_name = "Maintainer"
227236

228237
db_request.method = "POST"
229-
db_request.user = pretend.stub()
238+
db_request.user = UserFactory.create()
239+
db_request.remote_addr = "10.10.10.10"
230240
db_request.POST = MultiDict({
231241
"role_id": role.id,
232242
"role_name": new_role_name,
@@ -250,6 +260,13 @@ def test_change_role(self, db_request):
250260
assert isinstance(result, HTTPSeeOther)
251261
assert result.headers["Location"] == "/the-redirect"
252262

263+
entry = db_request.db.query(JournalEntry).one()
264+
265+
assert entry.name == project.name
266+
assert entry.action == "change Owner testuser to Maintainer"
267+
assert entry.submitted_by == db_request.user
268+
assert entry.submitted_from == db_request.remote_addr
269+
253270
def test_change_role_invalid_role_name(self, pyramid_request):
254271
project = pretend.stub(name="foobar")
255272

@@ -282,7 +299,8 @@ def test_change_role_when_multiple(self, db_request):
282299
new_role_name = "Maintainer"
283300

284301
db_request.method = "POST"
285-
db_request.user = pretend.stub()
302+
db_request.user = UserFactory.create()
303+
db_request.remote_addr = "10.10.10.10"
286304
db_request.POST = MultiDict([
287305
("role_id", owner_role.id),
288306
("role_id", maintainer_role.id),
@@ -307,6 +325,13 @@ def test_change_role_when_multiple(self, db_request):
307325
assert isinstance(result, HTTPSeeOther)
308326
assert result.headers["Location"] == "/the-redirect"
309327

328+
entry = db_request.db.query(JournalEntry).one()
329+
330+
assert entry.name == project.name
331+
assert entry.action == "remove Owner testuser"
332+
assert entry.submitted_by == db_request.user
333+
assert entry.submitted_from == db_request.remote_addr
334+
310335
def test_change_missing_role(self, db_request):
311336
project = ProjectFactory.create(name="foobar")
312337
missing_role_id = str(uuid.uuid4())
@@ -360,6 +385,38 @@ def test_change_own_owner_role(self, db_request):
360385
assert isinstance(result, HTTPSeeOther)
361386
assert result.headers["Location"] == "/the-redirect"
362387

388+
def test_change_own_owner_role_when_multiple(self, db_request):
389+
project = ProjectFactory.create(name="foobar")
390+
user = UserFactory.create(username="testuser")
391+
owner_role = RoleFactory.create(
392+
user=user, project=project, role_name="Owner"
393+
)
394+
maintainer_role = RoleFactory.create(
395+
user=user, project=project, role_name="Maintainer"
396+
)
397+
398+
db_request.method = "POST"
399+
db_request.user = user
400+
db_request.POST = MultiDict([
401+
("role_id", owner_role.id),
402+
("role_id", maintainer_role.id),
403+
("role_name", "Maintainer"),
404+
])
405+
db_request.session = pretend.stub(
406+
flash=pretend.call_recorder(lambda *a, **kw: None),
407+
)
408+
db_request.route_path = pretend.call_recorder(
409+
lambda *a, **kw: "/the-redirect"
410+
)
411+
412+
result = views.change_project_role(project, db_request)
413+
414+
assert db_request.session.flash.calls == [
415+
pretend.call("Cannot remove yourself as Owner", queue="error"),
416+
]
417+
assert isinstance(result, HTTPSeeOther)
418+
assert result.headers["Location"] == "/the-redirect"
419+
363420

364421
class TestDeleteProjectRoles:
365422

@@ -371,7 +428,8 @@ def test_delete_role(self, db_request):
371428
)
372429

373430
db_request.method = "POST"
374-
db_request.user = pretend.stub()
431+
db_request.user = UserFactory.create()
432+
db_request.remote_addr = "10.10.10.10"
375433
db_request.POST = MultiDict({"role_id": role.id})
376434
db_request.session = pretend.stub(
377435
flash=pretend.call_recorder(lambda *a, **kw: None),
@@ -392,6 +450,13 @@ def test_delete_role(self, db_request):
392450
assert isinstance(result, HTTPSeeOther)
393451
assert result.headers["Location"] == "/the-redirect"
394452

453+
entry = db_request.db.query(JournalEntry).one()
454+
455+
assert entry.name == project.name
456+
assert entry.action == "remove Owner testuser"
457+
assert entry.submitted_by == db_request.user
458+
assert entry.submitted_from == db_request.remote_addr
459+
395460
def test_delete_missing_role(self, db_request):
396461
project = ProjectFactory.create(name="foobar")
397462
missing_role_id = str(uuid.uuid4())

warehouse/manage/views.py

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Licensed under the Apache License, Version 2.0 (the "License");
2+
23
# you may not use this file except in compliance with the License.
34
# You may obtain a copy of the License at
45
#
@@ -20,7 +21,7 @@
2021
from warehouse.accounts.interfaces import IUserService
2122
from warehouse.accounts.models import User
2223
from warehouse.manage.forms import CreateRoleForm, ChangeRoleForm
23-
from warehouse.packaging.models import Role
24+
from warehouse.packaging.models import JournalEntry, Role
2425

2526

2627
@view_config(
@@ -86,6 +87,14 @@ def manage_project_roles(project, request, _form_class=CreateRoleForm):
8687
request.db.add(
8788
Role(user=user, project=project, role_name=form.role_name.data)
8889
)
90+
request.db.add(
91+
JournalEntry(
92+
name=project.name,
93+
action=f"add {role_name} {username}",
94+
submitted_by=request.user,
95+
submitted_from=request.remote_addr,
96+
),
97+
)
8998
request.session.flash(
9099
f"Added collaborator '{form.username.data}'",
91100
queue="success"
@@ -131,19 +140,38 @@ def change_project_role(project, request, _form_class=ChangeRoleForm):
131140
# This user has more than one role, so just delete all the ones
132141
# that aren't what we want.
133142
#
134-
# This should be removed when fixing GH-2745.
135-
(
143+
# This branch should be removed when fixing GH-2745.
144+
roles = (
136145
request.db.query(Role)
137146
.filter(
138147
Role.id.in_(role_ids),
139148
Role.project == project,
140149
Role.role_name != form.role_name.data
141150
)
142-
.delete(synchronize_session="fetch")
151+
.all()
143152
)
144-
request.session.flash(
145-
'Successfully changed role', queue="success"
153+
removing_self = any(
154+
role.role_name == "Owner" and role.user == request.user
155+
for role in roles
146156
)
157+
if removing_self:
158+
request.session.flash(
159+
"Cannot remove yourself as Owner", queue="error"
160+
)
161+
else:
162+
for role in roles:
163+
request.db.delete(role)
164+
request.db.add(
165+
JournalEntry(
166+
name=project.name,
167+
action=f"remove {role.role_name} {role.user_name}",
168+
submitted_by=request.user,
169+
submitted_from=request.remote_addr,
170+
),
171+
)
172+
request.session.flash(
173+
'Successfully changed role', queue="success"
174+
)
147175
else:
148176
# This user only has one role, so get it and change the type.
149177
try:
@@ -160,6 +188,18 @@ def change_project_role(project, request, _form_class=ChangeRoleForm):
160188
"Cannot remove yourself as Owner", queue="error"
161189
)
162190
else:
191+
request.db.add(
192+
JournalEntry(
193+
name=project.name,
194+
action="change {} {} to {}".format(
195+
role.role_name,
196+
role.user_name,
197+
form.role_name.data,
198+
),
199+
submitted_by=request.user,
200+
submitted_from=request.remote_addr,
201+
),
202+
)
163203
role.role_name = form.role_name.data
164204
request.session.flash(
165205
'Successfully changed role', queue="success"
@@ -202,6 +242,14 @@ def delete_project_role(project, request):
202242
else:
203243
for role in roles:
204244
request.db.delete(role)
245+
request.db.add(
246+
JournalEntry(
247+
name=project.name,
248+
action=f"remove {role.role_name} {role.user_name}",
249+
submitted_by=request.user,
250+
submitted_from=request.remote_addr,
251+
),
252+
)
205253
request.session.flash("Successfully removed role", queue="success")
206254

207255
return HTTPSeeOther(

0 commit comments

Comments
 (0)