Merge "Invalidate token of user disabled in readonly backend" into stable/2025.1
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user