Skip to content

Commit 9d6c86e

Browse files
committed
Fix issue with delegation of patch via REST API
There have been reports of people being unable to delegate patches to themselves, despite being a maintainer or the project to which the patch is associated. The issue is a result of how we do a check for whether the user is a maintainer of the patch's project [1]. This check is checking if a given 'User.id' is in the list of items referenced by 'Project.maintainer_project'. However, 'Project.maintainer_project' is a backref to 'UserProfile.maintainer_projects'. This means we're comparing 'User.id' and 'UserProfile.id'. Boo. This wasn't seen in testing since we've had a post-save callback [2] for some time that ensures we always create a 'UserProfile' object whenever we create a 'User' object. This also means we won't have an issue on deployments initially deployed after that post-save callback was added, a 'User' with id=N will always have a corresponding 'UserProfile' with id=N. However, that's not true for older deployments such as the ozlabs.org one. [1] https://github.com/getpatchwork/patchwork/blob/89c924f9bc/patchwork/api/patch.py#L108-L111 [2] https://github.com/getpatchwork/patchwork/blob/89c924f9bc/patchwork/models.py#L204-L210 Signed-off-by: Stephen Finucane <[email protected]> Closes: #313 Reported-by: Bjorn Helgaas <[email protected]> (cherry picked from commit ab35df8)
1 parent a938f95 commit 9d6c86e

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

patchwork/api/patch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ def validate_delegate(self, value):
119119
if not value:
120120
return value
121121

122-
if not self.instance.project.maintainer_project.filter(
123-
id=value.id).exists():
122+
if not value.profile.maintainer_projects.only('id').filter(
123+
id=self.instance.project.id).exists():
124124
raise ValidationError("User '%s' is not a maintainer for project "
125125
"'%s'" % (value, self.instance.project))
126126
return value

patchwork/tests/api/test_patch.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,30 @@ def test_update_invalid_state(self):
286286
self.assertContains(resp, 'Expected one of: %s.' % state.name,
287287
status_code=status.HTTP_400_BAD_REQUEST)
288288

289+
def test_update_legacy_delegate(self):
290+
"""Regression test for bug #313."""
291+
project = create_project()
292+
state = create_state()
293+
patch = create_patch(project=project, state=state)
294+
user_a = create_maintainer(project)
295+
296+
# create a user (User), then delete the associated UserProfile and save
297+
# the user to ensure a new profile is generated
298+
user_b = create_user()
299+
self.assertEqual(user_b.id, user_b.profile.id)
300+
user_b.profile.delete()
301+
user_b.save()
302+
user_b.profile.maintainer_projects.add(project)
303+
user_b.profile.save()
304+
self.assertNotEqual(user_b.id, user_b.profile.id)
305+
306+
self.client.force_authenticate(user=user_a)
307+
resp = self.client.patch(self.api_url(patch.id),
308+
{'delegate': user_b.id})
309+
self.assertEqual(status.HTTP_200_OK, resp.status_code, resp)
310+
self.assertEqual(Patch.objects.get(id=patch.id).state, state)
311+
self.assertEqual(Patch.objects.get(id=patch.id).delegate, user_b)
312+
289313
def test_update_invalid_delegate(self):
290314
"""Update patch with invalid fields.
291315

0 commit comments

Comments
 (0)