Skip to content

Commit d23b104

Browse files
Joshua AndersonJoshua-Anderson
authored andcommitted
feat(controller): allow users to transfer app ownership.
1 parent f448cab commit d23b104

6 files changed

Lines changed: 78 additions & 5 deletions

File tree

api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
The **api** Django app presents a RESTful web API for interacting with the **deis** system.
33
"""
44

5-
__version__ = '1.6.0'
5+
__version__ = '1.7.0'

api/fixtures/tests.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,23 @@
3434
"email": "autotest2@deis.io",
3535
"date_joined": "2013-05-10T16:08:09.357Z"
3636
}
37+
},
38+
{
39+
"pk": 9,
40+
"model": "auth.user",
41+
"fields": {
42+
"username": "autotest3",
43+
"first_name": "Otto",
44+
"last_name": "Test",
45+
"is_active": true,
46+
"is_superuser": false,
47+
"is_staff": false,
48+
"last_login": "2013-05-10T16:08:09.357Z",
49+
"groups": [],
50+
"user_permissions": [],
51+
"password": "pbkdf2_sha256$10000$5Uoq7dl61vnN$gQhDpc2q2Rkn16VdPC+pNNEQcKpy+LGe29Zkad+2/m4=",
52+
"email": "autotest3@deis.io",
53+
"date_joined": "2013-05-10T16:08:09.357Z"
54+
}
3755
}
3856
]

api/tests/test_app.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,52 @@ def test_app_info_not_showing_wrong_app(self):
324324
response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
325325
self.assertEqual(response.status_code, 404)
326326

327+
def test_app_transfer(self):
328+
owner = User.objects.get(username='autotest2')
329+
owner_token = Token.objects.get(user=owner).key
330+
app_id = 'autotest'
331+
base_url = '/v1/apps'
332+
body = {'id': app_id}
333+
response = self.client.post(base_url, json.dumps(body), content_type='application/json',
334+
HTTP_AUTHORIZATION='token {}'.format(owner_token))
335+
# Transfer App
336+
url = '{}/{}'.format(base_url, app_id)
337+
new_owner = User.objects.get(username='autotest3')
338+
new_owner_token = Token.objects.get(user=new_owner).key
339+
body = {'owner': new_owner.username}
340+
response = self.client.post(url, json.dumps(body), content_type='application/json',
341+
HTTP_AUTHORIZATION='token {}'.format(owner_token))
342+
self.assertEqual(response.status_code, 200)
343+
344+
# Original user can no longer access it
345+
response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(owner_token))
346+
self.assertEqual(response.status_code, 403)
347+
348+
# New owner can access it
349+
response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(new_owner_token))
350+
self.assertEqual(response.status_code, 200)
351+
self.assertEqual(response.data['owner'], new_owner.username)
352+
353+
# Collaborators can't transfer
354+
body = {'username': owner.username}
355+
perms_url = url+"/perms/"
356+
response = self.client.post(perms_url, json.dumps(body), content_type='application/json',
357+
HTTP_AUTHORIZATION='token {}'.format(new_owner_token))
358+
self.assertEqual(response.status_code, 201)
359+
body = {'owner': self.user.username}
360+
response = self.client.post(url, json.dumps(body), content_type='application/json',
361+
HTTP_AUTHORIZATION='token {}'.format(owner_token))
362+
self.assertEqual(response.status_code, 403)
363+
364+
# Admins can transfer
365+
body = {'owner': self.user.username}
366+
response = self.client.post(url, json.dumps(body), content_type='application/json',
367+
HTTP_AUTHORIZATION='token {}'.format(self.token))
368+
self.assertEqual(response.status_code, 200)
369+
response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
370+
self.assertEqual(response.status_code, 200)
371+
self.assertEqual(response.data['owner'], self.user.username)
372+
327373

328374
FAKE_LOG_DATA = """
329375
2013-08-15 12:41:25 [33454] [INFO] Starting gunicorn 17.5

api/tests/test_users.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ def test_super_user_can_list(self):
2121
HTTP_AUTHORIZATION='token {}'.format(token))
2222

2323
self.assertEqual(response.status_code, 200)
24-
self.assertEqual(len(response.data['results']), 2)
25-
self.assertEqual(response.data['results'][0]['username'], 'autotest')
26-
self.assertEqual(response.data['results'][1]['username'], 'autotest2')
24+
self.assertEqual(len(response.data['results']), 3)
2725

2826
def test_non_super_user_cannot_list(self):
2927
url = '/v1/users/'

api/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
views.AppPermsViewSet.as_view({'get': 'list', 'post': 'create'})),
6464
# apps base endpoint
6565
url(r"^apps/(?P<id>{})/?".format(settings.APP_URL_REGEX),
66-
views.AppViewSet.as_view({'get': 'retrieve', 'delete': 'destroy'})),
66+
views.AppViewSet.as_view({'get': 'retrieve', 'post': 'update', 'delete': 'destroy'})),
6767
url(r'^apps/?',
6868
views.AppViewSet.as_view({'get': 'list', 'post': 'create'})),
6969
# key

api/views.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,17 @@ def run(self, request, **kwargs):
224224
return Response(output_and_rc, status=status.HTTP_200_OK,
225225
content_type='text/plain')
226226

227+
def update(self, request, **kwargs):
228+
app = self.get_object()
229+
230+
if request.data.get('owner'):
231+
if self.request.user != app.owner and not self.request.user.is_superuser:
232+
raise PermissionDenied()
233+
new_owner = get_object_or_404(User, username=request.data['owner'])
234+
app.owner = new_owner
235+
app.save()
236+
return Response(status=status.HTTP_200_OK)
237+
227238

228239
class BuildViewSet(ReleasableViewSet):
229240
"""A viewset for interacting with Build objects."""

0 commit comments

Comments
 (0)