def _validate_claims(self, claims): """Ask keystone (as keystone admin) for information for this user.""" # TODO(todd): cache self.log.debug('Asking keystone to validate token') headers = {"Content-type": "application/json", "Accept": "text/json", "X-Auth-Token": self.admin_token} self.log.debug('headers: %r', headers) self.log.debug('url: %s', self.keystone_url) conn = http_connect(self.keystone_url.hostname, self.keystone_url.port, 'GET', '/v2.0/tokens/%s' % claims, headers=headers) resp = conn.getresponse() data = resp.read() conn.close() # Check http status code for the "OK" family of responses if not str(resp.status).startswith('20'): return False identity_info = json.loads(data) roles = [] role_refs = identity_info["auth"]["user"]["roleRefs"] if role_refs is not None: for role_ref in role_refs: roles.append(role_ref["roleId"]) # TODO(Ziad): add groups back in identity = {'user': identity_info['auth']['user']['username'], 'tenant': identity_info['auth']['user']['tenantId'], 'roles':roles} return identity
def _forward_request(self): """Token/Auth processed & claims added to headers""" self._decorate_request('AUTHORIZATION', "Basic %s" % self.service_pass) #now decide how to pass on the call if self.app: # Pass to downstream WSGI component return self.app(self.env, self.start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) req = Request(self.proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, self.proxy_headers, ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now return Response(status=resp.status, body=data)(self.proxy_headers, self.start_response)
def __call__(self, env, start_response): def custom_start_response(status, headers): if self.delay_auth_decision: headers.append(('WWW-Authenticate', "Basic realm='API Realm'")) return start_response(status, headers) #TODO(Rasib): PERFORM OPENID AUTH #Auth processed, headers added now decide how to pass on the call if self.app: # Pass to downstream WSGI component env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass return self.app(env, custom_start_response) proxy_headers = [] proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass # We are forwarding to a remote service (no downstream WSGI app) req = Request(proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, \ req.method, parsed.path, \ proxy_headers, \ ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now return Response(status=resp.status, body=data)(env, start_response)
def _validate_claims(self, claims): """Validate claims, and provide identity information isf applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token #TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini #auth = self.get_admin_auth_token("admin", "secrete", "1") #admin_token = json.loads(auth)["auth"]["token"]["id"] # 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": "text/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() if not str(resp.status).startswith('20'): # Keystone rejected claim return False else: #TODO(Ziad): there is an optimization we can do here. We have just #received data from Keystone that we can use instead of making #another call in _expound_claims return True
def _expound_claims(self): # 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": "text/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' % self.claims, headers=headers) resp = conn.getresponse() data = resp.read() conn.close() if not str(resp.status).startswith('20'): raise LookupError('Unable to locate claims: %s' % resp.status) token_info = json.loads(data) roles = [] role_refs = token_info["auth"]["user"]["roleRefs"] if role_refs != None: for role_ref in role_refs: roles.append(role_ref["roleId"]) verified_claims = {'user': token_info['auth']['user']['username'], 'tenant': token_info['auth']['user']['tenantId'], 'roles': roles} return verified_claims
def _expound_claims(self): # 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": "text/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', '/v1.0/token/%s' % self.claims, headers=headers) resp = conn.getresponse() data = resp.read() conn.close() if not str(resp.status).startswith('20'): raise LookupError('Unable to locate claims: %s' % resp.status) token_info = json.loads(data) #TODO(Ziad): make this more robust first_group = token_info['auth']['user']['groups']['group'][0] verified_claims = {'user': token_info['auth']['user']['username'], 'tenant': token_info['auth']['user']['tenantId'], 'group': '%s/%s' % (first_group['id'], first_group['tenantId'])} return verified_claims
def _expound_claims(self): # 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} conn = http_connect(self.auth_host, self.auth_port, 'GET', self._build_token_uri(self.claims), headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() # pylint: disable=E1103 conn.close() if not str(resp.status).startswith('20'): raise LookupError('Unable to locate claims: %s' % resp.status) token_info = json.loads(data) #TODO(Ziad): make this more robust #first_group = token_info['auth']['user']['groups']['group'][0] roles = [] rolegrants = token_info["access"]["user"]["roles"] if rolegrants is not None: roles = [rolegrant["id"] for rolegrant in rolegrants] token_info = json.loads(data) roles = [role['name'] for role in token_info[ "access"]["user"]["roles"]] # in diablo, there were two ways to get tenant data tenant = token_info['access']['token'].get('tenant') if tenant: # post diablo tenant_id = tenant['id'] tenant_name = tenant['name'] else: # diablo only tenant_id = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') verified_claims = { 'user': { 'id': token_info['access']['user']['id'], 'name': token_info['access']['user']['name'], }, 'tenant': { 'id': tenant_id, 'name': tenant_name }, 'roles': roles} return verified_claims
def _validate_claims(self, claims, retry=False): """Validate claims, and provide identity information if applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token # TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini # NOTE(salvatore-orlando): Temporarily restoring auth token retrieval, # with credentials in configuration file if not self.admin_token: auth = self.get_admin_auth_token(self.admin_user, self.admin_password) self.admin_token = json.loads(auth)["access"]["token"]["id"] # 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 } conn = http_connect(self.auth_host, self.auth_port, 'GET', self._build_token_uri(claims), headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() # pylint: disable=E1103 conn.close() if not str(resp.status).startswith('20'): # Keystone rejected claim # In case a 404 error it might just be that the token has expired # Therefore try and get a new token # of course assuming admin credentials have been specified # Note(salvatore-orlando): the 404 here is not really # what should be returned if self.admin_user and self.admin_password and \ not retry and str(resp.status) == '404': LOG.warn("Unable to validate token." + "Admin token possibly expired.") self.admin_token = None return self._validate_claims(claims, True) return False else: #TODO(Ziad): there is an optimization we can do here. We have just #received data from Keystone that we can use instead of making #another call in _expound_claims LOG.info("Claims successfully validated") return True
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) resp = conn.getresponse() data = resp.read() conn.close() if not str(resp.status).startswith('20'): 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: # Engine looks for the non case-sensitive role 'Admin' # to determine admin-ness roles.append(role_ref["name"]) try: tenant = token_info['access']['token']['tenant']['id'] tenant_name = token_info['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') verified_claims = { 'user': token_info['access']['user']['username'], 'tenant': tenant, 'roles': roles } if tenant_name: verified_claims['tenantName'] = tenant_name return verified_claims
def _validate_claims(self, claims, retry=False): """Validate claims, and provide identity information if applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token # TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini # NOTE(salvatore-orlando): Temporarily restoring auth token retrieval, # with credentials in configuration file if not self.admin_token: auth = self.get_admin_auth_token(self.admin_user, self.admin_password) self.admin_token = json.loads(auth)["access"]["token"]["id"] # 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} conn = http_connect(self.auth_host, self.auth_port, 'GET', self._build_token_uri(claims), headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() # pylint: disable=E1103 conn.close() if not str(resp.status).startswith('20'): # Keystone rejected claim # In case a 404 error it might just be that the token has expired # Therefore try and get a new token # of course assuming admin credentials have been specified # Note(salvatore-orlando): the 404 here is not really # what should be returned if self.admin_user and self.admin_password and \ not retry and str(resp.status) == '404': LOG.warn("Unable to validate token." + "Admin token possibly expired.") self.admin_token = None return self._validate_claims(claims, True) return False else: #TODO(Ziad): there is an optimization we can do here. We have just #received data from Keystone that we can use instead of making #another call in _expound_claims LOG.info("Claims successfully validated") return True
def _validate_claims(self, claims): """Ask keystone (as keystone admin) for information for this user.""" # TODO(todd): cache self.log.debug('Asking keystone to validate token') headers = { "Content-type": "application/json", "Accept": "application/json", "X-Auth-Token": self.admin_token } self.log.debug('headers: %r', headers) self.log.debug('url: %s', self.keystone_url) conn = http_connect(self.keystone_url.hostname, self.keystone_url.port, 'GET', '/v2.0/tokens/%s' % claims, headers=headers) resp = conn.getresponse() data = resp.read() conn.close() # Check http status code for the "OK" family of responses if not str(resp.status).startswith('20'): return False identity_info = json.loads(data) roles = [] role_refs = identity_info["access"]["user"]["roles"] if role_refs is not None: for role_ref in role_refs: roles.append(role_ref["id"]) try: tenant = identity_info['access']['token']['tenantId'] except: tenant = None if not tenant: tenant = identity_info['access']['user']['tenantId'] # TODO(Ziad): add groups back in identity = { 'user': identity_info['access']['user']['username'], 'tenant': tenant, 'roles': roles } return identity
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) resp = conn.getresponse() data = resp.read() conn.close() if not str(resp.status).startswith('20'): 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']) try: tenant = token_info['access']['token']['tenant']['id'] tenant_name = token_info['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') verified_claims = { 'user': token_info['access']['user']['id'], 'userName': token_info['access']['user']['username'], 'tenant': tenant, 'roles': roles} if tenant_name: verified_claims['tenantName'] = tenant_name return verified_claims
def _supports_osksvalidate(self): """Check if target Keystone server supports OS-KSVALIDATE.""" if self.tested_for_osksvalidate: return self.osksvalidate headers = {"Accept": "application/json"} logger.debug("Connecting to %s://%s:%s to check extensions" % (self.auth_protocol, self.auth_host, self.auth_port)) try: self.last_test_for_osksvalidate = time.time() conn = http_connect(self.auth_host, self.auth_port, 'GET', '/v2.0/extensions/', headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() logger.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): logger.debug("Failed to detect extensions. " "Falling back to core API") return False except EnvironmentError as exc: if exc.errno == errno.ECONNREFUSED: logger.warning("Keystone server not responding. Extension " "detection will be retried later.") else: logger.exception("Unexpected error trying to detect " "extensions.") logger.debug("Falling back to core API behavior (using tokens in " "URL)") return False except httplib.HTTPException as exc: logger.exception("Error trying to detect extensions.") logger.debug("Falling back to core API behavior (using tokens in " "URL)") return False self.tested_for_osksvalidate = True return "OS-KSVALIDATE" in data
def _supports_osksvalidate(self): """Check if target Keystone server supports OS-KSVALIDATE.""" if self.tested_for_osksvalidate: return self.osksvalidate headers = {"Accept": "application/json"} logger.debug("Connecting to %s://%s:%s to check extensions" % ( self.auth_protocol, self.auth_host, self.auth_port)) try: self.last_test_for_osksvalidate = time.time() conn = http_connect(self.auth_host, self.auth_port, 'GET', '/v2.0/extensions/', headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() logger.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): logger.debug("Failed to detect extensions. " "Falling back to core API") return False except EnvironmentError as exc: if exc.errno == errno.ECONNREFUSED: logger.warning("Keystone server not responding. Extension " "detection will be retried later.") else: logger.exception("Unexpected error trying to detect " "extensions.") logger.debug("Falling back to core API behavior (using tokens in " "URL)") return False except httplib.HTTPException as exc: logger.exception("Error trying to detect extensions.") logger.debug("Falling back to core API behavior (using tokens in " "URL)") return False self.tested_for_osksvalidate = True return "OS-KSVALIDATE" in data
def _forward_request(self, env, start_response, proxy_headers): """Token/Auth processed & claims added to headers""" self._decorate_request('AUTHORIZATION', "Basic %s" % self.service_pass, env, proxy_headers) #now decide how to pass on the call if self.app: # Pass to downstream WSGI component logger.debug("Sending request to next app in WSGI pipeline") return self.app(env, start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) logger.debug("Sending request to %s" % self.service_url) req = Request(proxy_headers) parsed = urlparse(req.url) # pylint: disable=E1101 conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, proxy_headers, ssl=(self.service_protocol == 'https'), timeout=self.service_timeout) resp = conn.getresponse() data = resp.read() logger.debug("Response was %s" % resp.status) #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now if resp.status in (401, 305): # Add our own headers to the list headers = [("WWW_AUTHENTICATE", "Keystone uri='%s'" % self.auth_location)] return Response(status=resp.status, body=data, headerlist=headers)(env, start_response) else: return Response(status=resp.status, body=data)(env, start_response)
def _forward_request(self): """Token/Auth processed & claims added to headers""" #now decide how to pass on the call if self.app: # Pass to downstream WSGI component return self.app(self.env, self.start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) req = Request(self.proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, self.proxy_headers, ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() return Response(status=resp.status, body=data)(self.proxy_headers, self.start_response)
def _validate_claims(self, claims): """Validate claims, and provide identity information isf applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token #TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini #auth = self.get_admin_auth_token("admin", "secrete", "1") #admin_token = json.loads(auth)["auth"]["token"]["id"] # 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) resp = conn.getresponse() # data = resp.read() conn.close() if not str(resp.status).startswith('20'): # Keystone rejected claim return False else: #TODO(Ziad): there is an optimization we can do here. We have just #received data from Keystone that we can use instead of making #another call in _expound_claims return True
def _forward_request(self): """Token/Auth processed & claims added to headers""" #now decide how to pass on the call if self.app: # Pass to downstream WSGI component return self.app(self.env, self.start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) req = Request(self.proxy_headers) # pylint: disable=E1101 parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, self.proxy_headers, ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() return Response(status=resp.status, body=data)(self.proxy_headers, self.start_response)
def _validate_claims(self, claims): """Ask keystone (as keystone admin) for information for this user.""" # TODO(todd): cache self.log.debug("Asking keystone to validate token") headers = {"Content-type": "application/json", "Accept": "application/json", "X-Auth-Token": self.admin_token} self.log.debug("headers: %r", headers) self.log.debug("url: %s", self.keystone_url) conn = http_connect( self.keystone_url.hostname, self.keystone_url.port, "GET", "/v2.0/tokens/%s" % claims, headers=headers ) resp = conn.getresponse() data = resp.read() conn.close() # Check http status code for the "OK" family of responses if not str(resp.status).startswith("20"): return False identity_info = json.loads(data) roles = [] role_refs = identity_info["access"]["user"]["roles"] if role_refs is not None: for role_ref in role_refs: roles.append(role_ref["id"]) try: tenant = identity_info["access"]["token"]["tenantId"] except: tenant = None if not tenant: tenant = identity_info["access"]["user"]["tenantId"] # TODO(Ziad): add groups back in identity = {"user": identity_info["access"]["user"]["username"], "tenant": tenant, "roles": roles} return identity
def _validate_claims(self, claims, retry=True): """Validate claims, and provide identity information isf applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token if not self.admin_token: self.admin_token = self._get_admin_auth_token(self.admin_user, self.admin_password) # 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) resp = conn.getresponse() # data = resp.read() conn.close() if not str(resp.status).startswith('20'): if retry: self.admin_token = None return self._validate_claims(claims, False) else: return False else: #TODO(Ziad): there is an optimization we can do here. We have just #received data from Keystone that we can use instead of making #another call in _expound_claims return True
def _forward_request(self, env, start_response, proxy_headers): """Token/Auth processed & claims added to headers""" self._decorate_request('AUTHORIZATION', "Basic %s" % self.service_pass, env, proxy_headers) #now decide how to pass on the call if self.app: # Pass to downstream WSGI component return self.app(env, start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) req = webob.Request(proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, proxy_headers, ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now if resp.status == 401 or resp.status == 305: # Add our own headers to the list headers = [("WWW_AUTHENTICATE", "Keystone uri='%s'" % self.auth_location)] return webob.Response(status=resp.status, body=data, headerlist=headers)(env, start_response) else: return webob.Response(status=resp.status, body=data)(env, start_response)
def _expound_claims(self): # 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": "text/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" % self.claims, headers=headers) resp = conn.getresponse() data = resp.read() conn.close() if not str(resp.status).startswith("20"): raise LookupError("Unable to locate claims: %s" % resp.status) token_info = json.loads(data) # TODO(Ziad): make this more robust # first_group = token_info['auth']['user']['groups']['group'][0] roles = [] role_refs = token_info["auth"]["user"]["roleRefs"] if role_refs != None: for role_ref in role_refs: roles.append(role_ref["roleId"]) verified_claims = { "user": token_info["auth"]["user"]["username"], "tenant": token_info["auth"]["user"]["tenantId"], "roles": roles, } # TODO(Ziad): removed groups for now # ,'group': '%s/%s' % (first_group['id'], # first_group['tenantId'])} return verified_claims
def _forward_request(self, env, start_response, proxy_headers): """Token/Auth processed & claims added to headers""" self._decorate_request('AUTHORIZATION', 'Basic %s' % self.service_pass, env, proxy_headers) #now decide how to pass on the call if self.app: # Pass to downstream WSGI component return self.app(env, start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) req = webob.Request(proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, proxy_headers, ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now if resp.status == 401 or resp.status == 305: # Add our own headers to the list headers = [('WWW_AUTHENTICATE', "Keystone uri='%s'" % self.auth_location)] return webob.Response(status=resp.status, body=data, headerlist=headers)(env, start_response) else: return webob.Response(status=resp.status, body=data)(env, start_response)
def __call__(self, env, start_response): def custom_start_response(status, headers): if self.delay_auth_decision: headers.append(('WWW-Authenticate', "Basic realm='Use guest/guest'")) return start_response(status, headers) #Prep headers to proxy request to remote service proxy_headers = env.copy() user = '' #Look for authentication if 'HTTP_AUTHORIZATION' not in env: #No credentials were provided if self.delay_auth_decision: _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", proxy_headers, env) else: # If the user isn't authenticated, we reject the request and # return 401 indicating we need Basic Auth credentials. ret = HTTPUnauthorized("Authentication required", [('WWW-Authenticate', 'Basic realm="Use guest/guest"')]) return ret(env, start_response) else: # Claims were provided - validate them import base64 auth_header = env['HTTP_AUTHORIZATION'] _auth_type, encoded_creds = auth_header.split(None, 1) user, password = base64.b64decode(encoded_creds).split(':', 1) if not self.validateCreds(user, password): #Claims were rejected if not self.delay_auth_decision: # Reject request (or ask for valid claims) ret = HTTPUnauthorized("Authentication required", [('WWW-Authenticate', 'Basic realm="Use guest/guest"')]) return ret(env, start_response) else: # Claims are valid, forward request _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", proxy_headers, env) # TODO(Ziad): add additional details we may need, # like tenant and group info _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user, proxy_headers, env) _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed", proxy_headers, env) _decorate_request_headers('X_TENANT', 'blank', proxy_headers, env) #Auth processed, headers added now decide how to pass on the call if self.app: # Pass to downstream WSGI component env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass return self.app(env, custom_start_response) proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass # We are forwarding to a remote service (no downstream WSGI app) req = Request(proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, \ req.method, parsed.path, \ proxy_headers, \ ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now return Response(status=resp.status, body=data)(env, start_response)
def _verify_claims(self, env, claims): """Verify claims and extract identity information, if applicable.""" cached_claims = self._cache_get(env, claims) if cached_claims: LOG.debug("Found cached claims") claims, expires, valid = cached_claims if not valid: LOG.debug("Claims not valid (according to cache)") raise ValidationFailed() if expires <= datetime.now(): LOG.debug("Claims (token) expired (according to cache)") raise TokenExpired() return claims # Step 1: We need to auth with the keystone service, so get an # admin token #TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini #auth = self.get_admin_auth_token("admin", "secrete", "1") #admin_token = json.loads(auth)["auth"]["token"]["id"] # 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 LOG.debug("Connecting to %s://%s:%s to check claims" % (self.auth_protocol, self.auth_host, self.auth_port)) conn = http_connect(self.auth_host, self.auth_port, 'GET', '/v2.0/tokens/%s%s' % (claims, self.serviceId_qs), headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() LOG.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): # Cache it if there is a cache available if self.cache: LOG.debug("Caching that results were invalid") self._cache_put(env, claims, claims={ 'expires': datetime.strftime(datetime.now(), EXPIRE_TIME_FORMAT) }, valid=False) # Keystone rejected claim LOG.debug("Failing the validation") raise ValidationFailed() token_info = json.loads(data) roles = [ role['name'] for role in token_info["access"]["user"]["roles"] ] # in diablo, there were two ways to get tenant data tenant = token_info['access']['token'].get('tenant') if tenant: # post diablo tenant_id = tenant['id'] tenant_name = tenant['name'] else: # diablo only tenant_id = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') LOG.debug("Tenant identified: id=%s, name=%s" % (tenant_id, tenant_name)) verified_claims = { 'user': { 'id': token_info['access']['user']['id'], 'name': token_info['access']['user']['name'], }, 'tenant': { 'id': tenant_id, 'name': tenant_name }, 'roles': roles, 'expires': token_info['access']['token']['expires'] } LOG.debug("User identified: id=%s, name=%s" % (token_info['access']['user']['id'], token_info['access']['user']['name'])) expires = get_datetime(verified_claims['expires']) if expires <= datetime.now(): LOG.debug("Claims (token) expired: %s" % str(expires)) # Cache it if there is a cache available (we also cached bad # claims) if self.cache: LOG.debug("Caching expired claim (token)") self._cache_put(env, claims, verified_claims, valid=False) raise TokenExpired() # Cache it if there is a cache available if self.cache: LOG.debug("Caching validated claim") self._cache_put(env, claims, verified_claims, valid=True) LOG.debug("Returning successful validation") return verified_claims
def _verify_claims(self, claims): """Verify claims and extract identity information, if applicable.""" # Step 1: We need to auth with the keystone service, so get an # admin token #TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini #auth = self.get_admin_auth_token("admin", "secrete", "1") #admin_token = json.loads(auth)["auth"]["token"]["id"] # 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, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() conn.close() if not str(resp.status).startswith('20'): # Keystone rejected claim raise ValidationFailed() token_info = json.loads(data) roles = [role['name'] for role in token_info[ "access"]["user"]["roles"]] # in diablo, there were two ways to get tenant data tenant = token_info['access']['token'].get('tenant') if tenant: # post diablo tenant_id = tenant['id'] tenant_name = tenant['name'] else: # diablo only tenant_id = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') verified_claims = { 'user': { 'id': token_info['access']['user']['id'], 'name': token_info['access']['user']['name'], }, 'tenant': { 'id': tenant_id, 'name': tenant_name }, 'roles': roles, 'expires': token_info['access']['token']['expires']} return verified_claims
def __call__(self, env, start_response): def custom_start_response(status, headers): if self.delay_auth_decision: headers.append(('WWW-Authenticate', "Basic realm='Use guest/guest'")) return start_response(status, headers) #Prep headers to proxy request to remote service proxy_headers = env.copy() user = '' #Look for authentication if 'HTTP_AUTHORIZATION' not in env: #No credentials were provided if self.delay_auth_decision: _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", proxy_headers, env) else: # If the user isn't authenticated, we reject the request and # return 401 indicating we need Basic Auth credentials. return HTTPUnauthorized("Authentication required", [('WWW-Authenticate', 'Basic realm="Use guest/guest"')])\ (env, start_response) else: # Claims were provided - validate them import base64 auth_header = env['HTTP_AUTHORIZATION'] _auth_type, encoded_creds = auth_header.split(None, 1) user, password = base64.b64decode(encoded_creds).split(':', 1) if not self.validateCreds(user, password): #Claims were rejected if not self.delay_auth_decision: # Reject request (or ask for valid claims) return HTTPUnauthorized("Authentication required", [('WWW-Authenticate', 'Basic realm="Use guest/guest"')])\ (env, start_response) else: # Claims are valid, forward request _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", proxy_headers, env) # TODO(Ziad): add additional details we may need, # like tenant and group info _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user, proxy_headers, env) _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed", proxy_headers, env) _decorate_request_headers('X_TENANT', 'blank', proxy_headers, env) #Auth processed, headers added now decide how to pass on the call if self.app: # Pass to downstream WSGI component env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass return self.app(env, custom_start_response) proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass # We are forwarding to a remote service (no downstream WSGI app) req = Request(proxy_headers) parsed = urlparse(req.url) conn = http_connect(self.service_host, self.service_port, \ req.method, parsed.path, \ proxy_headers, \ ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now return Response(status=resp.status, body=data)(env, start_response)
def _verify_claims(self, env, claims, retry=True): """Verify claims and extract identity information, if applicable.""" cached_claims = self._cache_get(env, claims) if cached_claims: logger.debug("Found cached claims") claims, expires, valid = cached_claims if not valid: logger.debug("Claims not valid (according to cache)") raise ValidationFailed() if expires <= time.time(): logger.debug("Claims (token) expired (according to cache)") raise TokenExpired() return claims # Step 1: We need to auth with the keystone service, so get an # admin token if not self.admin_token: auth = self._get_admin_auth_token(self.admin_user, self.admin_password) self.admin_token = json.loads(auth)["access"]["token"]["id"] # 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 } if self.osksvalidate: headers['X-Subject-Token'] = claims path = '/v2.0/OS-KSVALIDATE/token/validate/%s' % \ self.service_id_querystring logger.debug("Connecting to %s://%s:%s to check claims using the" "OS-KSVALIDATE extension" % (self.auth_protocol, self.auth_host, self.auth_port)) else: path = '/v2.0/tokens/%s%s' % (claims, self.service_id_querystring) logger.debug("Connecting to %s://%s:%s to check claims" % (self.auth_protocol, self.auth_host, self.auth_port)) try: conn = http_connect(self.auth_host, self.auth_port, 'GET', path, headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() except EnvironmentError as exc: if exc.errno == errno.ECONNREFUSED: logger.error( "Keystone server not responding on %s://%s:%s " "to check claims" % (self.auth_protocol, self.auth_host, self.auth_port)) raise KeystoneUnreachable("Unable to connect to authentication" " server") else: logger.exception(exc) raise logger.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): # Cache it if there is a cache available if self.cache: logger.debug("Caching that results were invalid") self._cache_put(env, claims, claims={ 'expires': datetime.strftime(time.time(), EXPIRE_TIME_FORMAT) }, valid=False) if retry: self.admin_token = None return self._verify_claims(env, claims, False) else: # Keystone rejected claim logger.debug("Failing the validation") raise ValidationFailed() token_info = json.loads(data) roles = [ role['name'] for role in token_info["access"]["user"]["roles"] ] # in diablo, there were two ways to get tenant data tenant = token_info['access']['token'].get('tenant') if tenant: # post diablo tenant_id = tenant['id'] tenant_name = tenant['name'] else: # diablo only tenant_id = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') logger.debug("Tenant identified: id=%s, name=%s" % (tenant_id, tenant_name)) verified_claims = { 'user': { 'id': token_info['access']['user']['id'], 'name': token_info['access']['user']['name'], }, 'tenant': { 'id': tenant_id, 'name': tenant_name }, 'roles': roles, 'expires': token_info['access']['token']['expires'] } logger.debug("User identified: id=%s, name=%s" % (token_info['access']['user']['id'], token_info['access']['user']['name'])) expires = self._convert_date(verified_claims['expires']) if expires <= time.time(): logger.debug("Claims (token) expired: %s" % str(expires)) # Cache it if there is a cache available (we also cached bad # claims) if self.cache: logger.debug("Caching expired claim (token)") self._cache_put(env, claims, verified_claims, valid=False) raise TokenExpired() # Cache it if there is a cache available if self.cache: logger.debug("Caching validated claim") self._cache_put(env, claims, verified_claims, valid=True) logger.debug("Returning successful validation") return verified_claims
def _verify_claims(self, env, claims): """Verify claims and extract identity information, if applicable.""" cached_claims = self._cache_get(env, claims) if cached_claims: LOG.debug("Found cached claims") claims, expires, valid = cached_claims if not valid: LOG.debug("Claims not valid (according to cache)") raise ValidationFailed() if expires <= datetime.now(): LOG.debug("Claims (token) expired (according to cache)") raise TokenExpired() return claims # Step 1: We need to auth with the keystone service, so get an # admin token #TODO(ziad): Need to properly implement this, where to store creds # for now using token from ini #auth = self.get_admin_auth_token("admin", "secrete", "1") #admin_token = json.loads(auth)["auth"]["token"]["id"] # 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 LOG.debug("Connecting to %s://%s:%s to check claims" % ( self.auth_protocol, self.auth_host, self.auth_port)) conn = http_connect(self.auth_host, self.auth_port, 'GET', '/v2.0/tokens/%s%s' % (claims, self.serviceId_qs), headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() LOG.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): # Cache it if there is a cache available if self.cache: LOG.debug("Caching that results were invalid") self._cache_put(env, claims, claims={'expires': datetime.strftime(datetime.now(), EXPIRE_TIME_FORMAT)}, valid=False) # Keystone rejected claim LOG.debug("Failing the validation") raise ValidationFailed() token_info = json.loads(data) roles = [role['name'] for role in token_info[ "access"]["user"]["roles"]] # in diablo, there were two ways to get tenant data tenant = token_info['access']['token'].get('tenant') if tenant: # post diablo tenant_id = tenant['id'] tenant_name = tenant['name'] else: # diablo only tenant_id = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') LOG.debug("Tenant identified: id=%s, name=%s" % (tenant_id, tenant_name)) verified_claims = { 'user': { 'id': token_info['access']['user']['id'], 'name': token_info['access']['user']['name'], }, 'tenant': { 'id': tenant_id, 'name': tenant_name }, 'roles': roles, 'expires': token_info['access']['token']['expires']} LOG.debug("User identified: id=%s, name=%s" % ( token_info['access']['user']['id'], token_info['access']['user']['name'])) expires = get_datetime(verified_claims['expires']) if expires <= datetime.now(): LOG.debug("Claims (token) expired: %s" % str(expires)) # Cache it if there is a cache available (we also cached bad # claims) if self.cache: LOG.debug("Caching expired claim (token)") self._cache_put(env, claims, verified_claims, valid=False) raise TokenExpired() # Cache it if there is a cache available if self.cache: LOG.debug("Caching validated claim") self._cache_put(env, claims, verified_claims, valid=True) LOG.debug("Returning successful validation") return verified_claims
def _verify_claims(self, env, claims, retry=True): """Verify claims and extract identity information, if applicable.""" cached_claims = self._cache_get(env, claims) if cached_claims: logger.debug("Found cached claims") claims, expires, valid = cached_claims if not valid: logger.debug("Claims not valid (according to cache)") raise ValidationFailed() if expires <= time.time(): logger.debug("Claims (token) expired (according to cache)") raise TokenExpired() return claims # Step 1: We need to auth with the keystone service, so get an # admin token if not self.admin_token: auth = self._get_admin_auth_token(self.admin_user, self.admin_password) self.admin_token = json.loads(auth)["access"]["token"]["id"] # 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} if self.osksvalidate: headers['X-Subject-Token'] = claims path = '/v2.0/OS-KSVALIDATE/token/validate/%s' % \ self.service_id_querystring logger.debug("Connecting to %s://%s:%s to check claims using the" "OS-KSVALIDATE extension" % (self.auth_protocol, self.auth_host, self.auth_port)) else: path = '/v2.0/tokens/%s%s' % (claims, self.service_id_querystring) logger.debug("Connecting to %s://%s:%s to check claims" % ( self.auth_protocol, self.auth_host, self.auth_port)) try: conn = http_connect(self.auth_host, self.auth_port, 'GET', path, headers=headers, ssl=(self.auth_protocol == 'https'), key_file=self.key_file, cert_file=self.cert_file, timeout=self.auth_timeout) resp = conn.getresponse() data = resp.read() except EnvironmentError as exc: if exc.errno == errno.ECONNREFUSED: logger.error("Keystone server not responding on %s://%s:%s " "to check claims" % (self.auth_protocol, self.auth_host, self.auth_port)) raise KeystoneUnreachable("Unable to connect to authentication" " server") else: logger.exception(exc) raise logger.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): # Cache it if there is a cache available if self.cache: logger.debug("Caching that results were invalid") self._cache_put(env, claims, claims={'expires': datetime.strftime(time.time(), EXPIRE_TIME_FORMAT)}, valid=False) if retry: self.admin_token = None return self._verify_claims(env, claims, False) else: # Keystone rejected claim logger.debug("Failing the validation") raise ValidationFailed() token_info = json.loads(data) roles = [role['name'] for role in token_info[ "access"]["user"]["roles"]] # in diablo, there were two ways to get tenant data tenant = token_info['access']['token'].get('tenant') if tenant: # post diablo tenant_id = tenant['id'] tenant_name = tenant['name'] else: # diablo only tenant_id = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') logger.debug("Tenant identified: id=%s, name=%s" % (tenant_id, tenant_name)) verified_claims = { 'user': { 'id': token_info['access']['user']['id'], 'name': token_info['access']['user']['name'], }, 'tenant': { 'id': tenant_id, 'name': tenant_name }, 'roles': roles, 'expires': token_info['access']['token']['expires']} logger.debug("User identified: id=%s, name=%s" % ( token_info['access']['user']['id'], token_info['access']['user']['name'])) expires = self._convert_date(verified_claims['expires']) if expires <= time.time(): logger.debug("Claims (token) expired: %s" % str(expires)) # Cache it if there is a cache available (we also cached bad # claims) if self.cache: logger.debug("Caching expired claim (token)") self._cache_put(env, claims, verified_claims, valid=False) raise TokenExpired() # Cache it if there is a cache available if self.cache: logger.debug("Caching validated claim") self._cache_put(env, claims, verified_claims, valid=True) logger.debug("Returning successful validation") return verified_claims