diff options
| author | Steve Martinelli <stevemar@ca.ibm.com> | 2013-03-20 20:02:18 -0700 |
|---|---|---|
| committer | Dolph Mathews <dolph.mathews@gmail.com> | 2013-08-16 13:02:24 -0500 |
| commit | bcaa3072f37d3af3f9d526f18f311411ceeae160 (patch) | |
| tree | 8c08bffaabf6c8f195f3d87b9a26edcdaf287371 /keystone/token | |
| parent | 81534a182a4986d838591395aee8590ef61c599d (diff) | |
| download | keystone-bcaa3072f37d3af3f9d526f18f311411ceeae160.tar.gz keystone-bcaa3072f37d3af3f9d526f18f311411ceeae160.tar.xz keystone-bcaa3072f37d3af3f9d526f18f311411ceeae160.zip | |
Add delegated_auth support for keystone
Implements an OAuth 1.0a service provider.
blueprint: delegated-auth-via-oauth
DocImpact
SecurityImpact
Change-Id: Ib5561593ab608f3b22fbcd7196e2171f95b735e8
Diffstat (limited to 'keystone/token')
| -rw-r--r-- | keystone/token/backends/kvs.py | 28 | ||||
| -rw-r--r-- | keystone/token/backends/memcache.py | 10 | ||||
| -rw-r--r-- | keystone/token/backends/sql.py | 37 | ||||
| -rw-r--r-- | keystone/token/core.py | 20 | ||||
| -rw-r--r-- | keystone/token/providers/uuid.py | 31 |
5 files changed, 112 insertions, 14 deletions
diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py index 171d77df..b2c6ed30 100644 --- a/keystone/token/backends/kvs.py +++ b/keystone/token/backends/kvs.py @@ -90,6 +90,29 @@ class Token(kvs.Base, token.Driver): tokens.append(token.split('-', 1)[1]) return tokens + def _consumer_matches(self, consumer_id, token_ref_dict): + if consumer_id is None: + return True + else: + if 'token_data' in token_ref_dict: + token_data = token_ref_dict.get('token_data') + if 'token' in token_data: + token = token_data.get('token') + oauth = token.get('OS-OAUTH1') + if oauth and oauth.get('consumer_id') == consumer_id: + return True + return False + + def _list_tokens_for_consumer(self, consumer_id): + tokens = [] + now = timeutils.utcnow() + for token, ref in self.db.items(): + if not token.startswith('token-') or self.is_expired(now, ref): + continue + if self._consumer_matches(consumer_id, ref): + tokens.append(token.split('-', 1)[1]) + return tokens + def _list_tokens_for_user(self, user_id, tenant_id=None): def user_matches(user_id, ref): return ref.get('user') and ref['user'].get('id') == user_id @@ -110,9 +133,12 @@ class Token(kvs.Base, token.Driver): tokens.append(token.split('-', 1)[1]) return tokens - def list_tokens(self, user_id, tenant_id=None, trust_id=None): + def list_tokens(self, user_id, tenant_id=None, trust_id=None, + consumer_id=None): if trust_id: return self._list_tokens_for_trust(trust_id) + if consumer_id: + return self._list_tokens_for_consumer(consumer_id) else: return self._list_tokens_for_user(user_id, tenant_id) diff --git a/keystone/token/backends/memcache.py b/keystone/token/backends/memcache.py index d0d59eef..b80d01bc 100644 --- a/keystone/token/backends/memcache.py +++ b/keystone/token/backends/memcache.py @@ -178,7 +178,8 @@ class Token(token.Driver): self._add_to_revocation_list(data) return result - def list_tokens(self, user_id, tenant_id=None, trust_id=None): + def list_tokens(self, user_id, tenant_id=None, trust_id=None, + consumer_id=None): tokens = [] user_key = self._prefix_user_id(user_id) user_record = self.client.get(user_key) or "" @@ -199,6 +200,13 @@ class Token(token.Driver): continue if trust != trust_id: continue + if consumer_id is not None: + try: + oauth = token_ref['token_data']['token']['OS-OAUTH1'] + if oauth.get('consumer_id') != consumer_id: + continue + except KeyError: + continue tokens.append(token_id) return tokens diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py index 82eab651..5d24fb4f 100644 --- a/keystone/token/backends/sql.py +++ b/keystone/token/backends/sql.py @@ -78,7 +78,8 @@ class Token(sql.Base, token.Driver): token_ref.valid = False session.flush() - def delete_tokens(self, user_id, tenant_id=None, trust_id=None): + def delete_tokens(self, user_id, tenant_id=None, trust_id=None, + consumer_id=None): """Deletes all tokens in one session The user_id will be ignored if the trust_id is specified. user_id @@ -103,6 +104,11 @@ class Token(sql.Base, token.Driver): token_ref_dict = token_ref.to_dict() if not self._tenant_matches(tenant_id, token_ref_dict): continue + if consumer_id: + token_ref_dict = token_ref.to_dict() + if not self._consumer_matches(consumer_id, token_ref_dict): + continue + token_ref.valid = False session.flush() @@ -112,6 +118,13 @@ class Token(sql.Base, token.Driver): (token_ref_dict.get('tenant') and token_ref_dict['tenant'].get('id') == tenant_id)) + def _consumer_matches(self, consumer_id, token_ref_dict): + if consumer_id is None: + return True + else: + oauth = token_ref_dict['token_data']['token'].get('OS-OAUTH1', {}) + return oauth and oauth['consumer_id'] == consumer_id + def _list_tokens_for_trust(self, trust_id): session = self.get_session() tokens = [] @@ -141,9 +154,29 @@ class Token(sql.Base, token.Driver): tokens.append(token_ref['id']) return tokens - def list_tokens(self, user_id, tenant_id=None, trust_id=None): + def _list_tokens_for_consumer(self, user_id, consumer_id): + tokens = [] + session = self.get_session() + with session.begin(): + now = timeutils.utcnow() + query = session.query(TokenModel) + query = query.filter(TokenModel.expires > now) + query = query.filter(TokenModel.user_id == user_id) + token_references = query.filter_by(valid=True) + + for token_ref in token_references: + token_ref_dict = token_ref.to_dict() + if self._consumer_matches(consumer_id, token_ref_dict): + tokens.append(token_ref_dict['id']) + session.flush() + return tokens + + def list_tokens(self, user_id, tenant_id=None, trust_id=None, + consumer_id=None): if trust_id: return self._list_tokens_for_trust(trust_id) + if consumer_id: + return self._list_tokens_for_consumer(user_id, consumer_id) else: return self._list_tokens_for_user(user_id, tenant_id) diff --git a/keystone/token/core.py b/keystone/token/core.py index e8d04a7e..7eadbe63 100644 --- a/keystone/token/core.py +++ b/keystone/token/core.py @@ -174,41 +174,51 @@ class Driver(object): """ raise exception.NotImplemented() - def delete_tokens(self, user_id, tenant_id=None, trust_id=None): + def delete_tokens(self, user_id, tenant_id=None, trust_id=None, + consumer_id=None): """Deletes tokens by user. If the tenant_id is not None, only delete the tokens by user id under the specified tenant. If the trust_id is not None, it will be used to query tokens and the user_id will be ignored. + If the consumer_id is not None, only delete the tokens by consumer id + that match the specified consumer id :param user_id: identity of user :type user_id: string :param tenant_id: identity of the tenant :type tenant_id: string - :param trust_id: identified of the trust + :param trust_id: identity of the trust :type trust_id: string + :param consumer_id: identity of the consumer + :type consumer_id: string :returns: None. :raises: keystone.exception.TokenNotFound """ token_list = self.list_tokens(user_id, tenant_id=tenant_id, - trust_id=trust_id) + trust_id=trust_id, + consumer_id=consumer_id) + for token in token_list: try: self.delete_token(token) except exception.NotFound: pass - def list_tokens(self, user_id, tenant_id=None, trust_id=None): + def list_tokens(self, user_id, tenant_id=None, trust_id=None, + consumer_id=None): """Returns a list of current token_id's for a user :param user_id: identity of the user :type user_id: string :param tenant_id: identity of the tenant :type tenant_id: string - :param trust_id: identified of the trust + :param trust_id: identity of the trust :type trust_id: string + :param consumer_id: identity of the consumer + :type consumer_id: string :returns: list of token_id's """ diff --git a/keystone/token/providers/uuid.py b/keystone/token/providers/uuid.py index acfa9372..612df999 100644 --- a/keystone/token/providers/uuid.py +++ b/keystone/token/providers/uuid.py @@ -18,6 +18,7 @@ from __future__ import absolute_import +import json import sys import uuid @@ -206,12 +207,23 @@ class V3TokenDataHelper(object): 'domain': self._get_filtered_domain(user_ref['domain_id'])} token_data['user'] = filtered_user + def _populate_oauth_section(self, token_data, access_token): + if access_token: + access_token_id = access_token['id'] + consumer_id = access_token['consumer_id'] + token_data['OS-OAUTH1'] = ({'access_token_id': access_token_id, + 'consumer_id': consumer_id}) + def _populate_roles(self, token_data, user_id, domain_id, project_id, - trust): + trust, access_token): if 'roles' in token_data: # no need to repopulate roles return + if access_token: + token_data['roles'] = json.loads(access_token['requested_roles']) + return + if CONF.trust.enabled and trust: token_user_id = trust['trustor_user_id'] token_project_id = trust['project_id'] @@ -288,7 +300,7 @@ class V3TokenDataHelper(object): def get_token_data(self, user_id, method_names, extras, domain_id=None, project_id=None, expires=None, trust=None, token=None, include_catalog=True, - bind=None): + bind=None, access_token=None): token_data = {'methods': method_names, 'extras': extras} @@ -307,15 +319,17 @@ class V3TokenDataHelper(object): self._populate_scope(token_data, domain_id, project_id) self._populate_user(token_data, user_id, domain_id, project_id, trust) - self._populate_roles(token_data, user_id, domain_id, project_id, trust) + self._populate_roles(token_data, user_id, domain_id, project_id, trust, + access_token) if include_catalog: self._populate_service_catalog(token_data, user_id, domain_id, project_id, trust) self._populate_token_dates(token_data, expires=expires, trust=trust) + self._populate_oauth_section(token_data, access_token) return {'token': token_data} -@dependency.requires('token_api', 'identity_api', 'catalog_api') +@dependency.requires('token_api', 'identity_api', 'catalog_api', 'oauth_api') class Provider(token.provider.Provider): def __init__(self, *args, **kwargs): super(Provider, self).__init__(*args, **kwargs) @@ -380,6 +394,12 @@ class Provider(token.provider.Provider): if (CONF.trust.enabled and not trust and metadata_ref and 'trust_id' in metadata_ref): trust = self.trust_api.get_trust(metadata_ref['trust_id']) + + access_token = None + if 'oauth1' in method_names: + access_token_id = auth_context['access_token_id'] + access_token = self.oauth_api.get_access_token(access_token_id) + token_data = self.v3_token_data_helper.get_token_data( user_id, method_names, @@ -389,7 +409,8 @@ class Provider(token.provider.Provider): expires=expires_at, trust=trust, bind=auth_context.get('bind') if auth_context else None, - include_catalog=include_catalog) + include_catalog=include_catalog, + access_token=access_token) token_id = self._get_token_id(token_data) try: |
