86820ad8ec
This update patched keystone to support storing users in keyring under "CGCS" service. Test Plan for Debian: PASS: package build, image build PASS: system bootstrap, unlock PASS: Change keystone "admin" password, observe it changes in keyring too. PASS: Add a new keystone user "test" with password, add the user to keyring by "keyring set CGCS test". Change test's password, observe it changes in keyring too. PASS: Delete the keystone user "test", observe user "test" is deleted from keyring. Story: 2009965 Task: 44970 Signed-off-by: Andy Ning <andy.ning@windriver.com> Change-Id: I75ea23f87487b764370a0990ad8aba896d3a0767
152 lines
6.0 KiB
Diff
152 lines
6.0 KiB
Diff
From 45b5c5b71b4ad70c5694f06126adfc60a31c51fc Mon Sep 17 00:00:00 2001
|
|
From: Andy Ning <andy.ning@windriver.com>
|
|
Date: Tue, 5 Apr 2022 10:39:32 -0400
|
|
Subject: [PATCH] Support storing users in keyring
|
|
|
|
This patch added support to store keystone users in keyring in
|
|
"CGCS" service.
|
|
|
|
Signed-off-by: Andy Ning <andy.ning@windriver.com>
|
|
---
|
|
keystone/exception.py | 6 +++++
|
|
keystone/identity/core.py | 54 +++++++++++++++++++++++++++++++++++++++
|
|
requirements.txt | 1 +
|
|
3 files changed, 61 insertions(+)
|
|
|
|
diff --git a/keystone/exception.py b/keystone/exception.py
|
|
index c62338b..3cbddfb 100644
|
|
--- a/keystone/exception.py
|
|
+++ b/keystone/exception.py
|
|
@@ -227,6 +227,12 @@ class CredentialLimitExceeded(ForbiddenNotSecurity):
|
|
"of %(limit)d already exceeded for user.")
|
|
|
|
|
|
+class WRSForbiddenAction(Error):
|
|
+ message_format = _("That action is not permitted")
|
|
+ code = 403
|
|
+ title = 'Forbidden'
|
|
+
|
|
+
|
|
class SecurityError(Error):
|
|
"""Security error exception.
|
|
|
|
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
|
|
index 38ebe2f..31d6cd6 100644
|
|
--- a/keystone/identity/core.py
|
|
+++ b/keystone/identity/core.py
|
|
@@ -17,6 +17,7 @@
|
|
import copy
|
|
import functools
|
|
import itertools
|
|
+import keyring
|
|
import operator
|
|
import os
|
|
import threading
|
|
@@ -54,6 +55,7 @@ MEMOIZE_ID_MAPPING = cache.get_memoization_decorator(group='identity',
|
|
|
|
DOMAIN_CONF_FHEAD = 'keystone.'
|
|
DOMAIN_CONF_FTAIL = '.conf'
|
|
+KEYRING_CGCS_SERVICE = "CGCS"
|
|
|
|
# The number of times we will attempt to register a domain to use the SQL
|
|
# driver, if we find that another process is in the middle of registering or
|
|
@@ -1125,6 +1127,26 @@ class Manager(manager.Manager):
|
|
if new_ref['domain_id'] != orig_ref['domain_id']:
|
|
raise exception.ValidationError(_('Cannot change Domain ID'))
|
|
|
|
+ def _update_keyring_password(self, user, new_password):
|
|
+ """Update user password in Keyring backend.
|
|
+ This method Looks up user entries in Keyring backend
|
|
+ and accordingly update the corresponding user password.
|
|
+ :param user : keyring user struct
|
|
+ :param new_password : new password to set
|
|
+ """
|
|
+ if (new_password is not None) and ('name' in user):
|
|
+ try:
|
|
+ # only update if an entry exists
|
|
+ if (keyring.get_password(KEYRING_CGCS_SERVICE, user['name'])):
|
|
+ keyring.set_password(KEYRING_CGCS_SERVICE,
|
|
+ user['name'], new_password)
|
|
+ except (keyring.errors.PasswordSetError, RuntimeError):
|
|
+ msg = ('Failed to Update Keyring Password for the user %s')
|
|
+ LOG.warning(msg, user['name'])
|
|
+ # only raise an exception if this is the admin user
|
|
+ if (user['name'] == 'admin'):
|
|
+ raise exception.WRSForbiddenAction(msg % user['name'])
|
|
+
|
|
def _update_user_with_federated_objects(self, user, driver, entity_id):
|
|
# If the user did not pass a federated object along inside the user
|
|
# object then we simply update the user as normal and add the
|
|
@@ -1181,6 +1203,17 @@ class Manager(manager.Manager):
|
|
|
|
ref = self._update_user_with_federated_objects(user, driver, entity_id)
|
|
|
|
+ # Certain local Keystone users are stored in Keystone as opposed
|
|
+ # to the default SQL Identity backend, such as the admin user.
|
|
+ # When its password is updated, we need to update Keyring as well
|
|
+ # as certain services retrieve this user context from Keyring and
|
|
+ # will get auth failures
|
|
+ # Need update password before send out notification. Otherwise,
|
|
+ # any process monitor the notification will still get old password
|
|
+ # from Keyring.
|
|
+ if ('password' in user) and ('name' in ref):
|
|
+ self._update_keyring_password(ref, user['password'])
|
|
+
|
|
notifications.Audit.updated(self._USER, user_id, initiator)
|
|
|
|
enabled_change = ((user.get('enabled') is False) and
|
|
@@ -1210,6 +1243,7 @@ class Manager(manager.Manager):
|
|
hints.add_filter('user_id', user_id)
|
|
fed_users = PROVIDERS.shadow_users_api.list_federated_users_info(hints)
|
|
|
|
+ username = user_old.get('name', "")
|
|
driver.delete_user(entity_id)
|
|
PROVIDERS.assignment_api.delete_user_assignments(user_id)
|
|
self.get_user.invalidate(self, user_id)
|
|
@@ -1223,6 +1257,18 @@ class Manager(manager.Manager):
|
|
|
|
PROVIDERS.credential_api.delete_credentials_for_user(user_id)
|
|
PROVIDERS.id_mapping_api.delete_id_mapping(user_id)
|
|
+
|
|
+ # Delete the keyring entry associated with this user (if present)
|
|
+ try:
|
|
+ keyring.delete_password(KEYRING_CGCS_SERVICE, username)
|
|
+ except keyring.errors.PasswordDeleteError:
|
|
+ LOG.warning(('delete_user: PasswordDeleteError for %s'),
|
|
+ username)
|
|
+ pass
|
|
+ except exception.UserNotFound:
|
|
+ LOG.warning(('delete_user: UserNotFound for %s'),
|
|
+ username)
|
|
+ pass
|
|
notifications.Audit.deleted(self._USER, user_id, initiator)
|
|
|
|
# Invalidate user role assignments cache region, as it may be caching
|
|
@@ -1475,6 +1521,14 @@ class Manager(manager.Manager):
|
|
notifications.Audit.updated(self._USER, user_id, initiator)
|
|
self._persist_revocation_event_for_user(user_id)
|
|
|
|
+ user = self.get_user(user_id)
|
|
+ # Update Keyring password for the 'user' if it
|
|
+ # has an entry in Keyring
|
|
+ if (original_password) and ('name' in user):
|
|
+ # Change the 'user' password in keyring, provided the user
|
|
+ # has an entry in Keyring backend
|
|
+ self._update_keyring_password(user, new_password)
|
|
+
|
|
@MEMOIZE
|
|
def _shadow_nonlocal_user(self, user):
|
|
try:
|
|
diff --git a/requirements.txt b/requirements.txt
|
|
index 33a2c42..1119c52 100644
|
|
--- a/requirements.txt
|
|
+++ b/requirements.txt
|
|
@@ -36,3 +36,4 @@ pycadf!=2.0.0,>=1.1.0 # Apache-2.0
|
|
msgpack>=0.5.0 # Apache-2.0
|
|
osprofiler>=1.4.0 # Apache-2.0
|
|
pytz>=2013.6 # MIT
|
|
+keyring>=5.3
|
|
--
|
|
2.25.1
|
|
|