2021-01-07 17:50:31 +00:00
|
|
|
"""
|
|
|
|
It's possible to create/rename/delete files and directories in Tahoe-LAFS using
|
|
|
|
SFTP.
|
2021-01-26 15:06:17 +00:00
|
|
|
|
|
|
|
These tests use Paramiko, rather than Twisted's Conch, because:
|
|
|
|
|
|
|
|
1. It's a different implementation, so we're not testing Conch against
|
|
|
|
itself.
|
|
|
|
|
|
|
|
2. Its API is much simpler to use.
|
2021-01-07 17:50:31 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
from future.utils import PY2
|
|
|
|
if PY2:
|
|
|
|
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
|
|
|
|
|
2021-10-26 11:16:24 +00:00
|
|
|
import os.path
|
2021-01-07 18:27:34 +00:00
|
|
|
from posixpath import join
|
|
|
|
from stat import S_ISDIR
|
|
|
|
|
2021-01-07 17:50:31 +00:00
|
|
|
from paramiko import SSHClient
|
|
|
|
from paramiko.client import AutoAddPolicy
|
|
|
|
from paramiko.sftp_client import SFTPClient
|
2021-01-07 18:27:34 +00:00
|
|
|
from paramiko.ssh_exception import AuthenticationException
|
2021-01-08 18:33:22 +00:00
|
|
|
from paramiko.rsakey import RSAKey
|
2021-01-07 17:50:31 +00:00
|
|
|
|
2021-01-07 18:27:34 +00:00
|
|
|
import pytest
|
2021-01-07 17:50:31 +00:00
|
|
|
|
2021-01-21 18:54:22 +00:00
|
|
|
from .util import generate_ssh_key, run_in_thread
|
2021-01-08 18:41:04 +00:00
|
|
|
|
2021-01-07 18:27:34 +00:00
|
|
|
|
2021-10-26 11:16:24 +00:00
|
|
|
def connect_sftp(connect_args):
|
2021-01-07 18:27:34 +00:00
|
|
|
"""Create an SFTP client."""
|
2021-01-07 17:50:31 +00:00
|
|
|
client = SSHClient()
|
|
|
|
client.set_missing_host_key_policy(AutoAddPolicy)
|
2021-01-08 18:33:22 +00:00
|
|
|
client.connect("localhost", port=8022, look_for_keys=False,
|
|
|
|
allow_agent=False, **connect_args)
|
2021-01-07 17:50:31 +00:00
|
|
|
sftp = SFTPClient.from_transport(client.get_transport())
|
2021-01-07 18:27:34 +00:00
|
|
|
|
|
|
|
def rmdir(path, delete_root=True):
|
|
|
|
for f in sftp.listdir_attr(path=path):
|
|
|
|
childpath = join(path, f.filename)
|
|
|
|
if S_ISDIR(f.st_mode):
|
|
|
|
rmdir(childpath)
|
|
|
|
else:
|
|
|
|
sftp.remove(childpath)
|
|
|
|
if delete_root:
|
|
|
|
sftp.rmdir(path)
|
|
|
|
|
|
|
|
# Delete any files left over from previous tests :(
|
|
|
|
rmdir("/", delete_root=False)
|
|
|
|
|
|
|
|
return sftp
|
|
|
|
|
|
|
|
|
2021-01-21 18:54:22 +00:00
|
|
|
@run_in_thread
|
2021-01-08 18:41:04 +00:00
|
|
|
def test_bad_account_password_ssh_key(alice, tmpdir):
|
2021-01-07 18:59:57 +00:00
|
|
|
"""
|
2021-10-26 11:16:24 +00:00
|
|
|
Can't login with unknown username, any password, or wrong SSH pub key.
|
2021-01-07 18:59:57 +00:00
|
|
|
"""
|
2021-10-26 11:16:24 +00:00
|
|
|
# Any password, wrong username:
|
|
|
|
for u, p in [("alice-key", "wrong"), ("someuser", "password")]:
|
2021-01-07 18:27:34 +00:00
|
|
|
with pytest.raises(AuthenticationException):
|
2021-01-07 18:59:57 +00:00
|
|
|
connect_sftp(connect_args={
|
|
|
|
"username": u, "password": p,
|
|
|
|
})
|
2021-01-08 18:41:04 +00:00
|
|
|
|
2021-10-26 11:16:24 +00:00
|
|
|
another_key = os.path.join(str(tmpdir), "ssh_key")
|
2021-01-08 18:41:04 +00:00
|
|
|
generate_ssh_key(another_key)
|
2021-10-26 11:16:24 +00:00
|
|
|
good_key = RSAKey(filename=os.path.join(alice.node_dir, "private", "ssh_client_rsa_key"))
|
2021-01-08 18:41:04 +00:00
|
|
|
bad_key = RSAKey(filename=another_key)
|
|
|
|
|
|
|
|
# Wrong key:
|
|
|
|
with pytest.raises(AuthenticationException):
|
|
|
|
connect_sftp(connect_args={
|
2021-10-26 11:16:24 +00:00
|
|
|
"username": "alice-key", "pkey": bad_key,
|
2021-01-08 18:41:04 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
# Wrong username:
|
|
|
|
with pytest.raises(AuthenticationException):
|
|
|
|
connect_sftp(connect_args={
|
|
|
|
"username": "someoneelse", "pkey": good_key,
|
|
|
|
})
|
2021-01-07 18:59:57 +00:00
|
|
|
|
2021-10-26 11:16:24 +00:00
|
|
|
def sftp_client_key(node):
|
|
|
|
return RSAKey(
|
|
|
|
filename=os.path.join(node.node_dir, "private", "ssh_client_rsa_key"),
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_sftp_client_key_exists(alice, alice_sftp_client_key_path):
|
|
|
|
"""
|
|
|
|
Weakly validate the sftp client key fixture by asserting that *something*
|
|
|
|
exists at the supposed key path.
|
|
|
|
"""
|
|
|
|
assert os.path.exists(alice_sftp_client_key_path)
|
2021-01-07 18:59:57 +00:00
|
|
|
|
2021-01-21 18:54:22 +00:00
|
|
|
@run_in_thread
|
2021-01-07 18:59:57 +00:00
|
|
|
def test_ssh_key_auth(alice):
|
|
|
|
"""It's possible to login authenticating with SSH public key."""
|
2021-10-26 11:16:24 +00:00
|
|
|
key = sftp_client_key(alice)
|
2021-01-07 18:59:57 +00:00
|
|
|
sftp = connect_sftp(connect_args={
|
2021-10-26 11:16:24 +00:00
|
|
|
"username": "alice-key", "pkey": key
|
2021-01-07 18:59:57 +00:00
|
|
|
})
|
|
|
|
assert sftp.listdir() == []
|
2021-01-07 18:27:34 +00:00
|
|
|
|
|
|
|
|
2021-01-21 18:54:22 +00:00
|
|
|
@run_in_thread
|
2021-01-07 18:27:34 +00:00
|
|
|
def test_read_write_files(alice):
|
|
|
|
"""It's possible to upload and download files."""
|
2021-10-26 11:16:24 +00:00
|
|
|
sftp = connect_sftp(connect_args={
|
|
|
|
"username": "alice-key",
|
|
|
|
"pkey": sftp_client_key(alice),
|
|
|
|
})
|
2021-01-26 15:06:57 +00:00
|
|
|
with sftp.file("myfile", "wb") as f:
|
|
|
|
f.write(b"abc")
|
|
|
|
f.write(b"def")
|
|
|
|
|
|
|
|
with sftp.file("myfile", "rb") as f:
|
|
|
|
assert f.read(4) == b"abcd"
|
|
|
|
assert f.read(2) == b"ef"
|
|
|
|
assert f.read(1) == b""
|
2021-01-07 18:27:34 +00:00
|
|
|
|
|
|
|
|
2021-01-21 18:54:22 +00:00
|
|
|
@run_in_thread
|
2021-01-07 18:27:34 +00:00
|
|
|
def test_directories(alice):
|
|
|
|
"""
|
|
|
|
It's possible to create, list directories, and create and remove files in
|
|
|
|
them.
|
|
|
|
"""
|
2021-10-26 11:16:24 +00:00
|
|
|
sftp = connect_sftp(connect_args={
|
|
|
|
"username": "alice-key",
|
|
|
|
"pkey": sftp_client_key(alice),
|
|
|
|
})
|
2021-01-07 18:27:34 +00:00
|
|
|
assert sftp.listdir() == []
|
|
|
|
|
|
|
|
sftp.mkdir("childdir")
|
|
|
|
assert sftp.listdir() == ["childdir"]
|
|
|
|
|
|
|
|
with sftp.file("myfile", "wb") as f:
|
|
|
|
f.write(b"abc")
|
|
|
|
assert sorted(sftp.listdir()) == ["childdir", "myfile"]
|
|
|
|
|
|
|
|
sftp.chdir("childdir")
|
|
|
|
assert sftp.listdir() == []
|
|
|
|
|
|
|
|
with sftp.file("myfile2", "wb") as f:
|
|
|
|
f.write(b"def")
|
|
|
|
assert sftp.listdir() == ["myfile2"]
|
|
|
|
|
|
|
|
sftp.chdir(None) # root
|
|
|
|
with sftp.file("childdir/myfile2", "rb") as f:
|
|
|
|
assert f.read() == b"def"
|
|
|
|
|
|
|
|
sftp.remove("myfile")
|
|
|
|
assert sftp.listdir() == ["childdir"]
|
|
|
|
|
|
|
|
sftp.rmdir("childdir")
|
|
|
|
assert sftp.listdir() == []
|
2021-01-07 18:59:57 +00:00
|
|
|
|
|
|
|
|
2021-01-21 18:54:22 +00:00
|
|
|
@run_in_thread
|
2021-01-07 18:59:57 +00:00
|
|
|
def test_rename(alice):
|
|
|
|
"""Directories and files can be renamed."""
|
2021-10-26 11:16:24 +00:00
|
|
|
sftp = connect_sftp(connect_args={
|
|
|
|
"username": "alice-key",
|
|
|
|
"pkey": sftp_client_key(alice),
|
|
|
|
})
|
2021-01-07 18:59:57 +00:00
|
|
|
sftp.mkdir("dir")
|
|
|
|
|
|
|
|
filepath = join("dir", "file")
|
|
|
|
with sftp.file(filepath, "wb") as f:
|
|
|
|
f.write(b"abc")
|
|
|
|
|
|
|
|
sftp.rename(filepath, join("dir", "file2"))
|
|
|
|
sftp.rename("dir", "dir2")
|
|
|
|
|
|
|
|
with sftp.file(join("dir2", "file2"), "rb") as f:
|
|
|
|
assert f.read() == b"abc"
|