Skip to content

Commit d887af0

Browse files
author
Joshua Anderson
committed
feat(controller): allow admins to change a user's password.
1 parent bb9e90a commit d887af0

10 files changed

Lines changed: 1485 additions & 22 deletions

File tree

client-go/cmd/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func Logout() error {
104104
func Passwd(username string, password string, newPassword string) error {
105105
var err error
106106

107-
if password == "" {
107+
if password == "" && username == "" {
108108
fmt.Print("current password: ")
109109
password, err = readPassword()
110110
fmt.Println()

client-go/controller/api/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type AuthLoginResponse tokenResponse
2121
// AuthPasswdRequest is the definition of POST /v1/auth/passwd/.
2222
type AuthPasswdRequest struct {
2323
Username string `json:"username,omitempty"`
24-
Password string `json:"password"`
24+
Password string `json:"password,omitempty"`
2525
NewPassword string `json:"new_password"`
2626
}
2727

client/deis.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
__version__ = '1.9.0-dev'
8484

8585
# what version of the API is this client compatible with?
86-
__api_version__ = '1.5'
86+
__api_version__ = '1.6'
8787

8888

8989
locale.setlocale(locale.LC_ALL, '')
@@ -928,7 +928,7 @@ def auth_passwd(self, args):
928928
raise EnvironmentError(
929929
'Could not find token. Use `deis login` or `deis register` to get started.')
930930
password = args.get('--password')
931-
if not password:
931+
if not password and not args.get('--username'):
932932
password = getpass('current password: ')
933933
new_password = args.get('--new-password')
934934
if not new_password:
@@ -938,10 +938,13 @@ def auth_passwd(self, args):
938938
self._logger.error('Password mismatch, not changing.')
939939
sys.exit(1)
940940
payload = {
941-
'password': password,
942941
'new_password': new_password,
943942
'username': args.get('--username', self._settings['username']),
944943
}
944+
945+
if password:
946+
payload['password'] = password
947+
945948
response = self._dispatch('post', "/v1/auth/passwd", json.dumps(payload))
946949
if response.status_code == requests.codes.ok:
947950
self._logger.info('Password change succeeded.')

controller/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.5.0'
5+
__version__ = '1.6.0'

controller/api/tests/test_auth.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,11 @@ def test_change_user_passwd(self):
251251
new_password = 'password'
252252
submit = {
253253
'username': self.user1.username,
254-
'password': old_password,
255254
'new_password': new_password,
256255
}
257256
response = self.client.post(url, json.dumps(submit), content_type='application/json',
258257
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
259-
self.assertEqual(response.status_code, 400)
258+
self.assertEqual(response.status_code, 200)
260259
# test login with old password
261260
url = '/v1/auth/login/'
262261
payload = urllib.urlencode({'username': self.user1.username, 'password': old_password})
@@ -268,16 +267,22 @@ def test_change_user_passwd(self):
268267
response = self.client.post(url, data=payload,
269268
content_type='application/x-www-form-urlencoded')
270269
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']
270+
# Non-admins can't change another user's password
271+
submit['password'], submit['new_password'] = submit['new_password'], old_password
273272
url = '/v1/auth/passwd'
274273
response = self.client.post(url, json.dumps(submit), content_type='application/json',
275274
HTTP_AUTHORIZATION='token {}'.format(self.user2_token))
276275
self.assertEqual(response.status_code, 403)
277-
# however, targeting yourself should be fine.
276+
# change back password with a regular user
278277
response = self.client.post(url, json.dumps(submit), content_type='application/json',
279278
HTTP_AUTHORIZATION='token {}'.format(self.user1_token))
280279
self.assertEqual(response.status_code, 200)
280+
# test login with new password
281+
url = '/v1/auth/login/'
282+
payload = urllib.urlencode({'username': self.user1.username, 'password': old_password})
283+
response = self.client.post(url, data=payload,
284+
content_type='application/x-www-form-urlencoded')
285+
self.assertEqual(response.status_code, 200)
281286

282287
def test_regenerate(self):
283288
""" Test that token regeneration works"""

controller/api/views.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,20 @@ def get_object(self):
3737
return self.get_queryset()[0]
3838

3939
def passwd(self, request, **kwargs):
40-
obj = self.get_object()
40+
caller_obj = self.get_object()
41+
target_obj = self.get_object()
4142
if request.data.get('username'):
4243
# if you "accidentally" target yourself, that should be fine
43-
if obj.username == request.data['username'] or obj.is_superuser:
44-
obj = get_object_or_404(User, username=request.data['username'])
44+
if caller_obj.username == request.data['username'] or caller_obj.is_superuser:
45+
target_obj = get_object_or_404(User, username=request.data['username'])
4546
else:
4647
raise PermissionDenied()
47-
if not obj.check_password(request.data['password']):
48-
return Response({'detail': 'Current password does not match'},
49-
status=status.HTTP_400_BAD_REQUEST)
50-
obj.set_password(request.data['new_password'])
51-
obj.save()
48+
if request.data.get('password') or not caller_obj.is_superuser:
49+
if not target_obj.check_password(request.data['password']):
50+
return Response({'detail': 'Current password does not match'},
51+
status=status.HTTP_400_BAD_REQUEST)
52+
target_obj.set_password(request.data['new_password'])
53+
target_obj.save()
5254
return Response({'status': 'password set'})
5355

5456

docs/reference/api-v1.5.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
:title: Controller API v1.5
22
:description: The v1.5 REST API for Deis' Controller
33

4-
.. _controller_api_v1:
5-
64
Controller API v1.5
75
===================
86

0 commit comments

Comments
 (0)