summaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/contrib/revoke/model.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/contrib/revoke/model.py')
-rw-r--r--keystone-moon/keystone/contrib/revoke/model.py120
1 files changed, 63 insertions, 57 deletions
diff --git a/keystone-moon/keystone/contrib/revoke/model.py b/keystone-moon/keystone/contrib/revoke/model.py
index 5e92042d..1a23d57d 100644
--- a/keystone-moon/keystone/contrib/revoke/model.py
+++ b/keystone-moon/keystone/contrib/revoke/model.py
@@ -11,6 +11,9 @@
# under the License.
from oslo_utils import timeutils
+from six.moves import map
+
+from keystone.common import utils
# The set of attributes common between the RevokeEvent
@@ -43,6 +46,15 @@ _TOKEN_KEYS = ['identity_domain_id',
'trustor_id',
'trustee_id']
+# Alternative names to be checked in token for every field in
+# revoke tree.
+ALTERNATIVES = {
+ 'user_id': ['user_id', 'trustor_id', 'trustee_id'],
+ 'domain_id': ['identity_domain_id', 'assignment_domain_id'],
+ # For a domain-scoped token, the domain is in assignment_domain_id.
+ 'domain_scope_id': ['assignment_domain_id', ],
+}
+
REVOKE_KEYS = _NAMES + _EVENT_ARGS
@@ -100,10 +112,10 @@ class RevokeEvent(object):
if self.consumer_id is not None:
event['OS-OAUTH1:access_token_id'] = self.access_token_id
if self.expires_at is not None:
- event['expires_at'] = timeutils.isotime(self.expires_at)
+ event['expires_at'] = utils.isotime(self.expires_at)
if self.issued_before is not None:
- event['issued_before'] = timeutils.isotime(self.issued_before,
- subsecond=True)
+ event['issued_before'] = utils.isotime(self.issued_before,
+ subsecond=True)
return event
def key_for_name(self, name):
@@ -111,7 +123,7 @@ class RevokeEvent(object):
def attr_keys(event):
- return map(event.key_for_name, _EVENT_NAMES)
+ return list(map(event.key_for_name, _EVENT_NAMES))
class RevokeTree(object):
@@ -176,7 +188,52 @@ class RevokeTree(object):
del parent[key]
def add_events(self, revoke_events):
- return map(self.add_event, revoke_events or [])
+ return list(map(self.add_event, revoke_events or []))
+
+ @staticmethod
+ def _next_level_keys(name, token_data):
+ """Generate keys based on current field name and token data
+
+ Generate all keys to look for in the next iteration of revocation
+ event tree traversal.
+ """
+ yield '*'
+ if name == 'role_id':
+ # Roles are very special since a token has a list of them.
+ # If the revocation event matches any one of them,
+ # revoke the token.
+ for role_id in token_data.get('roles', []):
+ yield role_id
+ else:
+ # For other fields we try to get any branch that concur
+ # with any alternative field in the token.
+ for alt_name in ALTERNATIVES.get(name, [name]):
+ yield token_data[alt_name]
+
+ def _search(self, revoke_map, names, token_data):
+ """Search for revocation event by token_data
+
+ Traverse the revocation events tree looking for event matching token
+ data issued after the token.
+ """
+ if not names:
+ # The last (leaf) level is checked in a special way because we
+ # verify issued_at field differently.
+ try:
+ return revoke_map['issued_before'] > token_data['issued_at']
+ except KeyError:
+ return False
+
+ name, remaining_names = names[0], names[1:]
+
+ for key in self._next_level_keys(name, token_data):
+ subtree = revoke_map.get('%s=%s' % (name, key))
+ if subtree and self._search(subtree, remaining_names, token_data):
+ return True
+
+ # If we made it out of the loop then no element in revocation tree
+ # corresponds to our token and it is good.
+ return False
def is_revoked(self, token_data):
"""Check if a token matches the revocation event
@@ -195,58 +252,7 @@ class RevokeTree(object):
'consumer_id', 'access_token_id'
"""
- # Alternative names to be checked in token for every field in
- # revoke tree.
- alternatives = {
- 'user_id': ['user_id', 'trustor_id', 'trustee_id'],
- 'domain_id': ['identity_domain_id', 'assignment_domain_id'],
- # For a domain-scoped token, the domain is in assignment_domain_id.
- 'domain_scope_id': ['assignment_domain_id', ],
- }
- # Contains current forest (collection of trees) to be checked.
- partial_matches = [self.revoke_map]
- # We iterate over every layer of our revoke tree (except the last one).
- for name in _EVENT_NAMES:
- # bundle is the set of partial matches for the next level down
- # the tree
- bundle = []
- wildcard = '%s=*' % (name,)
- # For every tree in current forest.
- for tree in partial_matches:
- # If there is wildcard node on current level we take it.
- bundle.append(tree.get(wildcard))
- if name == 'role_id':
- # Roles are very special since a token has a list of them.
- # If the revocation event matches any one of them,
- # revoke the token.
- for role_id in token_data.get('roles', []):
- bundle.append(tree.get('role_id=%s' % role_id))
- else:
- # For other fields we try to get any branch that concur
- # with any alternative field in the token.
- for alt_name in alternatives.get(name, [name]):
- bundle.append(
- tree.get('%s=%s' % (name, token_data[alt_name])))
- # tree.get returns `None` if there is no match, so `bundle.append`
- # adds a 'None' entry. This call remoes the `None` entries.
- partial_matches = [x for x in bundle if x is not None]
- if not partial_matches:
- # If we end up with no branches to follow means that the token
- # is definitely not in the revoke tree and all further
- # iterations will be for nothing.
- return False
-
- # The last (leaf) level is checked in a special way because we verify
- # issued_at field differently.
- for leaf in partial_matches:
- try:
- if leaf['issued_before'] > token_data['issued_at']:
- return True
- except KeyError:
- pass
- # If we made it out of the loop then no element in revocation tree
- # corresponds to our token and it is good.
- return False
+ return self._search(self.revoke_map, _EVENT_NAMES, token_data)
def build_token_values_v2(access, default_domain_id):