Skip to content

Commit d51816e

Browse files
author
Matthew Fisher
committed
Merge pull request #3745 from bacongobbler/3292-auth-passwd-add-user-argument
feat(client): add --username flag to auth:passwd
2 parents 917973d + 317ee5d commit d51816e

5 files changed

Lines changed: 134 additions & 8 deletions

File tree

client/deis.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -895,9 +895,11 @@ def auth_passwd(self, args):
895895
896896
Options:
897897
--password=<password>
898-
provide the current password for the account.
898+
the current password for the account.
899899
--new-password=<new-password>
900-
provide a new password for the account.
900+
the new password for the account.
901+
--username=<username>
902+
the account's username.
901903
"""
902904
if not self._settings.get('token'):
903905
raise EnvironmentError(
@@ -912,7 +914,11 @@ def auth_passwd(self, args):
912914
if new_password != confirm:
913915
self._logger.error('Password mismatch, not changing.')
914916
sys.exit(1)
915-
payload = {'password': password, 'new_password': new_password}
917+
payload = {
918+
'password': password,
919+
'new_password': new_password,
920+
'username': args.get('--username', self._settings['username']),
921+
}
916922
response = self._dispatch('post', "/v1/auth/passwd", json.dumps(payload))
917923
if response.status_code == requests.codes.ok:
918924
self._logger.info('Password change succeeded.')

controller/api/fixtures/test_auth.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,41 @@
1616
"email": "autotest@deis.io",
1717
"date_joined": "2013-05-10T16:08:09.357Z"
1818
}
19+
},
20+
{
21+
"pk": 8,
22+
"model": "auth.user",
23+
"fields": {
24+
"username": "autotest2",
25+
"first_name": "Otto",
26+
"last_name": "Test",
27+
"is_active": true,
28+
"is_superuser": false,
29+
"is_staff": false,
30+
"last_login": "2013-05-10T16:08:09.357Z",
31+
"groups": [],
32+
"user_permissions": [],
33+
"password": "pbkdf2_sha256$10000$5Uoq7dl61vnN$gQhDpc2q2Rkn16VdPC+pNNEQcKpy+LGe29Zkad+2/m4=",
34+
"email": "autotest@deis.io",
35+
"date_joined": "2013-05-10T16:08:09.357Z"
36+
}
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": "autotest@deis.io",
53+
"date_joined": "2013-05-10T16:08:09.357Z"
54+
}
1955
}
2056
]

controller/api/tests/test_auth.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ class AuthTest(TestCase):
2121

2222
"""Tests user registration, authentication and authorization"""
2323

24+
def setUp(self):
25+
self.admin = User.objects.get(username='autotest')
26+
self.admin_token = Token.objects.get(user=self.admin).key
27+
self.user1 = User.objects.get(username='autotest2')
28+
self.user1_token = Token.objects.get(user=self.user1).key
29+
self.user2 = User.objects.get(username='autotest3')
30+
self.user2_token = Token.objects.get(user=self.user2).key
31+
2432
def test_auth(self):
2533
"""
2634
Test that a user can register using the API, login and logout
@@ -98,10 +106,6 @@ def test_auth_registration_admin_only_fails_if_not_admin(self):
98106
@override_settings(REGISTRATION_MODE="admin_only")
99107
def test_auth_registration_admin_only_works(self):
100108
"""test that a superuser can register when registration is admin only."""
101-
102-
user = User.objects.get(username='autotest')
103-
token = Token.objects.get(user=user)
104-
105109
url = '/v1/auth/register'
106110

107111
username, password = 'newuser_by_admin', 'password'
@@ -119,7 +123,7 @@ def test_auth_registration_admin_only_works(self):
119123
'is_staff': True,
120124
}
121125
response = self.client.post(url, json.dumps(submit), content_type='application/json',
122-
HTTP_AUTHORIZATION='token {}'.format(token))
126+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
123127

124128
self.assertEqual(response.status_code, 201)
125129
for key in response.data:
@@ -236,3 +240,41 @@ def test_passwd(self):
236240
response = self.client.post(url, data=payload,
237241
content_type='application/x-www-form-urlencoded')
238242
self.assertEqual(response.status_code, 200)
243+
244+
def test_change_user_passwd(self):
245+
"""
246+
Test that an administrator can change a user's password, while a regular user cannot.
247+
"""
248+
# change password
249+
url = '/v1/auth/passwd'
250+
old_password = self.user1.password
251+
new_password = 'password'
252+
submit = {
253+
'username': self.user1.username,
254+
'password': old_password,
255+
'new_password': new_password,
256+
}
257+
response = self.client.post(url, json.dumps(submit), content_type='application/json',
258+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
259+
self.assertEqual(response.status_code, 400)
260+
# test login with old password
261+
url = '/v1/auth/login/'
262+
payload = urllib.urlencode({'username': self.user1.username, 'password': old_password})
263+
response = self.client.post(url, data=payload,
264+
content_type='application/x-www-form-urlencoded')
265+
self.assertEqual(response.status_code, 400)
266+
# test login with new password
267+
payload = urllib.urlencode({'username': self.user1.username, 'password': new_password})
268+
response = self.client.post(url, data=payload,
269+
content_type='application/x-www-form-urlencoded')
270+
self.assertEqual(response.status_code, 200)
271+
# try to change back password with a regular user
272+
submit['password'], submit['new_password'] = submit['new_password'], submit['password']
273+
url = '/v1/auth/passwd'
274+
response = self.client.post(url, json.dumps(submit), content_type='application/json',
275+
HTTP_AUTHORIZATION='token {}'.format(self.user2_token))
276+
self.assertEqual(response.status_code, 403)
277+
# however, targeting yourself should be fine.
278+
response = self.client.post(url, json.dumps(submit), content_type='application/json',
279+
HTTP_AUTHORIZATION='token {}'.format(self.user1_token))
280+
self.assertEqual(response.status_code, 200)

controller/api/views.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ def get_object(self):
3737

3838
def passwd(self, request, **kwargs):
3939
obj = self.get_object()
40+
if request.data.get('username'):
41+
# if you "accidentally" target yourself, that should be fine
42+
if obj.username == request.data['username'] or obj.is_superuser:
43+
obj = get_object_or_404(User, username=request.data['username'])
44+
else:
45+
raise PermissionDenied()
4046
if not obj.check_password(request.data['password']):
4147
return Response({'detail': 'Current password does not match'},
4248
status=status.HTTP_400_BAD_REQUEST)

docs/reference/api-v1.4.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This is the v1.4 REST API for the :ref:`Controller`.
1212
What's New
1313
----------
1414

15+
**New!** optional ``username`` argument for /v1/auth/passwd/
1516
**New!** Deprecate X prefixed headers
1617
``X_DEIS_API_VERSION`` -> ``DEIS_API_VERSION``
1718
``X_DEIS_PLATFORM_VERSION`` -> ``DEIS_PLATFORM_VERSION``
@@ -118,6 +119,41 @@ Example Response:
118119
DEIS_PLATFORM_VERSION: 1.6.1
119120
120121
122+
Change Password
123+
```````````````
124+
125+
Example Request:
126+
127+
.. code-block:: console
128+
129+
POST /v1/auth/passwd/ HTTP/1.1
130+
Host: deis.example.com
131+
Authorization: token abc123
132+
133+
{
134+
"password": "foo",
135+
"new_password": "bar"
136+
}
137+
138+
Optional parameters:
139+
140+
.. code-block:: console
141+
142+
{"username": "testuser"}
143+
144+
.. note::
145+
146+
Using the ``username`` parameter requires administrative privileges
147+
148+
Example Response:
149+
150+
.. code-block:: console
151+
152+
HTTP/1.1 200 OK
153+
DEIS_API_VERSION: 1.4
154+
DEIS_PLATFORM_VERSION: 1.6.1
155+
156+
121157
Applications
122158
------------
123159

0 commit comments

Comments
 (0)