From 79b902180529c42f79001932b52eee2c77b66aec Mon Sep 17 00:00:00 2001 From: Fabian Bonk Date: Fri, 19 Jul 2019 16:31:20 +0200 Subject: [PATCH] mgr/dashboard: support setting password hashes Signed-off-by: Fabian Bonk --- doc/mgr/dashboard.rst | 7 ++++ .../mgr/dashboard/services/access_control.py | 24 ++++++++++++- .../dashboard/tests/test_access_control.py | 36 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/doc/mgr/dashboard.rst b/doc/mgr/dashboard.rst index 86e62e854db..8c2ba4a5340 100644 --- a/doc/mgr/dashboard.rst +++ b/doc/mgr/dashboard.rst @@ -619,6 +619,13 @@ We provide a set of CLI commands to manage user accounts: $ ceph dashboard ac-user-set-password +- *Change Password Hash*:: + + $ ceph dashboard ac-user-set-password-hash + + The hash must be a bcrypt hash and salt, e.g. ``$2b$12$Pt3Vq/rDt2y9glTPSV.VFegiLkQeIpddtkhoFetNApYmIJOY8gau2``. + This can be used to import users from an external database. + - *Modify User (name, and email)*:: $ ceph dashboard ac-user-set-info diff --git a/src/pybind/mgr/dashboard/services/access_control.py b/src/pybind/mgr/dashboard/services/access_control.py index 2fb873479c1..6c7e58c83c1 100644 --- a/src/pybind/mgr/dashboard/services/access_control.py +++ b/src/pybind/mgr/dashboard/services/access_control.py @@ -186,7 +186,10 @@ class User(object): self.lastUpdate = int(time.time()) def set_password(self, password): - self.password = password_hash(password) + self.set_password_hash(password_hash(password)) + + def set_password_hash(self, hashed_password): + self.password = hashed_password self.refreshLastUpdate() def compare_password(self, password): @@ -632,6 +635,25 @@ def ac_user_set_password(_, username, password): return -errno.ENOENT, '', str(ex) +@CLIWriteCommand('dashboard ac-user-set-password-hash', + 'name=username,type=CephString ' + 'name=hashed_password,type=CephString', + 'Set user password bcrypt hash') +def ac_user_set_password_hash(_, username, hashed_password): + try: + # make sure the hashed_password is actually a bcrypt hash + bcrypt.checkpw(b'', hashed_password.encode('utf-8')) + user = mgr.ACCESS_CTRL_DB.get_user(username) + user.set_password_hash(hashed_password) + + mgr.ACCESS_CTRL_DB.save() + return 0, json.dumps(user.to_dict()), '' + except ValueError: + return -errno.EINVAL, '', 'Invalid password hash' + except UserDoesNotExist as ex: + return -errno.ENOENT, '', str(ex) + + @CLIWriteCommand('dashboard ac-user-set-info', 'name=username,type=CephString ' 'name=name,type=CephString ' diff --git a/src/pybind/mgr/dashboard/tests/test_access_control.py b/src/pybind/mgr/dashboard/tests/test_access_control.py index 68bdaf1aa79..955f71bd22f 100644 --- a/src/pybind/mgr/dashboard/tests/test_access_control.py +++ b/src/pybind/mgr/dashboard/tests/test_access_control.py @@ -562,6 +562,42 @@ class AccessControlTest(unittest.TestCase, CLICommandTestMixin): self.assertEqual(ctx.exception.retcode, -errno.ENOENT) self.assertEqual(str(ctx.exception), "User 'admin' does not exist") + def test_set_user_password_hash(self): + user_orig = self.test_create_user() + user = self.exec_cmd('ac-user-set-password-hash', username='admin', + hashed_password='$2b$12$Pt3Vq/rDt2y9glTPSV.' + 'VFegiLkQeIpddtkhoFetNApYmIJOY8gau2') + pass_hash = password_hash('newpass', user['password']) + self.assertDictEqual(user, { + 'username': 'admin', + 'password': pass_hash, + 'name': 'admin User', + 'email': 'admin@user.com', + 'lastUpdate': user['lastUpdate'], + 'roles': [] + }) + self.validate_persistent_user('admin', [], pass_hash, 'admin User', + 'admin@user.com') + self.assertGreaterEqual(user['lastUpdate'], user_orig['lastUpdate']) + + def test_set_user_password_hash_nonexistent_user(self): + with self.assertRaises(CmdException) as ctx: + self.exec_cmd('ac-user-set-password-hash', username='admin', + hashed_password='$2b$12$Pt3Vq/rDt2y9glTPSV.' + 'VFegiLkQeIpddtkhoFetNApYmIJOY8gau2') + + self.assertEqual(ctx.exception.retcode, -errno.ENOENT) + self.assertEqual(str(ctx.exception), "User 'admin' does not exist") + + def test_set_user_password_hash_broken_hash(self): + self.test_create_user() + with self.assertRaises(CmdException) as ctx: + self.exec_cmd('ac-user-set-password-hash', username='admin', + hashed_password='') + + self.assertEqual(ctx.exception.retcode, -errno.EINVAL) + self.assertEqual(str(ctx.exception), 'Invalid password hash') + def test_set_login_credentials(self): self.exec_cmd('set-login-credentials', username='admin', password='admin') -- 2.39.5