def sftp_ctx(): """ Context manager that provides an SFTP client object (an SFTP session across an open SSH Transport) """ transport = paramiko.Transport((current_app.config['SFTP_HOSTNAME'], int(current_app.config['SFTP_PORT']))) authentication_kwarg = {} if current_app.config['SFTP_PASSWORD']: authentication_kwarg['password'] = current_app.config['SFTP_PASSWORD'] elif current_app.config['SFTP_RSA_KEY_FILE']: authentication_kwarg['pkey'] = paramiko.RSAKey( filename=current_app.config['SFTP_RSA_KEY_FILE']) else: raise SFTPCredentialsException transport.connect(username=current_app.config['SFTP_USERNAME'], **authentication_kwarg) sftp = paramiko.SFTPClient.from_transport(transport) try: yield sftp except Exception as e: sentry.captureException() raise paramiko.SFTPError("Exception occurred with SFTP: {}".format(e)) finally: sftp.close() transport.close()
def test_exists_paramiko_error(self, mock_ssh): """ Test run RemoteHost.exists """ remote = remote_host.RemoteHost("53.1.1.1", "ssh_user", "ssh_key_file") exists = remote.remote_exists("true_path") self.assertTrue(exists, "expected true") remote.ftp.stat.assert_called_with("true_path") remote.ftp = mock_ssh mock_ssh.stat.side_effect = paramiko.SFTPError("paramiko.SFTPError") exists = remote.remote_exists("paramiko_exception_path") self.assertFalse(exists, "expected False") remote.ftp.stat.assert_called_with("paramiko_exception_path")
def __init__(self, name: str, mode: str = 'r', bufsize: int = -1, hostname: str = None, port: int = 22, username: str = None, password: str = None, keyfile: str = None, root: str = None, mkdir: bool = True, *args, **kwargs): """Open/create a file over a SSH connection. Args: name: Name of file. mode: Open mode. bufsize: Size of buffer size for SFTP connection. hostname: Name of host to connect to. port: Port on host to connect to. username: Username to log in on host. password: Password for username. keyfile: Path to SSH key on local machine. root: Root directory on host. mkdir: Whether or not to automatically create directories. """ # no root given? if root is None: raise ValueError('No root directory given.') # filename is not allowed to start with a / or contain .. if name.startswith('/') or '..' in name: raise ValueError('Only files within root directory are allowed.') # build filename self.filename = name full_path = os.path.join(root, name) # connect self._ssh = paramiko.SSHClient() self._ssh.load_system_host_keys() self._ssh.connect(hostname, port=port, username=username, password=password, key_filename=keyfile) self._sftp = self._ssh.open_sftp() # need to create directory? path = os.path.dirname(full_path) try: self._sftp.chdir(path) except IOError: if mkdir: self._sftp.mkdir(path) else: raise ValueError( 'Cannot write into sub-directory with disabled mkdir option.' ) # just the code from paramiko.SFTPClient.open imode = 0 if ('r' in mode) or ('+' in mode): imode |= paramiko.sftp.SFTP_FLAG_READ if ('w' in mode) or ('+' in mode) or ('a' in mode): imode |= paramiko.sftp.SFTP_FLAG_WRITE if 'w' in mode: imode |= paramiko.sftp.SFTP_FLAG_CREATE | paramiko.sftp.SFTP_FLAG_TRUNC if 'a' in mode: imode |= paramiko.sftp.SFTP_FLAG_CREATE | paramiko.sftp.SFTP_FLAG_APPEND if 'x' in mode: imode |= paramiko.sftp.SFTP_FLAG_CREATE | paramiko.sftp.SFTP_FLAG_EXCL attrblock = paramiko.SFTPAttributes() t, msg = self._sftp._request(paramiko.sftp.CMD_OPEN, full_path, imode, attrblock) if t != paramiko.sftp.CMD_HANDLE: raise paramiko.SFTPError('Expected handle') handle = msg.get_binary() # init FileIO paramiko.SFTPFile.__init__(self, self._sftp, handle, mode, bufsize)
def open(self, filename: Union[Text, bytes], mode: Text = 'r', bufsize: int = -1) -> SFTPFile: if self._sftp is None: raise paramiko.SFTPError("Expected handle") return self._sftp.open(filename, mode, bufsize)