Merge "Invalidate token of user disabled in readonly backend" into stable/2025.1

This commit is contained in:
Zuul
2025-12-12 17:58:52 +00:00
committed by Gerrit Code Review
3 changed files with 50 additions and 6 deletions

View File

@@ -873,18 +873,29 @@ class TokenAPITests:
# Make sure the token is valid
r = self._validate_token(unscoped_token)
self.assertValidUnscopedTokenResponse(r)
user_id = self.user["id"]
domain_id, driver, entity_id = (
PROVIDERS.identity_api._get_domain_driver_and_entity_id(user_id)
)
user = PROVIDERS.identity_api.get_user(user_id)
user["enabled"] = False
# Disable the user
self._set_user_enabled(self.user, enabled=False)
PROVIDERS.identity_api._update_user_with_federated_objects(
user, driver, entity_id
)
PROVIDERS.identity_api.get_user.invalidate(self, user_id)
# Ensure validating a token for a disabled user fails
self._validate_token(
unscoped_token, expected_status=http.client.NOT_FOUND
)
# Enable the user
self._set_user_enabled(self.user)
# Ensure validating a token for a re-enabled user fails
self._validate_token(
unscoped_token, expected_status=http.client.NOT_FOUND
## Re-enable the user
user["enabled"] = True
PROVIDERS.identity_api._update_user_with_federated_objects(
user, driver, entity_id
)
PROVIDERS.identity_api.get_user.invalidate(self, user_id)
## Ensure validating a token for a re-enabled user passes
self._validate_token(unscoped_token, expected_status=http.client.OK)
def test_unscoped_token_is_invalid_after_disabling_user_domain(self):
unscoped_token = self._get_unscoped_token()

View File

@@ -214,7 +214,32 @@ class Manager(manager.Manager):
raise exception.TokenNotFound(_('Failed to validate token'))
if current_time < expiry:
# Check for the normal revocation
self.check_revocation(token)
# Token is not revoked, but maybe it should've been.
if token.user_id:
# NOTE: in readonly backend drivers (the ones where the user
# can be deactivated remotely without involving Keystone)
# there is no token revocation event for the user deactivation.
# As such it is necessary to implement an additional check.
# Due to the caching of the `_validate_token` function it is
# not enough to have the validation in the token model itself.
# On the other side we cannot really use the same revocation
# strategy, since when the user gets re-enabled (i.e. it was
# accidentially disabled) it would not be able to login for a
# token TTL time (since we do not know when exactly the user
# was disabled/enabled (revocation to expire).
# See bug https://bugs.launchpad.net/keystone/+bug/2122615
user = PROVIDERS.identity_api.get_user(token.user_id)
if user and not user.get('enabled'):
LOG.warn(
'Unable to validate token because user '
f' {token.user_id} is disabled'
)
raise exception.UserDisabled(user_id=token.user_id)
# Token has not expired and has not been revoked.
return None
else:

View File

@@ -0,0 +1,8 @@
---
security:
- |
A potential security related issue is fixed where a token of the user from a
read-only backend (i.e. LDAP) continues being accepted after the user is
disabled in the backend. This is caused by the fact that Keystone does not
receive any notification for that and is not able to revoke such tokens.
See https://bugs.launchpad.net/keystone/+bug/2122615 for details.