upstream/openstack/keystone/debian/patches/0001-Support-storing-users-in-keyring.patch
Andy Ning 86820ad8ec Add support to keystone to store users in keyring on Debian
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
2022-04-09 16:03:20 +00:00

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