def find_env(kls, location, crypto, owner, cache=None): """Add environment variables to our environment file""" if cache and location in cache: return cache[location] while True: keys, error = EnvironmentFile.loaded_file_from(location, crypto, owner) if error: quit_choice = "Quit" fixed_choice = "I fixed it, try again" override_choice = "Override the existing file" choice = ask_for_choice("Couldn't read the environment file, what do you want to do?", choices=[quit_choice, fixed_choice, override_choice]) if choice == quit_choice: raise UserQuit() elif choice == fixed_choice: continue else: break else: break if not keys: keys = EnvironmentFile(location, crypto, owner) if cache is not None: cache[location] = keys return keys
def configure(repo_name, location, crypto, new_remote=None, version_with=None): """Configure a repository""" repository = Repository(repo_name, location, crypto) if repository.versioned: remote = "<none set>" if repository.remote: remote = repository.remote remove_choice = "Stop versioning this directory" change_choice = "Change the remote of this directory" carryon_choice = "Keep the remote as is" if version_with == "nothing": choice = remove_choice elif new_remote is None and version_with is None: choice = ask_for_choice( [ "This repo is already versioned\tremote={0}\tlocation={1}".format(remote, repository.location) , "What do you want to do?" ] , [remove_choice, change_choice, carryon_choice] ) else: choice = change_choice if choice == remove_choice: repository.deleteme() elif choice == change_choice: repository.change_remote(new_remote, remote_type=version_with) else: change_choice = "Add versioning to this repository" carryon_choice = "Keep the repository unversioned" if version_with == "nothing": log.info("Repository is already unversioned") elif version_with is None: choice = ask_for_choice( [ "This repo is not versioned yet ({0})".format(repository.location) , "What do you want to do?" ] , [carryon_choice, change_choice] ) else: choice = change_choice if choice == change_choice: repository.change_remote(new_remote, remote_type=version_with)
def rsaobj_from_location(self, location, only_need_public=False): """ Get us a paramiko.RSAKey from this location If the location is a password protected private key and only_need_public then we look for a public key If we can't find a public key, then we ask for the password and get the fingerprint that way If it isn't a private key, then we raise a credo.NotSSHKey exception """ if only_need_public: if location in self._location_to_public_rsaobj: return self._location_to_public_rsaobj[location] else: if location in self._location_to_private_rsaobj: return self._location_to_private_rsaobj[location] rsaobj = None try: rsaobj = self.make_rsakey(location, private=True) self._location_to_private_rsaobj[location] = rsaobj except PasswordRequired: if only_need_public: pub_key = "{0}.pub".format(location) if os.path.exists(pub_key): try: rsaobj = self.make_rsakey(pub_key) self._location_to_public_rsaobj[location] = rsaobj except BadSSHKey as err: log.info("Something wrong with public key %s: %s", pub_key, err) if rsaobj is None: while True: if only_need_public: log.info( "Couldn't find a public key for password protected private key at %s", location) password = get_response( "Password for your private key ({0})".format(location), password=True) try: rsaobj = self.make_rsakey(location, password=password, private=True) self._location_to_private_rsaobj[location] = rsaobj break except BadSSHKey: choice = ask_for_choice( "Couldn't decode the key ({0})".format(location), ["Try again", "Ignore"]) if choice == "Ignore": return return rsaobj
def configure(repo_name, location, crypto, new_remote=None, version_with=None): """Configure a repository""" repository = Repository(repo_name, location, crypto) if repository.versioned: remote = "<none set>" if repository.remote: remote = repository.remote remove_choice = "Stop versioning this directory" change_choice = "Change the remote of this directory" carryon_choice = "Keep the remote as is" if version_with == "nothing": choice = remove_choice elif new_remote is None and version_with is None: choice = ask_for_choice([ "This repo is already versioned\tremote={0}\tlocation={1}". format(remote, repository.location), "What do you want to do?" ], [remove_choice, change_choice, carryon_choice]) else: choice = change_choice if choice == remove_choice: repository.deleteme() elif choice == change_choice: repository.change_remote(new_remote, remote_type=version_with) else: change_choice = "Add versioning to this repository" carryon_choice = "Keep the repository unversioned" if version_with == "nothing": log.info("Repository is already unversioned") elif version_with is None: choice = ask_for_choice([ "This repo is not versioned yet ({0})".format( repository.location), "What do you want to do?" ], [carryon_choice, change_choice]) else: choice = change_choice if choice == change_choice: repository.change_remote(new_remote, remote_type=version_with)
def deleteme(self): """Delete the versioning!""" quit_choice = "Quit" confirm_choice = "Yes I do want to delete!" git_folder = os.path.join(self.location, ".git") choice = ask_for_choice("Are you sure you want to remove {0}?".format(git_folder), [quit_choice, confirm_choice]) if choice == quit_choice: raise UserQuit() else: shutil.rmtree(git_folder)
def resolve_dirty_repo(self): """Wait for the user to resolve any changes already in the repo""" while True: if not self.repo.status(): break quit_choice = "Quit" fixed_choice = "I fixed it, please continue" choice = ask_for_choice("Seems there is already changes\tlocation={0}".format(self.location), choices=[quit_choice, fixed_choice]) if choice == quit_choice: raise UserQuit() elif self.repo.status(): log.error("Ummm, there's still changes already in the repository...\tlocation=%s", self.location)
def rsaobj_from_location(self, location, only_need_public=False): """ Get us a paramiko.RSAKey from this location If the location is a password protected private key and only_need_public then we look for a public key If we can't find a public key, then we ask for the password and get the fingerprint that way If it isn't a private key, then we raise a credo.NotSSHKey exception """ if only_need_public: if location in self._location_to_public_rsaobj: return self._location_to_public_rsaobj[location] else: if location in self._location_to_private_rsaobj: return self._location_to_private_rsaobj[location] rsaobj = None try: rsaobj = self.make_rsakey(location, private=True) self._location_to_private_rsaobj[location] = rsaobj except PasswordRequired: if only_need_public: pub_key = "{0}.pub".format(location) if os.path.exists(pub_key): try: rsaobj = self.make_rsakey(pub_key) self._location_to_public_rsaobj[location] = rsaobj except BadSSHKey as err: log.info("Something wrong with public key %s: %s", pub_key, err) if rsaobj is None: while True: if only_need_public: log.info("Couldn't find a public key for password protected private key at %s", location) password = get_response("Password for your private key ({0})".format(location), password=True) try: rsaobj = self.make_rsakey(location, password=password, private=True) self._location_to_private_rsaobj[location] = rsaobj break except BadSSHKey: choice = ask_for_choice("Couldn't decode the key ({0})".format(location), ["Try again", "Ignore"]) if choice == "Ignore": return return rsaobj
def retrieve_public_key_from_disk(self, fingerprint, reason=None): """Get this fingerprint and return whether we successfully did so""" if self.keys.collection.location_for_fingerprint(fingerprint): return True def make_ssh_key_location(folders): """Get a string to say where we have ssh key folders""" if len(self.ssh_key_folders) == 1: return self.ssh_key_folders[0] else: return "one of {0}".format(", ".join(self.ssh_key_folders)) def add_more_ssh_key_folders(): """Add more ssh key folders""" more_ssh_key_folders = ask_for_ssh_key_folders( already_have=self.ssh_key_folders) self.ssh_key_folders.extend(more_ssh_key_folders) while not self.ssh_key_folders: add_more_ssh_key_folders() ssh_key_locations = make_ssh_key_location(self.ssh_key_folders) while True: if self.keys.collection.location_for_fingerprint(fingerprint): return True ignore_choice = "I don't have this key" try_again_choice = "I've added the key to {0}".format( ssh_key_locations) have_another_ssh_key_folder = "I have another folder with ssh keys in it" choice = ask_for_choice( "Looking for a public key with fingerprint {0}".format( fingerprint), choices=[ ignore_choice, try_again_choice, have_another_ssh_key_folder ]) if choice == ignore_choice: return False elif choice == try_again_choice: continue elif choice == have_another_ssh_key_folder: add_more_ssh_key_folders() ssh_key_locations = make_ssh_key_location(self.ssh_key_folders) self.keys.find_public_keys()
def fix_keys(self, location, error): """Get user to fix the keys file""" info = {"error": error} while True: quit_choice = "Quit" remove_choice = "Remove the file" try_again_choice = "I fixed it, Try again" choices = [try_again_choice, remove_choice, quit_choice] response = ask_for_choice("Couldn't load {0} as a json file ({1})".format(location, info["error"]), choices) if response == quit_choice: raise UserQuit() elif response == remove_choice: os.remove(location) return {} else: try: return json.load(location) except ValueError as err: info["error"] = err
def retrieve_public_key_from_disk(self, fingerprint, reason=None): """Get this fingerprint and return whether we successfully did so""" if self.keys.collection.location_for_fingerprint(fingerprint): return True def make_ssh_key_location(folders): """Get a string to say where we have ssh key folders""" if len(self.ssh_key_folders) == 1: return self.ssh_key_folders[0] else: return "one of {0}".format(", ".join(self.ssh_key_folders)) def add_more_ssh_key_folders(): """Add more ssh key folders""" more_ssh_key_folders = ask_for_ssh_key_folders(already_have=self.ssh_key_folders) self.ssh_key_folders.extend(more_ssh_key_folders) while not self.ssh_key_folders: add_more_ssh_key_folders() ssh_key_locations = make_ssh_key_location(self.ssh_key_folders) while True: if self.keys.collection.location_for_fingerprint(fingerprint): return True ignore_choice = "I don't have this key" try_again_choice = "I've added the key to {0}".format(ssh_key_locations) have_another_ssh_key_folder = "I have another folder with ssh keys in it" choice = ask_for_choice("Looking for a public key with fingerprint {0}".format(fingerprint), choices=[ignore_choice, try_again_choice, have_another_ssh_key_folder]) if choice == ignore_choice: return False elif choice == try_again_choice: continue elif choice == have_another_ssh_key_folder: add_more_ssh_key_folders() ssh_key_locations = make_ssh_key_location(self.ssh_key_folders) self.keys.find_public_keys()
def fix_keys(self, location, error): """Get user to fix the keys file""" info = {"error": error} while True: quit_choice = "Quit" remove_choice = "Remove the file" try_again_choice = "I fixed it, Try again" choices = [try_again_choice, remove_choice, quit_choice] response = ask_for_choice( "Couldn't load {0} as a json file ({1})".format( location, info["error"]), choices) if response == quit_choice: raise UserQuit() elif response == remove_choice: os.remove(location) return {} else: try: return json.load(location) except ValueError as err: info["error"] = err
def do_import(credo, source=False, half_life=None, **kwargs): """Import some creds""" typ, info = ask_user_for_secrets(credo, source=source) if typ == "amazon": access_key, secret_key = info iam_pair = IamPair(access_key, secret_key, half_life=half_life) if not iam_pair.works: raise BadCredential("The credentials you just provided don't work....") half_life = normalise_half_life(half_life, access_key) or getattr(credo, "half_life", None) iam_pair.set_half_life(half_life) iam_pair._changed = False elif typ == "saml": access_key = None # Keep asking for username and password until they give one while True: idp_username = get_response("Idp username", default=os.environ.get("USER")) idp_password = get_response("Idp password", password=True) iam_pair = IamSaml(info, idp_username, idp_password, half_life=half_life) try: arns = iam_pair.arns except SamlNotAuthorized: continue saml_account = ask_for_choice("Which account do you want to use?", choices=sorted(arns, key=lambda a: a.role_arn)) break else: raise ProgrammerError("Unknown credential type {0}".format(typ)) structure, chains = credo.find_credentials(asker=ask_for_choice_or_new, want_new=True) creds = list(credo.credentials_from(structure, chains, typ=typ))[0] cred_path = creds.credential_path log.info("Making credentials for\trepo=%s\taccount=%s\tuser=%s", cred_path.repository.name, cred_path.account.name, cred_path.user.name) if typ != "saml": cred_path.repository.pub_key_syncer.sync() log.debug("Crypto has private keys %s", credo.crypto.private_key_fingerprints) log.debug("Crypto has public_keys %s", credo.crypto.public_key_fingerprints) if not credo.crypto.can_encrypt: raise CantEncrypt("No public keys to encrypt with", repo=cred_path.repository.name) if not credo.crypto.can_sign: log.error("Couldn't find any private keys matching your known public keys!") cred_path.repository.pub_key_syncer.sync(ask_anyway=True) if not credo.crypto.can_sign: raise CantSign("No private keys with matching public keys to sign with", repo=cred_path.repository.name) if typ == "amazon": account_id = cred_path.account.account_id(iam_pair=iam_pair) if iam_pair.ask_amazon_for_account() != account_id: raise BadCredential("The credentials you are importing are for a different account" , credentials_account_id=iam_pair.ask_amazon_for_account(), importing_into_account_name=cred_path.account.name, importing_into_account_id=account_id ) username = cred_path.user.username(iam_pair=iam_pair) if iam_pair.ask_amazon_for_username() != username: raise BadCredential("The credentials you are importing are for a different user" , credentials_user=iam_pair.ask_amazon_for_username(), importing_into_user=username ) creds.keys.add(iam_pair) else: contents = creds.contents contents.keys["provider"] = info contents.keys["role"] = saml_account.encrypted_values() contents.keys["idp_username"] = idp_username creds.save(half_life=half_life) cred_path.repository.synchronize()
def manually_confirm_keys(self, location, contents): """Manually confirm the urls and pems in our contents file""" result = {"pems": [], "urls": []} pems = contents.get("pems", []) urls = contents.get("urls", []) if pems: log.info( "You need to confirm that you own the private keys for these public keys" ) fingerprints_with_pems = self.crypto.zip_with_fingerprints(pems) print("File contains public keys with these fingerprints", file=sys.stderr) print("\t{0}".format("\n\t".join(v[0].decode('utf-8') for v in fingerprints_with_pems)), file=sys.stderr) if self.crypto.private_key_fingerprints: print("\nWe know about these private keys", file=sys.stderr) print("\t{0}".format("\n\t".join( self.crypto.private_key_fingerprints)), file=sys.stderr) matches = all(fingerprint in self.crypto.private_key_fingerprints for fingerprint, _ in fingerprints_with_pems) matches_str = " (Not all the public keys match existing private keys)" if matches: matches_str = " (All the keys match private keys on your computer)" quit_choice = "Quit" confirm_all_choice = "I say all of them are valid {0}".format( matches_str) individual_confirm_choice = "I'll confirm them individually" if matches: confirm_all_choice = "I say all of them are valid (All the keys match private keys on this computer)" else: individual_confirm_choice = "I'll confirm them individually (Not all the keys match private keys on this computer)" choice = ask_for_choice( "How do you want to confirm your keys?", [quit_choice, confirm_all_choice, individual_confirm_choice]) if choice == quit_choice: raise UserQuit() elif choice == confirm_all_choice: result["pems"].extend(pems) else: for fingerprint, pem in fingerprints_with_pems: matches = fingerprint in self.crypto.private_key_fingerprints matches_str = "" if matches: matches_str = " (Matches a private key on this computer)" quit_choice = "Quit" exclude_choice = "No" include_choice = "yes" if matches: include_choice = "Yes{0}".format(matches_str) else: exclude_choice = "No{0}".format(matches_str) choice = ask_for_choice( "Is {0} a fingerprint of yours?".format(fingerprint), [include_choice, exclude_choice, quit_choice]) if choice == quit_choice: raise UserQuit() elif choice == include_choice: result["pems"].append(pem) if urls: log.info("You need to confirm you want keys from these urls") print("\t{0}".join("\n\t".join(urls))) quit_choice = "Quit" confirm_all_choice = "All of them are valid" individual_confirm_choice = "I'll confirm them individually" choice = ask_for_choice( "How do you want to confirm the urls?", [quit_choice, confirm_all_choice, individual_confirm_choice]) if choice == quit_choice: raise UserQuit() elif confirm_all_choice: result["urls"].extend(urls) else: for url in urls: quit_choice = "Quit" exclude_choice = "No" include_choice = "Yes" choice = ask_for_choice( "Is this urls fine?", [include_choice, exclude_choice, quit_choice]) if choice == quit_choice: raise UserQuit() elif choice == include_choice: result["urls"].append(url) return result
def manually_confirm_keys(self, location, contents): """Manually confirm the urls and pems in our contents file""" result = {"pems": [], "urls": []} pems = contents.get("pems", []) urls = contents.get("urls", []) if pems: log.info("You need to confirm that you own the private keys for these public keys") fingerprints_with_pems = self.crypto.zip_with_fingerprints(pems) print("File contains public keys with these fingerprints", file=sys.stderr) print("\t{0}".format("\n\t".join(v[0] for v in fingerprints_with_pems)), file=sys.stderr) if self.crypto.private_key_fingerprints: print("\nWe know about these private keys", file=sys.stderr) print("\t{0}".format("\n\t".join(self.crypto.private_key_fingerprints)), file=sys.stderr) matches = all(fingerprint in self.crypto.private_key_fingerprints for fingerprint, _ in fingerprints_with_pems) matches_str = " (Not all the public keys match existing private keys)" if matches: matches_str = " (All the keys match private keys on your computer)" quit_choice = "Quit" confirm_all_choice = "I say all of them are valid {0}".format(matches_str) individual_confirm_choice = "I'll confirm them individually" if matches: confirm_all_choice = "I say all of them are valid (All the keys match private keys on this computer)" else: individual_confirm_choice = "I'll confirm them individually (Not all the keys match private keys on this computer)" choice = ask_for_choice("How do you want to confirm your keys?", [quit_choice, confirm_all_choice, individual_confirm_choice]) if choice == quit_choice: raise UserQuit() elif choice == confirm_all_choice: result["pems"].extend(pems) else: for fingerprint, pem in fingerprints_with_pems: matches = fingerprint in self.crypto.private_key_fingerprints matches_str = "" if matches: matches_str = " (Matches a private key on this computer)" quit_choice = "Quit" exclude_choice = "No" include_choice = "yes" if matches: include_choice = "Yes{0}".format(matches_str) else: exclude_choice = "No{0}".format(matches_str) choice = ask_for_choice("Is {0} a fingerprint of yours?".format(fingerprint), [include_choice, exclude_choice, quit_choice]) if choice == quit_choice: raise UserQuit() elif choice == include_choice: result["pems"].append(pem) if urls: log.info("You need to confirm you want keys from these urls") print("\t{0}".join("\n\t".join(urls))) quit_choice = "Quit" confirm_all_choice = "All of them are valid" individual_confirm_choice = "I'll confirm them individually" choice = ask_for_choice("How do you want to confirm the urls?", [quit_choice, confirm_all_choice, individual_confirm_choice]) if choice == quit_choice: raise UserQuit() elif confirm_all_choice: result["urls"].extend(urls) else: for url in urls: quit_choice = "Quit" exclude_choice = "No" include_choice = "Yes" choice = ask_for_choice("Is this urls fine?", [include_choice, exclude_choice, quit_choice]) if choice == quit_choice: raise UserQuit() elif choice == include_choice: result["urls"].append(url) return result
def do_import(credo, source=False, half_life=None, **kwargs): """Import some creds""" typ, info = ask_user_for_secrets(credo, source=source) if typ == "amazon": access_key, secret_key = info iam_pair = IamPair(access_key, secret_key, half_life=half_life) if not iam_pair.works: raise BadCredential( "The credentials you just provided don't work....") half_life = normalise_half_life(half_life, access_key) or getattr( credo, "half_life", None) iam_pair.set_half_life(half_life) iam_pair._changed = False elif typ == "saml": access_key = None # Keep asking for username and password until they give one while True: idp_username = get_response("Idp username", default=os.environ.get("USER")) idp_password = get_response("Idp password", password=True) iam_pair = IamSaml(info, idp_username, idp_password, half_life=half_life) try: arns = iam_pair.arns except SamlNotAuthorized: continue saml_account = ask_for_choice("Which account do you want to use?", choices=sorted( arns, key=lambda a: a.role_arn)) break else: raise ProgrammerError("Unknown credential type {0}".format(typ)) structure, chains = credo.find_credentials(asker=ask_for_choice_or_new, want_new=True) creds = list(credo.credentials_from(structure, chains, typ=typ))[0] cred_path = creds.credential_path log.info("Making credentials for\trepo=%s\taccount=%s\tuser=%s", cred_path.repository.name, cred_path.account.name, cred_path.user.name) if typ != "saml": cred_path.repository.pub_key_syncer.sync() log.debug("Crypto has private keys %s", credo.crypto.private_key_fingerprints) log.debug("Crypto has public_keys %s", credo.crypto.public_key_fingerprints) if not credo.crypto.can_encrypt: raise CantEncrypt("No public keys to encrypt with", repo=cred_path.repository.name) if not credo.crypto.can_sign: log.error( "Couldn't find any private keys matching your known public keys!" ) cred_path.repository.pub_key_syncer.sync(ask_anyway=True) if not credo.crypto.can_sign: raise CantSign( "No private keys with matching public keys to sign with", repo=cred_path.repository.name) if typ == "amazon": account_id = cred_path.account.account_id(iam_pair=iam_pair) if iam_pair.ask_amazon_for_account() != account_id: raise BadCredential( "The credentials you are importing are for a different account", credentials_account_id=iam_pair.ask_amazon_for_account(), importing_into_account_name=cred_path.account.name, importing_into_account_id=account_id) username = cred_path.user.username(iam_pair=iam_pair) if iam_pair.ask_amazon_for_username() != username: raise BadCredential( "The credentials you are importing are for a different user", credentials_user=iam_pair.ask_amazon_for_username(), importing_into_user=username) creds.keys.add(iam_pair) else: contents = creds.contents contents.keys["provider"] = info contents.keys["role"] = saml_account.encrypted_values() contents.keys["idp_username"] = idp_username creds.save(half_life=half_life) cred_path.repository.synchronize()