Stop adding entry in local_user while updating ephemerals

Problem description
===================
Today we have a consistency problem when updating federated
users via OpenStack. When I update a ephemeral user via OpenStack,
a registry in the local_user table is created, making this user
having entries in user, local_user and federated_user tables in
the database.

Furthermore, if I try to do some operations using this user
(that has entries in all three tables), I get a "More than one
user exists with the name ..." error from the OpenStack
Keystone API. It happens because the user has an entry in both
local_user and federated_user tables.

I fix the persistence in the local_user table for ephemeral
users when doing updates.

Proposal
========
I fix the problem with creating an entry in the
local_user table while updating an ephemeral user

Closes-Bug: #1848342

Change-Id: I2ac6e90f24b94dc5c0d9c0758f008a388597036c
This commit is contained in:
Pedro Martins 2019-10-10 08:51:32 -03:00 committed by pedro
parent ccb080867b
commit 7597ecc135
4 changed files with 80 additions and 2 deletions

View File

@ -82,9 +82,13 @@ class User(sql.ModelBase, sql.ModelDictMixinWithExtras):
@name.setter
def name(self, value):
if not self.local_user:
if self.federated_users:
self.federated_users[0].display_name = value
elif self.local_user:
self.local_user.name = value
else:
self.local_user = LocalUser()
self.local_user.name = value
self.local_user.name = value
@name.expression
def name(cls):

View File

@ -12,6 +12,7 @@
import uuid
from keystone.common import driver_hints
from keystone.common import provider_api
PROVIDERS = provider_api.ProviderAPIs
@ -62,3 +63,33 @@ class ShadowUsersCoreTests(object):
# The shadowed users still share the same unique ID.
self.assertEqual(shadow_user1['id'], shadow_user2['id'])
def test_shadow_federated_user_not_creating_a_local_user(self):
PROVIDERS.identity_api.shadow_federated_user(
self.federated_user['idp_id'],
self.federated_user['protocol_id'],
self.federated_user['unique_id'],
self.federated_user['display_name'],
"some_id@mail.provider")
hints = driver_hints.Hints()
hints.add_filter('name', self.federated_user['display_name'])
users = PROVIDERS.identity_api.list_users(hints=hints)
self.assertEqual(1, len(users))
# Avoid caching
self.federated_user['display_name'] = uuid.uuid4().hex
PROVIDERS.identity_api.shadow_federated_user(
self.federated_user['idp_id'],
self.federated_user['protocol_id'],
self.federated_user['unique_id'],
self.federated_user['display_name'],
"some_id@mail.provider")
hints.add_filter('name', self.federated_user['display_name'])
users = PROVIDERS.identity_api.list_users(hints=hints)
# The number os users must remain 1
self.assertEqual(1, len(users))

View File

@ -396,6 +396,26 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.delete('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
def test_update_ephemeral_user(self):
federated_user_a = model.FederatedUser()
federated_user_b = model.FederatedUser()
federated_user_a.idp_id = 'a_idp'
federated_user_b.idp_id = 'b_idp'
federated_user_a.display_name = 'federated_a'
federated_user_b.display_name = 'federated_b'
federated_users = [federated_user_a, federated_user_b]
user_a = model.User()
user_a.federated_users = federated_users
self.assertEqual(federated_user_a.display_name, user_a.name)
self.assertIsNone(user_a.password)
user_a.name = 'new_federated_a'
self.assertEqual('new_federated_a', user_a.name)
self.assertIsNone(user_a.local_user)
def test_update_user(self):
"""Call ``PATCH /users/{user_id}``."""
user = unit.new_user_ref(domain_id=self.domain_id)

View File

@ -0,0 +1,23 @@
---
fixes:
- |
[`bug 1848342 <https://bugs.launchpad.net/keystone/+bug/1848342>`_]
There was an inconsistency in the ephemeral user update flow. Every time a
federated user logged in, keystone created an entry in the local_user
table instead of just updating the entries in the user and federated_user
tables, which caused duplicate entries when listing users. Now, the
keystone will not create the entry in the local_user table while updating
an ephemeral user.
If you are affected by this bug, a fix in the keystone database will be
needed so we recommend to dump the users' tables before doing this process:
mysql db example:
- mysqldump -h <mysql host> -p -P <mysql port> -u keystone keystone federated_user local_user user > user_tables.sql
- mysql -h <mysql host> -D keystone -p -P <mysql port> -u keystone -e 'delete from local_user where user_id in (select user_id from federated_user);'
SQL:
- delete from local_user where user_id in (select user_id from federated_user);