keystone  18.0.0
About: OpenStack Keystone (Core Service: Identity) provides an authentication and authorization service for other OpenStack services. Provides a catalog of endpoints for all OpenStack services.
The "Victoria" series (maintained release).
  Fossies Dox: keystone-18.0.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

revoke_model.py
Go to the documentation of this file.
1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
11 # under the License.
12 
13 from oslo_log import log
14 from oslo_serialization import msgpackutils
15 from oslo_utils import timeutils
16 
17 from keystone.common import cache
18 from keystone.common import utils
19 
20 
21 LOG = log.getLogger(__name__)
22 
23 # The set of attributes common between the RevokeEvent
24 # and the dictionaries created from the token Data.
25 _NAMES = ['trust_id',
26  'consumer_id',
27  'access_token_id',
28  'audit_id',
29  'audit_chain_id',
30  'expires_at',
31  'domain_id',
32  'project_id',
33  'user_id',
34  'role_id']
35 
36 
37 # Additional arguments for creating a RevokeEvent
38 _EVENT_ARGS = ['issued_before', 'revoked_at']
39 
40 # Names of attributes in the RevocationEvent, including "virtual" attributes.
41 # Virtual attributes are those added based on other values.
42 _EVENT_NAMES = _NAMES + ['domain_scope_id']
43 
44 # Values that will be in the token data but not in the event.
45 # These will compared with event values that have different names.
46 # For example: both trustor_id and trustee_id are compared against user_id
47 _TOKEN_KEYS = ['identity_domain_id',
48  'assignment_domain_id',
49  'issued_at',
50  'trustor_id',
51  'trustee_id']
52 
53 # Alternative names to be checked in token for every field in
54 # revoke tree.
55 ALTERNATIVES = {
56  'user_id': ['user_id', 'trustor_id', 'trustee_id'],
57  'domain_id': ['identity_domain_id', 'assignment_domain_id'],
58  # For a domain-scoped token, the domain is in assignment_domain_id.
59  'domain_scope_id': ['assignment_domain_id', ],
60 }
61 
62 
63 REVOKE_KEYS = _NAMES + _EVENT_ARGS
64 
65 
66 def blank_token_data(issued_at):
67  token_data = dict()
68  for name in _NAMES:
69  token_data[name] = None
70  for name in _TOKEN_KEYS:
71  token_data[name] = None
72  # required field
73  token_data['issued_at'] = issued_at
74  return token_data
75 
76 
77 class RevokeEvent(object):
78  def __init__(self, **kwargs):
79  for k in REVOKE_KEYS:
80  v = kwargs.get(k)
81  setattr(self, k, v)
82 
83  if self.domain_id and self.expires_at:
84  # This is revoking a domain-scoped token.
86  self.domain_id = None
87  else:
88  # This is revoking all tokens for a domain.
89  self.domain_scope_id = None
90 
91  if self.expires_at is not None:
92  # Trim off the expiration time because MySQL timestamps are only
93  # accurate to the second.
94  self.expires_at = self.expires_at.replace(microsecond=0)
95 
96  if self.revoked_at is None:
97  self.revoked_at = timeutils.utcnow().replace(microsecond=0)
98  if self.issued_before is None:
100 
101  def to_dict(self):
102  keys = ['user_id',
103  'role_id',
104  'domain_id',
105  'domain_scope_id',
106  'project_id',
107  'audit_id',
108  'audit_chain_id',
109  ]
110  event = {key: self.__dict__[key] for key in keys
111  if self.__dict__[key] is not None}
112  if self.trust_id is not None:
113  event['OS-TRUST:trust_id'] = self.trust_id
114  if self.consumer_id is not None:
115  event['OS-OAUTH1:consumer_id'] = self.consumer_id
116  if self.access_token_id is not None:
117  event['OS-OAUTH1:access_token_id'] = self.access_token_id
118  if self.expires_at is not None:
119  event['expires_at'] = utils.isotime(self.expires_at)
120  if self.issued_before is not None:
121  event['issued_before'] = utils.isotime(self.issued_before,
122  subsecond=True)
123  if self.revoked_at is not None:
124  event['revoked_at'] = utils.isotime(self.revoked_at,
125  subsecond=True)
126  return event
127 
128 
129 def is_revoked(events, token_data):
130  """Check if a token matches a revocation event.
131 
132  Compare a token against every revocation event. If the token matches an
133  event in the `events` list, the token is revoked. If the token is compared
134  against every item in the list without a match, it is not considered
135  revoked from the `revoke_api`.
136 
137  :param events: a list of RevokeEvent instances
138  :param token_data: map based on a flattened view of the token. The required
139  fields are `expires_at`,`user_id`, `project_id`,
140  `identity_domain_id`, `assignment_domain_id`,
141  `trust_id`, `trustor_id`, `trustee_id` `consumer_id` and
142  `access_token_id`
143  :returns: True if the token matches an existing revocation event, meaning
144  the token is revoked. False is returned if the token does not
145  match any revocation events, meaning the token is considered
146  valid by the revocation API.
147  """
148  return any([matches(e, token_data) for e in events])
149 
150 
151 def matches(event, token_values):
152  """See if the token matches the revocation event.
153 
154  A brute force approach to checking.
155  Compare each attribute from the event with the corresponding
156  value from the token. If the event does not have a value for
157  the attribute, a match is still possible. If the event has a
158  value for the attribute, and it does not match the token, no match
159  is possible, so skip the remaining checks.
160 
161  :param event: a RevokeEvent instance
162  :param token_values: dictionary with set of values taken from the
163  token
164  :returns: True if the token matches the revocation event, indicating the
165  token has been revoked
166  """
167  # If any one check does not match, the whole token does
168  # not match the event. The numerous return False indicate
169  # that the token is still valid and short-circuits the
170  # rest of the logic.
171 
172  # The token has two attributes that can match the domain_id.
173  if event.domain_id is not None and event.domain_id not in(
174  token_values['identity_domain_id'],
175  token_values['assignment_domain_id'],):
176  return False
177 
178  if event.domain_scope_id is not None and event.domain_scope_id not in (
179  token_values['assignment_domain_id'],):
180  return False
181 
182  # If an event specifies an attribute name, but it does not match, the token
183  # is not revoked.
184  if event.expires_at is not None and event.expires_at not in (
185  token_values['expires_at'],):
186  return False
187 
188  if event.trust_id is not None and event.trust_id not in (
189  token_values['trust_id'],):
190  return False
191 
192  if event.consumer_id is not None and event.consumer_id not in (
193  token_values['consumer_id'],):
194  return False
195 
196  if event.audit_chain_id is not None and event.audit_chain_id not in (
197  token_values['audit_chain_id'],):
198  return False
199 
200  if event.role_id is not None and event.role_id not in (
201  token_values['roles']):
202  return False
203 
204  return True
205 
206 
208 
209  token_expires_at = timeutils.parse_isotime(token.expires_at)
210 
211  # Trim off the microseconds because the revocation event only has
212  # expirations accurate to the second.
213  token_expires_at = token_expires_at.replace(microsecond=0)
214 
215  token_values = {
216  'expires_at': timeutils.normalize_time(token_expires_at),
217  'issued_at': timeutils.normalize_time(
218  timeutils.parse_isotime(token.issued_at)),
219  'audit_id': token.audit_id,
220  'audit_chain_id': token.parent_audit_id,
221  }
222 
223  if token.user_id is not None:
224  token_values['user_id'] = token.user_id
225  # Federated users do not have a domain, be defensive and get the user
226  # domain set to None in the federated user case.
227  token_values['identity_domain_id'] = token.user_domain['id']
228  else:
229  token_values['user_id'] = None
230  token_values['identity_domain_id'] = None
231 
232  if token.project_id is not None:
233  token_values['project_id'] = token.project_id
234  # The domain_id of projects acting as domains is None
235  token_values['assignment_domain_id'] = token.project_domain['id']
236  else:
237  token_values['project_id'] = None
238 
239  if token.domain_id is not None:
240  token_values['assignment_domain_id'] = token.domain_id
241  else:
242  token_values['assignment_domain_id'] = None
243 
244  role_list = []
245  if token.roles is not None:
246  for role in token.roles:
247  role_list.append(role['id'])
248  token_values['roles'] = role_list
249 
250  if token.trust_scoped:
251  token_values['trust_id'] = token.trust['id']
252  token_values['trustor_id'] = token.trustor['id']
253  token_values['trustee_id'] = token.trustee['id']
254  else:
255  token_values['trust_id'] = None
256  token_values['trustor_id'] = None
257  token_values['trustee_id'] = None
258 
259  if token.oauth_scoped:
260  token_values['consumer_id'] = token.access_token['consumer_id']
261  token_values['access_token_id'] = token.access_token['id']
262  else:
263  token_values['consumer_id'] = None
264  token_values['access_token_id'] = None
265 
266  return token_values
267 
268 
269 class _RevokeEventHandler(object):
270  # NOTE(morganfainberg): There needs to be reserved "registry" entries set
271  # in oslo_serialization for application-specific handlers. We picked 127
272  # here since it's waaaaaay far out before oslo_serialization will use it.
273  identity = 127
274  handles = (RevokeEvent,)
275 
276  def __init__(self, registry):
277  self._registry = registry
278 
279  def serialize(self, obj):
280  return msgpackutils.dumps(obj.__dict__, registry=self._registry)
281 
282  def deserialize(self, data):
283  revoke_event_data = msgpackutils.loads(data, registry=self._registry)
284  try:
285  revoke_event = RevokeEvent(**revoke_event_data)
286  except Exception:
287  LOG.debug("Failed to deserialize RevokeEvent. Data is %s",
288  revoke_event_data)
289  raise
290  return revoke_event
291 
292 
293 cache.register_model_handler(_RevokeEventHandler)
keystone.models.revoke_model.RevokeEvent.domain_id
domain_id
Definition: revoke_model.py:86
keystone.models.revoke_model._RevokeEventHandler
Definition: revoke_model.py:269
keystone.models.revoke_model.RevokeEvent.issued_before
issued_before
Definition: revoke_model.py:99
keystone.models.revoke_model.build_token_values
def build_token_values(token)
Definition: revoke_model.py:207
keystone.models.revoke_model._RevokeEventHandler.deserialize
def deserialize(self, data)
Definition: revoke_model.py:282
keystone.models.revoke_model.blank_token_data
def blank_token_data(issued_at)
Definition: revoke_model.py:66
keystone.models.revoke_model.RevokeEvent.expires_at
expires_at
Definition: revoke_model.py:94
keystone.models.revoke_model.RevokeEvent.domain_scope_id
domain_scope_id
Definition: revoke_model.py:85
keystone.models.revoke_model._RevokeEventHandler._registry
_registry
Definition: revoke_model.py:277
keystone.models.revoke_model.RevokeEvent.to_dict
def to_dict(self)
Definition: revoke_model.py:101
keystone.models.revoke_model.RevokeEvent.__init__
def __init__(self, **kwargs)
Definition: revoke_model.py:78
keystone.models.revoke_model.is_revoked
def is_revoked(events, token_data)
Definition: revoke_model.py:129
keystone.models.revoke_model.RevokeEvent.revoked_at
revoked_at
Definition: revoke_model.py:97
keystone.models.revoke_model._RevokeEventHandler.__init__
def __init__(self, registry)
Definition: revoke_model.py:276
keystone.models.revoke_model._RevokeEventHandler.serialize
def serialize(self, obj)
Definition: revoke_model.py:279
keystone.models.revoke_model.matches
def matches(event, token_values)
Definition: revoke_model.py:151
keystone.models.revoke_model.RevokeEvent
Definition: revoke_model.py:77
keystone.common
Definition: __init__.py:1