upstream/openstack/python-keystone/centos/patches/0001-Rebasing-Keyring-integration.patch
Shuicheng Lin d1294d7e67 Update Keyring password info before sending out notification
Need update password before send out notification. Otherwise, any
process which monitors the "updated" notification will still get old
password from Keyring.

Partial-Bug: 1853017

Change-Id: Id1c94fedca41abe96c7b38880bf325d4a25a95eb
Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
2019-12-27 15:00:31 +08:00

148 lines
5.8 KiB
Diff

From dfe0978f6590818487bb9fc5e9b8156e77a25590 Mon Sep 17 00:00:00 2001
From: rpm-build <rpm-build>
Date: Mon, 8 Apr 2019 15:25:28 -0400
Subject: [PATCH 1/1] Rebasing Keyring integration
---
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 b85878b..56601ce 100644
--- a/keystone/exception.py
+++ b/keystone/exception.py
@@ -224,6 +224,12 @@ class ApplicationCredentialLimitExceeded(ForbiddenNotSecurity):
"maximum 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 ed43e76..da7e7ba 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
@@ -1069,6 +1071,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'])
+
@domains_configured
@exception_translated('user')
def update_user(self, user_id, user_ref, initiator=None):
@@ -1099,6 +1121,17 @@ class Manager(manager.Manager):
ref = driver.update_user(entity_id, user)
+ # 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
@@ -1128,6 +1161,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)
@@ -1141,6 +1175,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
@@ -1390,6 +1436,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 e3de1c6..e6d3536 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -42,3 +42,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
--
1.8.3.1