def clouds(): ''' Looks up auth token key and returns the clouds this user has been authenticated against ''' token_store = TokenStore(memcache.Client(['127.0.0.1:11211'])) token_id = flask.request.headers["x-auth-token"] token_info = token_store.get(str(token_id)) active_clouds = json.dumps( [key for key in token_info if key != "__tukey_internal"]) print active_clouds return active_clouds
def list_simple_tenant_usage(project_id): # TODO: move this to the database resources = { "cloud": { "adler": "OSDC-Adler", "tcga": "Bionimbus-PDC", "pdc": "Bionimbus-PDC", "sullivan": "OSDC-Sullivan", "atwood": "atwood", "goldberg": "goldberg", "cobb": "cobb", }, "hadoop": {name.replace("-", "_").lower(): name for name in ["OCC-Y", "OCC-LVOC-HADOOP", "skidmore"]}, } toks = TokenStore(memcache.Client(["127.0.0.1:11211"])) token_info = toks.get(str(flask.g.auth_token)) username = token_info["__tukey_internal"]["access"]["user"]["username"] conn_str = "".join( [ "dbname='", local_settings.USAGE_DB_NAME, "' user='******' host='", local_settings.USAGE_DB_HOST, "' password='******' port=", str(local_settings.USAGE_DB_PORT), ] ) db_connection = psycopg2.connect(conn_str) from tukey_middleware.cloud_driver.osdc_euca import OsdcUsage start = flask.request.args.get("start") stop = flask.request.args.get("end") # TODO: instead pass in a connection string osdc_usage = OsdcUsage(db_connection, resources) tenant_usage = osdc_usage.list_usages(start, stop, str(username)) return json.dumps({"tenant_usage": tenant_usage})
def tenant_request(): ''' Request for just the tenant info. This request assumes that /tokens was accessed and created the entry in memcached ''' try: token_id = flask.request.headers["x-auth-token"] except KeyError: flask.abort(401) toks = TokenStore(memcache.Client(['127.0.0.1:11211'])) token_info = toks.get(str(token_id)) tenants = { "tenants_links": [], "tenants": [ token_info["__tukey_internal"]["access"]["token"]["tenant"] ] } return json.dumps(tenants)
def get_cloud_by_id(self, cloud_name, auth_token): try: toks = TokenStore(memcache.Client(['127.0.0.1:11211'])) token_info = toks.get(str(auth_token)) if cloud_name.startswith("cluster"): cloud_name = cloud_name[len("cluster"):] cloud_info = token_info[cloud_name] base_driver = self._initialize_cloud(cloud_info, cloud_name, auth_token) return OsdcNovacluster(base_driver) elif cloud_name.startswith("login"): return self.build_login_driver_by_name(cloud_name, token_info, auth_token) elif cloud_name == "all": # This will probably only ever be used for generating pubkeys # for all clouds # For now we assume that is the case drivers = [] for key, value in token_info.items(): try: if key != "__tukey_internal": if key.startswith("login"): drivers.append(self.build_login_driver_by_name( key, token_info, auth_token)) elif value.get("instance_keypairs", False): drivers.append(self._initialize_cloud(value, key, auth_token)) except TukeyAuthException: continue return AllKeypairsDriver(drivers) else: cloud_info = token_info[cloud_name] except Exception as exc: self.logger.info("Accessing cloud %s without auth_token %s", cloud_name, exc.message) cloud_info = self.settings[cloud_name] return self._initialize_cloud(cloud_info, cloud_name, auth_token)
def list_simple_tenant_usage(project_id): #TODO: move this to the database resources = { 'cloud': { 'adler': 'OSDC-Adler', 'tcga': 'Bionimbus-PDC', 'pdc': 'Bionimbus-PDC', 'sullivan': 'OSDC-Sullivan', 'atwood': 'atwood', 'goldberg': 'goldberg', 'cobb': 'cobb' }, 'hadoop': { name.replace('-','_').lower(): name for name in [ 'OCC-Y', 'OCC-LVOC-HADOOP', 'skidmore'] } } toks = TokenStore(memcache.Client(['127.0.0.1:11211'])) token_info = toks.get(str(flask.g.auth_token)) username = token_info["__tukey_internal"]["access"]["user"]["username"] conn_str = "".join(["dbname='", local_settings.USAGE_DB_NAME, "' user='******' host='", local_settings.USAGE_DB_HOST, "' password='******' port=", str(local_settings.USAGE_DB_PORT)]) db_connection = psycopg2.connect(conn_str) from tukey_middleware.cloud_driver.osdc_euca import OsdcUsage start = flask.request.args.get('start') stop = flask.request.args.get('end') #TODO: instead pass in a connection string osdc_usage = OsdcUsage(db_connection, resources) tenant_usage = osdc_usage.list_usages(start, stop, str(username)) return json.dumps({"tenant_usage": tenant_usage})
def all_clouds(self, auth_token, client_format=None): ''' Return list of cloud_driver objects settings is a dictionary of cloud names and their drivers and parameters ''' self.client_format = client_format clouds = [] toks = TokenStore(memcache.Client(['127.0.0.1:11211'])) token_info = toks.get(str(auth_token)) for name, cloud in [(n, c) for n, c in token_info.items() if n != '__tukey_internal' and not n.startswith("login")]: try: driver_instance = self._initialize_cloud(cloud, name, auth_token) except TukeyAuthException: continue clouds.append(driver_instance) clouds.append(create_login_driver(name, driver_instance, token_info["login" + name])) return clouds
def token_request(): ''' Intercept a token request and use that to talk to multiple clouds based on values stored in the database. request data format will be: {"auth": {"passwordCredentials": { "password": "******", "username": "******" }}} tukeyPassword is a password shared between the middleware and the portal to prevent anyone from talking to the middleware and impersonating users. method can be shibboleth or openid. identifier is looked up in the tukey auth db which stores users openstack credentials. Those credentials are used to talk to the Keystone service for each cloud. The auth tokens from each keystone request are stored in memcached with one of the tokens used as the key and returned back to Horizon. ''' token_store = TokenStore(memcache.Client(['127.0.0.1:11211'])) logger = utils.get_logger() try: token_id = flask.request.headers["x-auth-token"] token_info = token_store.get(str(token_id)) return json.dumps(token_info["__tukey_internal"]) except KeyError: pass pw_creds = json.loads(flask.request.data)["auth"]["passwordCredentials"] # string equality in Python is probably a side-channel vector if pw_creds["password"] != settings["shared_password"]: return ("Wrong credentials", 401) method, userid = pw_creds["username"].split() user_info_query = ''' select username, password, cloud_name, display_name, auth_url, login_url, instance_keypairs.cloud_id from login join login_enabled on login.id = login_enabled.login_id join login_identifier on login.userid = login_identifier.userid join login_identifier_enabled on login_identifier.id = login_identifier_enabled.login_identifier_id join login_method on login_method.method_id = login_identifier.method_id join cloud on cloud.cloud_id = login.cloud_id left outer join instance_keypairs on instance_keypairs.cloud_id = cloud.cloud_id where login_method.method_name='%(method)s' and LOWER(login_identifier.identifier)=LOWER('%(id)s'); ''' % {"method": method, "id": userid} engine = sqlalchemy.create_engine(settings["auth_db_str"]) with engine.begin() as connection: results = connection.execute(sqlalchemy.text(user_info_query)) roles = [] info_by_cloud = {} tenant = None endpoints = {} for (_username, password, cloud, display_name, auth_url, login_url, instance_keypairs) in results: if auth_url: try: try: ksc = keystone_client(auth_url=auth_url, username=_username, password=password) except keystoneclient.apiclient.exceptions.Unauthorized: # this should be a valid username so let Horizon know logger.info(("Cloud %s Keystone at %s ", "rejected username password: %s %s"), cloud, auth_url, _username, password) # this is probably not the best or clearest, just different flask.abort(403) tenants = [t for t in ksc.tenants.list() if t.enabled] if len(tenants) < 1: logger.info("Cloud %s username: %s has no tenants", cloud, _username) continue for tenant in tenants: if tenant.name == _username: break token_response = ksc.get_raw_token_from_identity_service( auth_url, username=_username, password=password, tenant_name=tenant.name) try: # this should work if keystoneclient version <= 0.6.0 response, raw_token = token_response response_status = response.status_code except ValueError: # this should work if keystoneclient version >= 0.7.0 raw_token = token_response response_status = 200 # handle changes between 0.6.0 and 0.7.0 if "access" not in raw_token: raw_token = {"access": raw_token} if response_status != 200: logger.info(("Cloud %s Keystone at %s ", "rejected username: %s with status code: %s"), cloud, auth_url, _username, response_status) flask.abort(403) # add enpoints for endpoint in raw_token["access"]["serviceCatalog"]: endpoints[endpoint["type"]] = endpoint["name"] token_id = ksc.auth_token user_id = ksc.user_id username = _username raw_token["cloud"] = display_name if instance_keypairs: raw_token["instance_keypairs"] = True info_by_cloud[cloud] = raw_token info_by_cloud["login" + cloud] = login_url roles += raw_token["access"]["user"]["roles"] raw_token["cloud"] = display_name except AuthorizationFailure: logger.info("Keystone failed for %s", cloud) else: info_by_cloud[cloud] = {"username": _username, "cloud": display_name, "instance_keypairs": True if instance_keypairs else False} info_by_cloud["login" + cloud] = login_url if tenant is None: logger.info("Login failed for %s using method %s", userid, method) flask.abort(401) region = "RegionOne" host, port = "localhost", LOCAL_PORT allowed_services = ['compute', 'image', 'volume', 'object-store'] # glance assumes that it is at /v1 so we will give it that #service_paths = {k: K for k in allowed_services} #service_paths["image"] = "v1" services = [("http://%s:%s/%s/%s" % (host, port, service, tenant.id), service, service_name) for service, service_name in endpoints.items() if service in allowed_services] services += [("http://%s:%s/v2.0" % (host, port), "identity", "keystone")] catalog = { "access": { "token": { "expires": expiration(43200), "id": token_id, "tenant": format_tenant(tenant.name, tenant.id) }, "serviceCatalog": [ service_catalog_entry(url, region, url, url, service_type, name) for url, service_type, name in services] + [ service_catalog_entry( "http://%s:%s/services/Admin" % (host, port), region, "http://%s:%s/services/Cloud" % (host, port), "http://%s:%s/services/Cloud" % (host, port), "ec2", "ec2")], "user": { "username": username, "roles_links": [], "id": user_id, "roles": roles, "name": username }}, "path": "", "host": host, "port": port} info_by_cloud["__tukey_internal"] = catalog # TODO: # see what the shortest expiration is in the set of expirations # then set the returned expiration to that and make sure that # the memcache expiration is greater than that but has a value so # that memcached entries don't fill everything up token_store.set(str(token_id), info_by_cloud, 172800) logger.info("Login succeeded for %s using method %s" % (userid, method)) return json.dumps(catalog)
class KeystoneProxy(Auth): ''' Used to talk to the old tukey-middleware authentication proxy that uses memcached to keep track of user credentials ''' def __init__(self, cloud_name, auth_token, memcache_client, eucarc_path=None): ''' eucarc_path should be a format string that allows for the username ''' self.token_store = TokenStore(memcache_client) self.eucarc_path = eucarc_path self._keystone = None super(KeystoneProxy, self).__init__(cloud_name, auth_token) def init_auth(self, cloud_name, auth_token): ''' The KeystoneProxy uses the dictionary self.values to answer all the future auth questions ''' try: values = self.token_store.get(str(auth_token)) if values == None: self.values = {} else: if cloud_name in values: # store the distinguished info self._tukey_internal = values["__tukey_internal"] self.values = values[cloud_name] else: self.values = {} except memcache.Client.MemcachedKeyNoneError: self.values = {} # ec2 clouds if "username" in self.values.keys(): self._username = self.values["username"] self.ec2 = Ec2rcHandler(self.eucarc_path, cloud_name, self._username) # openstack clouds elif "access" in self.values.keys(): self._username = self.values["access"]["user"]["username"] @raise_unauthorized def username(self): return self._username @raise_unauthorized def ec2_access_key(self): return self.ec2["EC2_ACCESS_KEY"] @raise_unauthorized def ec2_secret_key(self): return self.ec2["EC2_SECRET_KEY"] def get_value(self, key): if key in self.values: return self.values[key] return self.ec2[key] @raise_unauthorized def host(self): return self.get_value("host") @raise_unauthorized def port(self): return self.get_value("port") @raise_unauthorized def path(self): return self.get_value("path") @raise_unauthorized def auth_token(self): return self.values["access"]["token"]["id"] @raise_unauthorized def tenant_id(self): return self.values["access"]["token"]["tenant"]["id"] @raise_unauthorized def tukey_tenant_id(self): return self._tukey_internal["access"]["token"]["tenant"]["id"] @property def auth_url(self): '''Format auth url, Need to support https!''' return self.get_endpoint("identity") @property def keystone(self): ''' Keystone client ''' if self._keystone is None: self._keystone = keystone_client(auth_url=self.auth_url, tenant_id=self.tenant_id(), token=self.auth_token()) return self._keystone @raise_unauthorized def tenant_name(self): ''' Fetch project name from keystone client''' return self.keystone.project_name def get_endpoint(self, name): ''' Return the internal URL of endpoint with name 'name' ''' for service in self.values["access"]["serviceCatalog"]: if service["type"] == name: return service["endpoints"][0]["publicURL"] @staticmethod def handle_parameters(params): try: ec2path = params["eucarc_path"] except KeyError: ec2path = None mc_class = utils.get_class(params["memcache_client"]["class"]) mc_params = params["memcache_client"]["params"] mc = mc_class(mc_params[0], mc_params[1]) logger = utils.get_logger() logger.debug(mc) logger.debug("doing lambda now ") return lambda cloud, token: KeystoneProxy( cloud, token, mc, eucarc_path=ec2path)