def _fetch_account_email(self): creds_file = os.path.expanduser("~/.onecodex") if not os.path.exists(creds_file): pass elif not os.access(creds_file, os.R_OK): warnings.warn("Check permissions on {}".format(collapse_user(creds_file))) else: try: return json.load(open(creds_file, "r"))["email"] except KeyError: pass except ValueError: warnings.warn("Credentials file ({}) is corrupt".format(collapse_user(creds_file))) return os.environ.get("ONE_CODEX_USER_EMAIL", os.environ.get("ONE_CODEX_USER_UUID"))
def _fetch_account_email(self): creds_file = os.path.expanduser("~/.onecodex") if not os.path.exists(creds_file): pass elif not os.access(creds_file, os.R_OK): warnings.warn("Check permissions on {}".format( collapse_user(creds_file))) else: try: return json.load(open(creds_file, "r"))["email"] except KeyError: pass except ValueError: warnings.warn("Credentials file ({}) is corrupt".format( collapse_user(creds_file))) return os.environ.get("ONE_CODEX_USER_EMAIL", os.environ.get("ONE_CODEX_USER_UUID"))
def _login(server, creds_file=None, api_key=None): """ Login main function """ # creds file path setup if creds_file is None: fp = os.path.expanduser("~/.onecodex") else: fp = creds_file # check if the creds file exists and has an api_key in it creds = {} if os.path.exists(fp): try: with open(fp, mode='r') as f: creds = json.load(f) if 'email' in creds: click.echo('Credentials file already exists ({})'.format( collapse_user(fp)), err=True) return creds['email'] except ValueError: click.echo( "Your ~/.onecodex credentials file appears to be corrupted." # noqa "Please delete it and re-authorize.", err=True) sys.exit(1) # else make it email, api_key = login_uname_pwd(server, api_key=api_key) if api_key is None: click.echo( "We could not verify your credentials. Either you mistyped your email " "or password, or your account does not currently have API access. " "Please contact [email protected] if you continue to experience problems." ) sys.exit(1) now = datetime.datetime.now().strftime(DATE_FORMAT) creds.update({ 'api_key': api_key, 'saved_at': now, 'updated_at': None, 'email': email, }) with open(fp, mode='w') as f: json.dump(creds, f) click.echo("Your ~/.onecodex credentials file was successfully created.", err=True) return email
def _remove_creds(creds_file=None): """Remove ~/.onecodex file, returning True if successul or False if the file didn't exist.""" if creds_file is None: creds_file = os.path.expanduser("~/.onecodex") try: os.remove(creds_file) except Exception as e: if e.errno == errno.ENOENT: return False elif e.errno == errno.EACCES: click.echo("Please check the permissions on {}".format( collapse_user(creds_file)), err=True) sys.exit(1) else: raise return True
def __init__(self, api_key=None, bearer_token=None, cache_schema=True, base_url=None, telemetry=None, schema_path="/api/v1/schema", **kwargs): if base_url is None: base_url = os.environ.get("ONE_CODEX_API_BASE", "https://app.onecodex.com") if base_url != "https://app.onecodex.com": warnings.warn("Using base API URL: {}".format(base_url)) if kwargs.get("experimental", False): warnings.warn( "Experimental API mode enabled. Features of the experimental API are subject to " "change without notice and should not be relied upon in a production enviroment." ) schema_path = "/api/v1_experimental/schema" cache_schema = False self._req_args = {} self._base_url = base_url self._schema_path = schema_path # Attempt to automatically fetch API key from # ~/.onecodex file, API key, or bearer token environment vars # *if and only if* no auth is explicitly passed to Api # # TODO: Consider only doing this if an add'l env var like # 'ONE_CODEX_AUTO_LOGIN' or similar is set. if api_key is None and bearer_token is None: creds_file = os.path.expanduser("~/.onecodex") if not os.path.exists(creds_file): pass elif not os.access(creds_file, os.R_OK): warnings.warn("Check permissions on {}".format( collapse_user(creds_file))) else: try: api_key = json.load(open(creds_file, "r"))["api_key"] except KeyError: # lacking an api_key doesn't mean the file is corrupt--it can just be that the # schema was cached after logging in anonymously pass except ValueError: warnings.warn("Credentials file ({}) is corrupt".format( collapse_user(creds_file))) if api_key is None: api_key = os.environ.get("ONE_CODEX_API_KEY") if bearer_token is None: bearer_token = os.environ.get("ONE_CODEX_BEARER_TOKEN") if bearer_token: # prefer bearer token where available self._req_args["auth"] = BearerTokenAuth(bearer_token) elif api_key: self._req_args["auth"] = HTTPBasicAuth(api_key, "") self._req_args["headers"] = { "X-OneCodex-Client-User-Agent": __version__ } # Create client instance self._client = ExtendedPotionClient(self._base_url, schema_path=self._schema_path, fetch_schema=False, **self._req_args) self._client._fetch_schema(cache_schema=cache_schema) self._session = self._client.session self._copy_resources() # Optionally configure Raven if telemetry is True or (telemetry is None and os.environ.get( "ONE_CODEX_AUTO_TELEMETRY", False)): self._raven_client = get_raven_client( user_context={"email": self._fetch_account_email()}) self._telemetry = True else: self._raven_client = None self._telemetry = False
def _fetch_schema(self, cache_schema=True, creds_file=None): self._cached_schema = {} creds_file = os.path.expanduser( "~/.onecodex") if creds_file is None else creds_file creds = {} if not os.path.exists(creds_file): pass elif not os.access(creds_file, os.R_OK): warnings.warn("Check permissions on {}".format( collapse_user(creds_file))) else: try: creds = json.load(open(creds_file, "r")) except ValueError: warnings.warn("Credentials file ({}) is corrupt".format( collapse_user(creds_file))) serialized_schema = None if cache_schema: # determine if we need to update schema_update_needed = True last_update = creds.get("schema_saved_at") if last_update is not None: last_update = datetime.strptime(last_update, self.DATE_FORMAT) time_diff = datetime.now() - last_update schema_update_needed = time_diff.days > self.SCHEMA_SAVE_DURATION if not schema_update_needed: # get the schema from the credentials file (as a string) serialized_schema = creds.get("schema") if serialized_schema is None: # if the schema wasn't cached or if it was expired, get it anew schema = self.session.get(self._schema_url).json( cls=PotionJSONSchemaDecoder, referrer=self._schema_url, client=self) expanded_schema = self.session.get(self._schema_url + "?expand=all").json() if "message" in schema: raise OneCodexException(schema["message"]) elif "message" in expanded_schema: raise OneCodexException(expanded_schema["message"]) # serialize the main schema serialized_schema = {} serialized_schema[self._schema_url] = json.dumps( schema, cls=PotionJSONEncoder) # serialize the object schemas for schema_name, schema_ref in schema["properties"].items(): cur_schema = expanded_schema["properties"][schema_name] serialized_schema[schema_ref._uri] = json.dumps( cur_schema, cls=PotionJSONEncoder) # save schema if we're going to, otherwise delete it from creds file if cache_schema: creds["schema_saved_at"] = datetime.strftime( datetime.now(), self.DATE_FORMAT) creds["schema"] = serialized_schema else: if "schema_saved_at" in creds: del creds["schema_saved_at"] if "schema" in creds: del creds["schema"] # always resave the creds (to make sure we're removing or saving the cached schema) try: if creds: json.dump(creds, open(creds_file, "w")) else: os.remove(creds_file) except Exception as e: if e.errno == errno.ENOENT: pass elif e.errno == errno.EACCES: warnings.warn("Check permissions on {}".format( collapse_user(creds_file))) else: raise # by the time we get here, we should have loaded the serialized schema from creds_file or # pulled it from the API and serialized it. now, we unserialize it and put it where it # needs to be. base_schema = serialized_schema.pop(self._schema_url, None) if base_schema is None: other_schema_url = [ ref for ref in serialized_schema if not ref.endswith("#") ] if len(other_schema_url) == 1: schema_url = other_schema_url[0] base_schema = serialized_schema.pop(schema_url) warnings.warn( "Using cached schema for {}, which does not match {}". format(schema_url, self._schema_url)) else: raise OneCodexException( "Could not find schema for {} in ~/.onecodex. Please delete this file, " "re-login and try again, or pass cache_schema=False.". format(self._schema_url)) else: schema_url = self._schema_url base_schema = json.loads(base_schema, cls=PotionJSONSchemaDecoder, referrer=schema_url, client=self) for name, schema_ref in base_schema["properties"].items(): object_schema = json.loads( serialized_schema[schema_ref._uri], cls=PotionJSONSchemaDecoder, referrer=self._schema_url, client=self, ) object_schema["_base_uri"] = schema_ref._uri.replace( "/schema#", "") self._cached_schema[schema_ref._uri] = object_schema class_name = upper_camel_case(name) setattr(self, class_name, self.resource_factory(name, object_schema))
def _login(server, creds_file=None, api_key=None, silent=False): """Login main function.""" # fetch_api_key and check_version expect server to end in / if server[-1] != "/": server = server + "/" # creds file path setup if creds_file is None: creds_file = os.path.expanduser("~/.onecodex") # check if the creds file exists and is readable if not os.path.exists(creds_file): if silent: return None creds = {} elif not os.access(creds_file, os.R_OK): click.echo("Please check the permissions on {}".format( collapse_user(creds_file)), err=True) sys.exit(1) else: # it is, so let's read it! with open(creds_file, "r") as fp: try: creds = json.load(fp) except ValueError: click.echo( "Your ~/.onecodex credentials file appears to be corrupted. " # noqa "Please delete it and re-authorize.", err=True, ) sys.exit(1) # check for updates if logged in more than one day ago last_update = creds.get("updated_at") or creds.get("saved_at") last_update = last_update if last_update else datetime.datetime.now( ).strftime(DATE_FORMAT) diff = datetime.datetime.now() - datetime.datetime.strptime( last_update, DATE_FORMAT) if diff.days >= 1: # if creds_file is old, check for updates upgrade_required, msg = check_version(__version__, server) creds["updated_at"] = datetime.datetime.now().strftime(DATE_FORMAT) try: json.dump(creds, open(creds_file, "w")) except Exception as e: if e.errno == errno.EACCES: click.echo( "Please check the permissions on {}".format( collapse_user(creds_file)), err=True, ) sys.exit(1) else: raise if upgrade_required: click.echo("\nWARNING: {}\n".format(msg), err=True) # finally, give the user back what they want (whether silent or not) if silent: return creds.get("api_key", None) click.echo( "Credentials file already exists ({}). Logout first.".format( collapse_user(creds_file)), err=True, ) return creds.get("email", None) # creds_file was not found and we're not silent, so prompt user to login email, api_key = login_uname_pwd(server, api_key=api_key) if api_key is None: click.echo( "We could not verify your credentials. Either you mistyped your email " "or password, or your account does not currently have API access. " "Please contact [email protected] if you continue to experience problems." ) sys.exit(1) creds.update({ "api_key": api_key, "saved_at": datetime.datetime.now().strftime(DATE_FORMAT), "updated_at": None, "email": email, }) try: json.dump(creds, open(creds_file, "w")) except Exception as e: if e.errno == errno.EACCES: click.echo("Please check the permissions on {}".format(creds_file), err=True) sys.exit(1) else: raise click.echo("Your ~/.onecodex credentials file was successfully created.", err=True) return email
def __init__( self, api_key=None, bearer_token=None, cache_schema=True, base_url=None, telemetry=None, schema_path="/api/v1/schema", ): if base_url is None: base_url = os.environ.get("ONE_CODEX_API_BASE", "https://app.onecodex.com") if base_url != "https://app.onecodex.com": warnings.warn("Using base API URL: {}".format(base_url)) self._req_args = {} self._base_url = base_url self._schema_path = schema_path # Attempt to automatically fetch API key from # ~/.onecodex file, API key, or bearer token environment vars # *if and only if* no auth is explicitly passed to Api # # TODO: Consider only doing this if an add'l env var like # 'ONE_CODEX_AUTO_LOGIN' or similar is set. if api_key is None and bearer_token is None: creds_file = os.path.expanduser("~/.onecodex") if not os.path.exists(creds_file): pass elif not os.access(creds_file, os.R_OK): warnings.warn("Check permissions on {}".format(collapse_user(creds_file))) else: try: api_key = json.load(open(creds_file, "r"))["api_key"] except KeyError: # lacking an api_key doesn't mean the file is corrupt--it can just be that the # schema was cached after logging in anonymously pass except ValueError: warnings.warn( "Credentials file ({}) is corrupt".format(collapse_user(creds_file)) ) if api_key is None: api_key = os.environ.get("ONE_CODEX_API_KEY") if bearer_token is None: bearer_token = os.environ.get("ONE_CODEX_BEARER_TOKEN") if bearer_token: # prefer bearer token where available self._req_args["auth"] = BearerTokenAuth(bearer_token) elif api_key: self._req_args["auth"] = HTTPBasicAuth(api_key, "") self._req_args["headers"] = {"X-OneCodex-Client-User-Agent": __version__} # Create client instance self._client = ExtendedPotionClient( self._base_url, schema_path=self._schema_path, fetch_schema=False, **self._req_args ) self._client._fetch_schema(cache_schema=cache_schema) self._session = self._client.session self._copy_resources() # Optionally configure Raven if telemetry is True or ( telemetry is None and os.environ.get("ONE_CODEX_AUTO_TELEMETRY", False) ): self._raven_client = get_raven_client( user_context={"email": self._fetch_account_email()} ) self._telemetry = True else: self._raven_client = None self._telemetry = False
def _fetch_schema(self, cache_schema=True, creds_file=None): self._cached_schema = {} creds_file = os.path.expanduser("~/.onecodex") if creds_file is None else creds_file creds = {} if not os.path.exists(creds_file): pass elif not os.access(creds_file, os.R_OK): warnings.warn("Check permissions on {}".format(collapse_user(creds_file))) else: try: creds = json.load(open(creds_file, "r")) except ValueError: warnings.warn("Credentials file ({}) is corrupt".format(collapse_user(creds_file))) serialized_schema = None if cache_schema: # determine if we need to update schema_update_needed = True last_update = creds.get("schema_saved_at") if last_update is not None: last_update = datetime.strptime(last_update, self.DATE_FORMAT) time_diff = datetime.now() - last_update schema_update_needed = time_diff.days > self.SCHEMA_SAVE_DURATION if not schema_update_needed: # get the schema from the credentials file (as a string) serialized_schema = creds.get("schema") if serialized_schema is None: # if the schema wasn't cached or if it was expired, get it anew # use a non-authenticated session to fetch the schema. if our creds are invalid # and we use the authenticated session in `self.session`, the fetch will fail # despite the schema being publically available. unauth_sess = Session() schema = unauth_sess.get(self._schema_url).json( cls=PotionJSONSchemaDecoder, referrer=self._schema_url, client=self ) expanded_schema = unauth_sess.get(self._schema_url + "?expand=all").json() if "message" in schema: raise OneCodexException(schema["message"]) elif "message" in expanded_schema: raise OneCodexException(expanded_schema["message"]) # serialize the main schema serialized_schema = {} serialized_schema[self._schema_url] = json.dumps(schema, cls=PotionJSONEncoder) # serialize the object schemas for schema_name, schema_ref in schema["properties"].items(): cur_schema = expanded_schema["properties"][schema_name] serialized_schema[schema_ref._uri] = json.dumps(cur_schema, cls=PotionJSONEncoder) # save schema if we're going to, otherwise delete it from creds file if cache_schema: creds["schema_saved_at"] = datetime.strftime(datetime.now(), self.DATE_FORMAT) creds["schema"] = serialized_schema else: if "schema_saved_at" in creds: del creds["schema_saved_at"] if "schema" in creds: del creds["schema"] # always resave the creds (to make sure we're removing or saving the cached schema) try: if creds: json.dump(creds, open(creds_file, "w")) else: os.remove(creds_file) except Exception as e: if e.errno == errno.ENOENT: pass elif e.errno == errno.EACCES: warnings.warn("Check permissions on {}".format(collapse_user(creds_file))) else: raise # by the time we get here, we should have loaded the serialized schema from creds_file or # pulled it from the API and serialized it. now, we unserialize it and put it where it # needs to be. base_schema = serialized_schema.pop(self._schema_url, None) base_schema = json.loads( base_schema, cls=PotionJSONSchemaDecoder, referrer=self._schema_url, client=self ) for name, schema_ref in base_schema["properties"].items(): object_schema = json.loads( serialized_schema[schema_ref._uri], cls=PotionJSONSchemaDecoder, referrer=self._schema_url, client=self, ) object_schema["_base_uri"] = schema_ref._uri.replace("/schema#", "") self._cached_schema[schema_ref._uri] = object_schema class_name = upper_camel_case(name) setattr(self, class_name, self.resource_factory(name, object_schema))