From b548d3dcf74c1aaece05498a84cd3465ac7793a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= Date: Mon, 23 May 2016 17:47:07 +0200 Subject: [PATCH] Add support for setting already hashed password You can use this for restoring dumped users list from swauth-list command. Change-Id: Ia77d7a0b91b2f79999286858e383477a80d7db15 --- bin/swauth-add-user | 8 +++++- doc/source/api.rst | 4 ++- swauth/middleware.py | 14 +++++++-- test/unit/test_middleware.py | 55 ++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/bin/swauth-add-user b/bin/swauth-add-user index 28133ac..4b51f7f 100755 --- a/bin/swauth-add-user +++ b/bin/swauth-add-user @@ -43,6 +43,9 @@ if __name__ == '__main__': 'the account already exists, this will have no effect on existing ' 'service URLs. Those will need to be updated with ' 'swauth-set-account-service') + parser.add_option('-e', '--hashed', dest='password_hashed', + action='store_true', default=False, help='Supplied password is ' + 'already hashed and in format :') parser.add_option('-A', '--admin-url', dest='admin_url', default='http://127.0.0.1:8080/auth/', help='The URL to the auth ' 'subsystem (default: http://127.0.0.1:8080/auth/') @@ -91,12 +94,15 @@ if __name__ == '__main__': path = '%sv2/%s/%s' % (parsed_path, account, user) headers = {'X-Auth-Admin-User': options.admin_user, 'X-Auth-Admin-Key': options.admin_key, - 'X-Auth-User-Key': password, 'Content-Length': '0'} if options.admin: headers['X-Auth-User-Admin'] = 'true' if options.reseller_admin: headers['X-Auth-User-Reseller-Admin'] = 'true' + if options.password_hashed: + headers['X-Auth-User-Key-Hash'] = password + else: + headers['X-Auth-User-Key'] = password conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers, ssl=(parsed.scheme == 'https')) resp = conn.getresponse() diff --git a/doc/source/api.rst b/doc/source/api.rst index d5890ba..e5effe4 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -287,7 +287,9 @@ A user can be created with a PUT request against a non-existent user URI. The new user's password must be set using the ``X-Auth-User-Key`` header. The user name MUST NOT start with a period ('.'). This requirement is enforced by the API, and will -result in a 400 error. +result in a 400 error. Alternatively you can use +``X-Auth-User-Key-Hash`` header for providing already hashed +password in format ``:``. Optional Headers: diff --git a/swauth/middleware.py b/swauth/middleware.py index 7955b8e..e04100b 100644 --- a/swauth/middleware.py +++ b/swauth/middleware.py @@ -1038,6 +1038,8 @@ class Swauth(object): account. X-Auth-User-Key represents the user's key (url encoded), + - OR - + X-Auth-User-Key-Hash represents the user's hashed key (url encoded), X-Auth-User-Admin may be set to `true` to create an account .admin, and X-Auth-User-Reseller-Admin may be set to `true` to create a .reseller_admin. @@ -1062,14 +1064,22 @@ class Swauth(object): account = req.path_info_pop() user = req.path_info_pop() key = unquote(req.headers.get('x-auth-user-key', '')) + key_hash = unquote(req.headers.get('x-auth-user-key-hash', '')) admin = req.headers.get('x-auth-user-admin') == 'true' reseller_admin = \ req.headers.get('x-auth-user-reseller-admin') == 'true' if reseller_admin: admin = True if req.path_info or not account or account[0] == '.' or not user or \ - user[0] == '.' or not key: + user[0] == '.' or (not key and not key_hash): return HTTPBadRequest(request=req) + if key_hash: + if ':' not in key_hash: + return HTTPBadRequest(request=req) + auth_type, hash = key_hash.split(':') + if getattr(swauth.authtypes, auth_type.title(), None) is None: + return HTTPBadRequest(request=req) + user_arg = account + ':' + user if reseller_admin: if not self.is_super_admin(req) and\ @@ -1095,7 +1105,7 @@ class Swauth(object): groups.append('.admin') if reseller_admin: groups.append('.reseller_admin') - auth_value = self.auth_encoder().encode(key) + auth_value = key_hash or self.auth_encoder().encode(key) resp = self.make_pre_authed_request( req.environ, 'PUT', path, json.dumps({'auth': auth_value, diff --git a/test/unit/test_middleware.py b/test/unit/test_middleware.py index 60f40a5..af8218c 100644 --- a/test/unit/test_middleware.py +++ b/test/unit/test_middleware.py @@ -18,6 +18,7 @@ import json import mock from time import time import unittest +from urllib import quote from swift.common.swob import Request from swift.common.swob import Response @@ -3025,6 +3026,60 @@ class TestAuth(unittest.TestCase): self.assertEqual(resp.status_int, 500) self.assertEqual(self.test_auth.app.calls, 1) + def test_put_user_key_hash(self): + key_hash = ("sha512:aSm0jEeqIp46T5YLZy1r8+cXs/Xzs1S4VUwVauhBs44=$ef" + "7332ec1288bf69c75682eb8d459d5a84baa7e43f45949c242a9af9" + "7130ef16ac361fe1aa33a789e218122b83c54ef1923fc015080741" + "ca21f6187329f6cb7a") + + self.test_auth.app = FakeApp(iter([ + ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''), + # PUT of user object + ('201 Created', {}, '')])) + resp = Request.blank('/auth/v2/act/usr', + environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Auth-Admin-User': '.super_admin', + 'X-Auth-Admin-Key': 'supertest', + 'X-Auth-User-Key-Hash': quote(key_hash)} + ).get_response(self.test_auth) + self.assertEqual(resp.status_int, 201) + self.assertEqual(self.test_auth.app.calls, 2) + self.assertEqual(json.loads(self.test_auth.app.request.body), + {"groups": [{"name": "act:usr"}, {"name": "act"}], + "auth": key_hash}) + + def test_put_user_key_hash_wrong_type(self): + key_hash = "wrong_auth_type:1234" + + self.test_auth.app = FakeApp(iter([ + ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''), + # PUT of user object + ('201 Created', {}, '')])) + resp = Request.blank('/auth/v2/act/usr', + environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Auth-Admin-User': '.super_admin', + 'X-Auth-Admin-Key': 'supertest', + 'X-Auth-User-Key-Hash': quote(key_hash)} + ).get_response(self.test_auth) + self.assertEqual(resp.status_int, 400) + self.assertEqual(self.test_auth.app.calls, 0) + + def test_put_user_key_hash_wrong_format(self): + key_hash = "1234" + + self.test_auth.app = FakeApp(iter([ + ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''), + # PUT of user object + ('201 Created', {}, '')])) + resp = Request.blank('/auth/v2/act/usr', + environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Auth-Admin-User': '.super_admin', + 'X-Auth-Admin-Key': 'supertest', + 'X-Auth-User-Key-Hash': quote(key_hash)} + ).get_response(self.test_auth) + self.assertEqual(resp.status_int, 400) + self.assertEqual(self.test_auth.app.calls, 0) + def test_delete_user_bad_creds(self): self.test_auth.app = FakeApp(iter([ ('200 Ok', {}, json.dumps({"groups": [{"name": "act2:adm"},