diff options
Diffstat (limited to 'keystone/middleware/auth_token.py')
-rw-r--r-- | keystone/middleware/auth_token.py | 196 |
1 files changed, 113 insertions, 83 deletions
diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py index 1dd437c82..c323565e2 100644 --- a/keystone/middleware/auth_token.py +++ b/keystone/middleware/auth_token.py @@ -120,9 +120,9 @@ class AuthProtocol(object): # where to tell clients to find the auth service (default to url # constructed based on endpoint we have for the service to use) self.auth_location = conf.get('auth_uri', - '%s://%s:%s' % (self.auth_protocol, - self.auth_host, - self.auth_port)) + '%s://%s:%s' % (self.auth_protocol, + self.auth_host, + self.auth_port)) # Credentials used to verify this component with the Auth service since # validating tokens is a privileged call @@ -143,7 +143,7 @@ class AuthProtocol(object): #Prep headers to forward request to local or remote downstream service proxy_headers = env.copy() for header in proxy_headers.iterkeys(): - if header[0:5] == 'HTTP_': + if header.startswith('HTTP_'): proxy_headers[header[5:]] = proxy_headers[header] del proxy_headers[header] @@ -155,7 +155,9 @@ class AuthProtocol(object): #Configured to allow downstream service to make final decision. #So mark status as Invalid and forward the request downstream self._decorate_request('X_IDENTITY_STATUS', - 'Invalid', env, proxy_headers) + 'Invalid', + env, + proxy_headers) else: #Respond to client as appropriate for this auth protocol return self._reject_request(env, start_response) @@ -167,13 +169,17 @@ class AuthProtocol(object): if self.delay_auth_decision: # Downstream service will receive call still and decide self._decorate_request('X_IDENTITY_STATUS', - 'Invalid', env, proxy_headers) + 'Invalid', + env, + proxy_headers) else: #Respond to client as appropriate for this auth protocol return self._reject_claims(env, start_response) else: self._decorate_request('X_IDENTITY_STATUS', - 'Confirmed', env, proxy_headers) + 'Confirmed', + env, + proxy_headers) #Collect information about valid claims if valid: @@ -181,34 +187,44 @@ class AuthProtocol(object): # Store authentication data if claims: - self._decorate_request('X_AUTHORIZATION', 'Proxy %s' % - claims['user'], env, proxy_headers) + self._decorate_request('X_AUTHORIZATION', + 'Proxy %s' % claims['user'], + env, + proxy_headers) # For legacy compatibility before we had ID and Name self._decorate_request('X_TENANT', - claims['tenant'], env, proxy_headers) + claims['tenant'], + env, + proxy_headers) # Services should use these self._decorate_request('X_TENANT_NAME', - claims.get('tenantName', claims['tenant']), - env, proxy_headers) + claims.get('tenantName', + claims['tenant']), + env, + proxy_headers) self._decorate_request('X_TENANT_ID', - claims['tenant'], env, proxy_headers) + claims['tenant'], + env, + proxy_headers) self._decorate_request('X_USER', - claims['userName'], env, proxy_headers) + claims['userName'], + env, + proxy_headers) self._decorate_request('X_USER_ID', - claims['user'], env, proxy_headers) - - if 'roles' in claims and len(claims['roles']) > 0: - if claims['roles'] != None: - roles = '' - for role in claims['roles']: - if len(roles) > 0: - roles += ',' - roles += role - self._decorate_request('X_ROLE', - roles, env, proxy_headers) + claims['user'], + env, + proxy_headers) + + # NOTE(lzyeval): claims has a key 'roles' which is + # guaranteed to be a list (see note below) + roles = ','.join(filter(lambda x: x, claims['roles'])) + self._decorate_request('X_ROLE', + roles, + env, + proxy_headers) # NOTE(todd): unused self.expanded = True @@ -223,15 +239,15 @@ class AuthProtocol(object): def _reject_request(self, env, start_response): """Redirect client to auth server""" - return webob.exc.HTTPUnauthorized('Authentication required', - [('WWW-Authenticate', - "Keystone uri='%s'" % self.auth_location)])(env, - start_response) + headers = [('WWW-Authenticate', + "Keystone uri='%s'" % self.auth_location)] + resp = webob.exc.HTTPUnauthorized('Authentication required', headers) + return resp(env, start_response) def _reject_claims(self, env, start_response): """Client sent bad claims""" - return webob.exc.HTTPUnauthorized()(env, - start_response) + resp = webob.exc.HTTPUnauthorized() + return resp(env, start_response) def _get_admin_auth_token(self, username, password): """ @@ -241,23 +257,27 @@ class AuthProtocol(object): """ headers = { "Content-type": "application/json", - "Accept": "application/json"} + "Accept": "application/json", + } params = { - "auth": { - "passwordCredentials": { + "auth": { + "passwordCredentials": { "username": username, "password": password, }, - "tenantName": ADMIN_TENANTNAME, - } - } + "tenantName": ADMIN_TENANTNAME, + } + } if self.auth_protocol == "http": conn = httplib.HTTPConnection(self.auth_host, self.auth_port) else: - conn = httplib.HTTPSConnection(self.auth_host, self.auth_port, - cert_file=self.cert_file) - conn.request("POST", '/v2.0/tokens', json.dumps(params), - headers=headers) + conn = httplib.HTTPSConnection(self.auth_host, + self.auth_port, + cert_file=self.cert_file) + conn.request("POST", + '/v2.0/tokens', + json.dumps(params), + headers=headers) response = conn.getresponse() data = response.read() conn.close() @@ -278,16 +298,21 @@ class AuthProtocol(object): # Step 2: validate the user's token with the auth service # since this is a priviledged op,m we need to auth ourselves # by using an admin token - headers = {'Content-type': 'application/json', - 'Accept': 'application/json', - 'X-Auth-Token': self.admin_token} - ##TODO(ziad):we need to figure out how to auth to keystone - #since validate_token is a priviledged call - #Khaled's version uses creds to get a token - # 'X-Auth-Token': admin_token} - # we're using a test token from the ini file for now - conn = http_connect(self.auth_host, self.auth_port, 'GET', - '/v2.0/tokens/%s' % claims, headers=headers) + headers = { + 'Content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': self.admin_token, + } + ##TODO(ziad):we need to figure out how to auth to keystone + #since validate_token is a priviledged call + #Khaled's version uses creds to get a token + # 'X-Auth-Token': admin_token} + # we're using a test token from the ini file for now + conn = http_connect(self.auth_host, + self.auth_port, + 'GET', + '/v2.0/tokens/%s' % claims, + headers=headers) resp = conn.getresponse() # data = resp.read() conn.close() @@ -307,16 +332,21 @@ class AuthProtocol(object): def _expound_claims(self, claims): # Valid token. Get user data and put it in to the call # so the downstream service can use it - headers = {'Content-type': 'application/json', - 'Accept': 'application/json', - 'X-Auth-Token': self.admin_token} - ##TODO(ziad):we need to figure out how to auth to keystone - #since validate_token is a priviledged call - #Khaled's version uses creds to get a token - # 'X-Auth-Token': admin_token} - # we're using a test token from the ini file for now - conn = http_connect(self.auth_host, self.auth_port, 'GET', - '/v2.0/tokens/%s' % claims, headers=headers) + headers = { + 'Content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': self.admin_token, + } + ##TODO(ziad):we need to figure out how to auth to keystone + #since validate_token is a priviledged call + #Khaled's version uses creds to get a token + # 'X-Auth-Token': admin_token} + # we're using a test token from the ini file for now + conn = http_connect(self.auth_host, + self.auth_port, + 'GET', + '/v2.0/tokens/%s' % claims, + headers=headers) resp = conn.getresponse() data = resp.read() conn.close() @@ -325,28 +355,28 @@ class AuthProtocol(object): raise LookupError('Unable to locate claims: %s' % resp.status) token_info = json.loads(data) - roles = [] - role_refs = token_info['access']['user']['roles'] - if role_refs != None: - for role_ref in role_refs: - # Nova looks for the non case-sensitive role 'admin' - # to determine admin-ness - roles.append(role_ref['name']) + access_user = token_info['access']['user'] + access_token = token_info['access']['token'] + # Nova looks for the non case-sensitive role 'admin' + # to determine admin-ness + # NOTE(lzyeval): roles is always a list + roles = map(lambda y: y['name'], access_user.get('roles', [])) try: - tenant = token_info['access']['token']['tenant']['id'] - tenant_name = token_info['access']['token']['tenant']['name'] + tenant = access_token['tenant']['id'] + tenant_name = access_token['tenant']['name'] except: tenant = None tenant_name = None if not tenant: - tenant = token_info['access']['user'].get('tenantId') - tenant_name = token_info['access']['user'].get('tenantName') + tenant = access_user.get('tenantId') + tenant_name = access_user.get('tenantName') verified_claims = { - 'user': token_info['access']['user']['id'], - 'userName': token_info['access']['user']['username'], - 'tenant': tenant, - 'roles': roles} + 'user': access_user['id'], + 'userName': access_user['username'], + 'tenant': tenant, + 'roles': roles, + } if tenant_name: verified_claims['tenantName'] = tenant_name return verified_claims @@ -385,7 +415,7 @@ class AuthProtocol(object): if resp.status == 401 or resp.status == 305: # Add our own headers to the list headers = [('WWW_AUTHENTICATE', - "Keystone uri='%s'" % self.auth_location)] + "Keystone uri='%s'" % self.auth_location)] return webob.Response(status=resp.status, body=data, headerlist=headers)(env, start_response) @@ -410,10 +440,10 @@ def app_factory(global_conf, **local_conf): return AuthProtocol(None, conf) if __name__ == '__main__': - app = deploy.loadapp('config:' + \ - os.path.join(os.path.abspath(os.path.dirname(__file__)), - os.pardir, - os.pardir, - 'examples/paste/auth_token.ini'), - global_conf={'log_name': 'auth_token.log'}) + app_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), + os.pardir, + os.pardir, + 'examples/paste/auth_token.ini') + app = deploy.loadapp('config:%s' % app_path, + global_conf={'log_name': 'auth_token.log'}) wsgi.server(eventlet.listen(('', 8090)), app) |