Skip to content

Commit 06211a2

Browse files
author
Matthew Fisher
authored
fix(api): transfer all downstream resources along with app (#1146)
This gives the target user complete ownership over the releases, configuration and other resources required when transferring an app's ownership.
1 parent ffeb14a commit 06211a2

4 files changed

Lines changed: 48 additions & 4 deletions

File tree

rootfs/api/fixtures/tests.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,23 @@
5252
"email": "autotest3@deis.io",
5353
"date_joined": "2013-05-10T16:08:09.357Z"
5454
}
55+
},
56+
{
57+
"pk": 10,
58+
"model": "auth.user",
59+
"fields": {
60+
"username": "autotest4",
61+
"first_name": "Otto",
62+
"last_name": "Test",
63+
"is_active": true,
64+
"is_superuser": false,
65+
"is_staff": false,
66+
"last_login": "2013-05-10T16:08:09.357Z",
67+
"groups": [],
68+
"user_permissions": [],
69+
"password": "pbkdf2_sha256$10000$5Uoq7dl61vnN$gQhDpc2q2Rkn16VdPC+pNNEQcKpy+LGe29Zkad+2/m4=",
70+
"email": "autotest4@deis.io",
71+
"date_joined": "2013-05-10T16:08:09.357Z"
72+
}
5573
}
5674
]

rootfs/api/tests/test_app.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,19 @@ def test_app_transfer(self, mock_requests):
330330
owner_token = Token.objects.get(user=owner).key
331331
self.client.credentials(HTTP_AUTHORIZATION='Token ' + owner_token)
332332

333-
app_id = self.create_app()
333+
collaborator = User.objects.get(username='autotest3')
334+
335+
app = App.objects.create(owner=owner)
336+
337+
# pretend the owner and a collaborator added some config to the app to ensure
338+
# resources owned by the owner are transferred, but not resources owned by the
339+
# collaborator.
340+
config1 = Config.objects.create(owner=owner, app=app, values={'FOO': 'bar'})
341+
config2 = Config.objects.create(owner=collaborator, app=app, values={'CAR': 'star'})
334342

335343
# Transfer App
336-
url = '/v2/apps/{}'.format(app_id)
337-
new_owner = User.objects.get(username='autotest3')
344+
url = '/v2/apps/{}'.format(app.id)
345+
new_owner = User.objects.get(username='autotest4')
338346
new_owner_token = Token.objects.get(user=new_owner).key
339347
body = {'owner': new_owner.username}
340348
response = self.client.post(url, body)
@@ -350,6 +358,18 @@ def test_app_transfer(self, mock_requests):
350358
self.assertEqual(response.status_code, 200, response.data)
351359
self.assertEqual(response.data['owner'], new_owner.username)
352360

361+
# At this point config1.owner field is still the old owner, but the value in the database
362+
# was updated to the new owner when we performed the transfer. The object's updated values
363+
# needs to be reloaded from the database to get an accurate idea who owns the object.
364+
# https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.refresh_from_db
365+
config1.refresh_from_db()
366+
config2.refresh_from_db()
367+
368+
# New owner also is given ownership to all resources owned by the original user, but not
369+
# resources created by other users
370+
self.assertEqual(config1.owner, new_owner)
371+
self.assertEqual(config2.owner, collaborator)
372+
353373
# Collaborators can't transfer
354374
body = {'username': owner.username}
355375
perms_url = url+"/perms/"

rootfs/api/tests/test_users.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def test_super_user_can_list(self):
1616
response = self.client.get(url,
1717
HTTP_AUTHORIZATION='token {}'.format(token))
1818
self.assertEqual(response.status_code, 200, response.data)
19-
self.assertEqual(len(response.data['results']), 3)
19+
self.assertEqual(len(response.data['results']), 4)
2020

2121
def test_non_super_user_cannot_list(self):
2222
user = User.objects.get(username='autotest2')

rootfs/api/views.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,12 +243,18 @@ def run(self, request, **kwargs):
243243

244244
def update(self, request, **kwargs):
245245
app = self.get_object()
246+
old_owner = app.owner
246247

247248
if request.data.get('owner'):
248249
if self.request.user != app.owner and not self.request.user.is_superuser:
249250
raise PermissionDenied()
250251
new_owner = get_object_or_404(User, username=request.data['owner'])
251252
app.owner = new_owner
253+
# ensure all downstream objects that are owned by this user and are part of this app
254+
# is also updated
255+
for downstream_model in [models.AppSettings, models.Build, models.Config,
256+
models.Domain, models.Release, models.TLS]:
257+
downstream_model.objects.filter(owner=old_owner, app=app).update(owner=new_owner)
252258
app.save()
253259
return Response(status=status.HTTP_200_OK)
254260

0 commit comments

Comments
 (0)