def _create_new_connection(is_headless, data): """ Creates a new Shotgun connection based on user input. :param bool is_headless: Indicates if the script was invoked without a shell. :param dict data: Data found in the credentials file. :returns: A Shotgun connection and a user entity for the loged in user. """ if is_headless: raise UserInteractionRequiredError() # If the credentials didn't work or the file didn't exist, # ask for the credentials. site = _get_credential("Site", data.get("site", "")) login = _get_credential("Login", data.get("login", "")) sg = None # While we don't have a valid connection, keep asking for a password. while not sg: password = getpass("Password: "******"Authentication failure. Bad password?" print sg = None else: _set_password(site, login, password) # Update the data dictionary. Note that the dictionary can also # contain information about Toggl, so we need to update it # instead of creating a new one. data["site"] = site data["login"] = login data["session_token"] = session_token with open(_get_credential_file_path(), "w") as f: json.dump(data, f) return sg, _get_self(sg, login)
def _log_into_sg(is_headless): """ Ensures that the user is logged into Shotgun. If not logged, the credentials are queried. If out of date, useful defaults are provided. :param bool is_headless: If True, logging won't attempt to ask for credentials. :returns: Shotgun connection and associated HumanUser entity. """ # Assume the file is empty originally. data = _get_credentials_from_file() # No session token, create a new connection. if not data.get("session_token"): return _create_new_connection(is_headless, data) # Try to create a session with the session token that is stored. sg = Shotgun(data["site"], session_token=data["session_token"]) try: return sg, _get_self(sg, data["login"]) except AuthenticationFault: pass print "Session token expired. Retrieving password from keyring." password = _get_password(data["site"], data["login"]) # If there is no password, ask for the credentials from scratch. if not password: print "Password not found in keyring or empty." return _create_new_connection(is_headless, data) try: sg = Shotgun(data["site"], login=data["login"], password=password) data["session_token"] = sg.get_session_token() with open(_get_credential_file_path(), "w") as f: json.dump(data, f) return sg, _get_self(sg, data["login"]) except AuthenticationFault: print "Password in keychain doesnt't seem to work. Did you change it?" return _create_new_connection(is_headless, data)
def generate_session_token(hostname, login, password, http_proxy, auth_token=None): """ Generates a session token for a given username/password on a given site. :param hostname: The host to connect to. :param login: The user to get a session for. :param password: Password for the user. :param http_proxy: Proxy to use. Can be None. :param auth_token: Two factor authentication token for the user. Can be None. :returns: The generated session token for that user/password/auth_token/site combo. :raises AuthenticationError: Raised when the user information is invalid. :raises MissingTwoFactorAuthenticationFault: Raised when missing a two factor authentication code or backup code. :raises Exception: Raised when a network error occurs. """ try: # Create the instance that does not connect right away for speed... logger.debug("Connecting to Shotgun to generate session token...") sg = Shotgun(hostname, login=login, password=password, http_proxy=http_proxy, connect=False, auth_token=auth_token) # .. and generate the session token. If it throws, we have invalid # credentials or invalid host/proxy settings. return sg.get_session_token() except AuthenticationFault: raise AuthenticationError("Authentication failed.") except (ProtocolError, httplib2.ServerNotFoundError): raise AuthenticationError("Server %s was not found." % hostname) # In the following handlers, we are not rethrowing an AuthenticationError for # a very specific reason. While wrong credentials or host is a user # recoverable error, problems with proxy settings or network errors are much # more severe errors which can't be fixed by reprompting. Therefore, they have # nothing to do with authentication and shouldn't be reported as such. except socket.error as e: logger.exception("Unexpected connection error.") # e.message is always an empty string, so look at the exception's arguments. # The arguments are always a string or a number and a string. if isinstance(e.args[0], str): # if the error is just a string, simply forward the message. raise Exception(e.args[0]) else: # We could argue here that we should only display the string portion of the # error since the error code is of little relevance to the user, but since # Toolkit doesn't properly log everything to a file at the moment, it's probably # safer to have the error code a little bit more in the open. Also, the formatting # of this exception type is pretty bad so let's reformat it ourselves. By default, it # turns a tuple into a string. raise Exception("%s (%d)" % (e.args[1], e.args[0])) except httplib2.socks.ProxyError as e: logger.exception("Unexpected connection error.") # Same comment applies here around formatting. # Note that e.message is always a tuple in this raise Exception("%s (%d)" % (e.message[1], e.message[0])) except MissingTwoFactorAuthenticationFault: # Silently catch and rethrow to avoid logging. raise except Exception as e: logger.exception("There was a problem logging in.") # If the error message is empty, like httplib.HTTPException, convert # the class name to a string if len(str(e)) == 0: raise Exception("Unknown error: %s" % type(e).__name__) else: raise