summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-01-14 12:56:45 +0000
committerGerrit Code Review <review@openstack.org>2016-01-14 12:56:45 +0000
commit1701856bf1cf0810941f6ae86eab40f6b0b08567 (patch)
tree76ac09e45369ab8defecf6605a9c42453076ccef
parentea68a87bf38f24fc44b0db64381eba188f428343 (diff)
parentbff03b5726fe5cac93d44a66715eea49b89c8cb0 (diff)
downloadkeystone-1701856bf1cf0810941f6ae86eab40f6b0b08567.tar.gz
Merge "Add audit IDs to revocation events" into stable/liberty
-rw-r--r--keystone/tests/unit/test_backend.py39
-rw-r--r--keystone/tests/unit/test_backend_sql.py3
-rw-r--r--keystone/token/persistence/backends/kvs.py9
-rw-r--r--keystone/token/persistence/backends/sql.py12
-rw-r--r--releasenotes/notes/bug-1490804-de58a9606edb31eb.yaml13
5 files changed, 61 insertions, 15 deletions
diff --git a/keystone/tests/unit/test_backend.py b/keystone/tests/unit/test_backend.py
index 2340645a0..127373665 100644
--- a/keystone/tests/unit/test_backend.py
+++ b/keystone/tests/unit/test_backend.py
@@ -4426,7 +4426,9 @@ class TokenTests(object):
token_id = self._create_token_id()
data = {'id': token_id, 'a': 'b',
'trust_id': None,
- 'user': {'id': 'testuserid'}}
+ 'user': {'id': 'testuserid'},
+ 'token_data': {'access': {'token': {
+ 'audit_ids': [uuid.uuid4().hex]}}}}
data_ref = self.token_provider_api._persistence.create_token(token_id,
data)
expires = data_ref.pop('expires')
@@ -4461,7 +4463,8 @@ class TokenTests(object):
# FIXME(morganfainberg): These tokens look nothing like "Real" tokens.
# This should be fixed when token issuance is cleaned up.
data = {'id': token_id, 'a': 'b',
- 'user': {'id': user_id}}
+ 'user': {'id': user_id},
+ 'access': {'token': {'audit_ids': [uuid.uuid4().hex]}}}
if tenant_id is not None:
data['tenant'] = {'id': tenant_id, 'name': tenant_id}
if tenant_id is NULL_OBJECT:
@@ -4470,7 +4473,7 @@ class TokenTests(object):
data['expires'] = expires
if trust_id is not None:
data['trust_id'] = trust_id
- data.setdefault('access', {}).setdefault('trust', {})
+ data['access'].setdefault('trust', {})
# Testuserid2 is used here since a trustee will be different in
# the cases of impersonation and therefore should not match the
# token's user_id.
@@ -4633,17 +4636,21 @@ class TokenTests(object):
self.assertEqual(data_ref, new_data_ref)
- def check_list_revoked_tokens(self, token_ids):
- revoked_ids = [x['id']
- for x in self.token_provider_api.list_revoked_tokens()]
+ def check_list_revoked_tokens(self, token_infos):
+ revocation_list = self.token_provider_api.list_revoked_tokens()
+ revoked_ids = [x['id'] for x in revocation_list]
+ revoked_audit_ids = [x['audit_id'] for x in revocation_list]
self._assert_revoked_token_list_matches_token_persistence(revoked_ids)
- for token_id in token_ids:
+ for token_id, audit_id in token_infos:
self.assertIn(token_id, revoked_ids)
+ self.assertIn(audit_id, revoked_audit_ids)
def delete_token(self):
token_id = uuid.uuid4().hex
+ audit_id = uuid.uuid4().hex
data = {'id_hash': token_id, 'id': token_id, 'a': 'b',
- 'user': {'id': 'testuserid'}}
+ 'user': {'id': 'testuserid'},
+ 'token_data': {'token': {'audit_ids': [audit_id]}}}
data_ref = self.token_provider_api._persistence.create_token(token_id,
data)
self.token_provider_api._persistence.delete_token(token_id)
@@ -4655,7 +4662,7 @@ class TokenTests(object):
exception.TokenNotFound,
self.token_provider_api._persistence.delete_token,
data_ref['id'])
- return token_id
+ return (token_id, audit_id)
def test_list_revoked_tokens_returns_empty_list(self):
revoked_ids = [x['id']
@@ -4706,12 +4713,16 @@ class TokenTests(object):
token_data = {'id_hash': token_id, 'id': token_id, 'a': 'b',
'expires': expire_time,
'trust_id': None,
- 'user': {'id': 'testuserid'}}
+ 'user': {'id': 'testuserid'},
+ 'token_data': {'token': {
+ 'audit_ids': [uuid.uuid4().hex]}}}
token2_id = uuid.uuid4().hex
token2_data = {'id_hash': token2_id, 'id': token2_id, 'a': 'b',
'expires': expire_time,
'trust_id': None,
- 'user': {'id': 'testuserid'}}
+ 'user': {'id': 'testuserid'},
+ 'token_data': {'token': {
+ 'audit_ids': [uuid.uuid4().hex]}}}
# Create 2 Tokens.
self.token_provider_api._persistence.create_token(token_id,
token_data)
@@ -4746,7 +4757,8 @@ class TokenTests(object):
def _test_predictable_revoked_pki_token_id(self, hash_fn):
token_id = self._create_token_id()
token_id_hash = hash_fn(token_id).hexdigest()
- token = {'user': {'id': uuid.uuid4().hex}}
+ token = {'user': {'id': uuid.uuid4().hex},
+ 'token_data': {'token': {'audit_ids': [uuid.uuid4().hex]}}}
self.token_provider_api._persistence.create_token(token_id, token)
self.token_provider_api._persistence.delete_token(token_id)
@@ -4768,7 +4780,8 @@ class TokenTests(object):
def test_predictable_revoked_uuid_token_id(self):
token_id = uuid.uuid4().hex
- token = {'user': {'id': uuid.uuid4().hex}}
+ token = {'user': {'id': uuid.uuid4().hex},
+ 'token_data': {'token': {'audit_ids': [uuid.uuid4().hex]}}}
self.token_provider_api._persistence.create_token(token_id, token)
self.token_provider_api._persistence.delete_token(token_id)
diff --git a/keystone/tests/unit/test_backend_sql.py b/keystone/tests/unit/test_backend_sql.py
index 69fac63a2..51221a313 100644
--- a/keystone/tests/unit/test_backend_sql.py
+++ b/keystone/tests/unit/test_backend_sql.py
@@ -492,7 +492,8 @@ class SqlToken(SqlTests, test_backend.TokenTests):
# necessary.
expected_query_args = (token_sql.TokenModel.id,
- token_sql.TokenModel.expires)
+ token_sql.TokenModel.expires,
+ token_sql.TokenModel.extra,)
with mock.patch.object(token_sql, 'sql') as mock_sql:
tok = token_sql.Token()
diff --git a/keystone/token/persistence/backends/kvs.py b/keystone/token/persistence/backends/kvs.py
index 519315866..60f7931db 100644
--- a/keystone/token/persistence/backends/kvs.py
+++ b/keystone/token/persistence/backends/kvs.py
@@ -210,6 +210,15 @@ class Token(token.persistence.TokenDriverV8):
subsecond=True)
revoked_token_data['id'] = data['id']
+ token_data = data['token_data']
+ if 'access' in token_data:
+ # It's a v2 token.
+ audit_ids = token_data['access']['token']['audit_ids']
+ else:
+ # It's a v3 token.
+ audit_ids = token_data['token']['audit_ids']
+ revoked_token_data['audit_id'] = audit_ids[0]
+
token_list = self._get_key_or_default(self.revocation_key, default=[])
if not isinstance(token_list, list):
# NOTE(morganfainberg): In the case that the revocation list is not
diff --git a/keystone/token/persistence/backends/sql.py b/keystone/token/persistence/backends/sql.py
index 6fc1d2237..d67762071 100644
--- a/keystone/token/persistence/backends/sql.py
+++ b/keystone/token/persistence/backends/sql.py
@@ -228,13 +228,23 @@ class Token(token.persistence.TokenDriverV8):
session = sql.get_session()
tokens = []
now = timeutils.utcnow()
- query = session.query(TokenModel.id, TokenModel.expires)
+ query = session.query(TokenModel.id, TokenModel.expires,
+ TokenModel.extra)
query = query.filter(TokenModel.expires > now)
token_references = query.filter_by(valid=False)
for token_ref in token_references:
+ token_data = token_ref[2]['token_data']
+ if 'access' in token_data:
+ # It's a v2 token.
+ audit_ids = token_data['access']['token']['audit_ids']
+ else:
+ # It's a v3 token.
+ audit_ids = token_data['token']['audit_ids']
+
record = {
'id': token_ref[0],
'expires': token_ref[1],
+ 'audit_id': audit_ids[0],
}
tokens.append(record)
return tokens
diff --git a/releasenotes/notes/bug-1490804-de58a9606edb31eb.yaml b/releasenotes/notes/bug-1490804-de58a9606edb31eb.yaml
new file mode 100644
index 000000000..0d5c20348
--- /dev/null
+++ b/releasenotes/notes/bug-1490804-de58a9606edb31eb.yaml
@@ -0,0 +1,13 @@
+---
+features:
+ - >
+ [`bug 1490804 <https://bugs.launchpad.net/keystone/+bug/1490804>`_]
+ Audit IDs are included in the token revocation list.
+security:
+ - >
+ [`bug 1490804 <https://bugs.launchpad.net/keystone/+bug/1490804>`_]
+ [`CVE-2015-7546 <http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-7546>`_]
+ A bug is fixed where an attacker could avoid token revocation when the PKI
+ or PKIZ token provider is used. The complete remediation for this
+ vulnerability requires the corresponding fix in the keystonemiddleware
+ project.