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
This commit is contained in:
Andy Ning 2022-04-05 16:58:00 -04:00 committed by Al Bailey
parent bab092610c
commit 86820ad8ec
2 changed files with 152 additions and 0 deletions

View File

@ -0,0 +1,151 @@
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

View File

@ -0,0 +1 @@
0001-Support-storing-users-in-keyring.patch