33import fsspec
44import socket
55import dlt
6- from dlt .common .configuration .specs import SFTPCredentials
76
87from dlt .common .json import json
98from dlt .common .configuration .inject import with_config
1211
1312from tests .load .utils import ALL_FILESYSTEM_DRIVERS
1413
14+ from paramiko .auth_strategy import Password
15+ from paramiko import RSAKey , Transport
16+ from paramiko .ssh_exception import SSHException
17+
1518if "sftp" not in ALL_FILESYSTEM_DRIVERS :
1619 pytest .skip ("sftp filesystem driver not configured" , allow_module_level = True )
1720
@@ -126,7 +129,17 @@ def states():
126129 assert sorted (result_states ) == sorted (expected_states )
127130
128131
129- def run_sftp_auth (user , password = None , key = None , passphrase = None , sock = None ):
132+ def run_sftp_auth (
133+ user ,
134+ password = None ,
135+ pkey = None ,
136+ key = None ,
137+ passphrase = None ,
138+ sock = None ,
139+ disabled_algorithms = None ,
140+ transport_factory = None ,
141+ auth_strategy = None ,
142+ ):
130143 env_vars = {
131144 "SOURCES__FILESYSTEM__BUCKET_URL" : "sftp://localhost" ,
132145 "SOURCES__FILESYSTEM__CREDENTIALS__SFTP_PORT" : "2222" ,
@@ -144,6 +157,14 @@ def run_sftp_auth(user, password=None, key=None, passphrase=None, sock=None):
144157
145158 config = get_config ()
146159
160+ if disabled_algorithms :
161+ config .credentials .sftp_disabled_algorithms = disabled_algorithms # type: ignore[union-attr]
162+ if transport_factory :
163+ config .credentials .sftp_transport_factory = transport_factory # type: ignore[union-attr]
164+ if auth_strategy :
165+ config .credentials .sftp_auth_strategy = auth_strategy # type: ignore[union-attr]
166+ if pkey :
167+ config .credentials .sftp_pkey = pkey # type: ignore[union-attr]
147168 if sock :
148169 config .credentials .sftp_sock = sock # type: ignore[union-attr]
149170
@@ -163,6 +184,17 @@ def test_filesystem_sftp_auth_private_key_protected():
163184 run_sftp_auth ("bobby" , key = get_key_path ("bobby" ), passphrase = "passphrase123" )
164185
165186
187+ def test_filesystem_sftp_auth_pkey ():
188+ run_sftp_auth ("foo" , pkey = RSAKey .from_private_key_file (get_key_path ("foo" )))
189+
190+
191+ def test_filesystem_sftp_pkey_auth_pkey_protected ():
192+ run_sftp_auth (
193+ "bobby" ,
194+ pkey = RSAKey .from_private_key_file (filename = get_key_path ("bobby" ), password = "passphrase123" ),
195+ )
196+
197+
166198# Test requires - ssh_agent with user's bobby key loaded. The commands and file names required are:
167199# eval "$(ssh-agent -s)"
168200# cp /path/to/tests/load/filesystem_sftp/bobby_rsa* ~/.ssh/id_rsa
@@ -190,3 +222,52 @@ def test_filesystem_sftp_with_socket():
190222 sock .close ()
191223 with pytest .raises (OSError ):
192224 run_sftp_auth ("billy" , key = get_key_path ("billy" ), sock = sock )
225+
226+
227+ class TaggedTransport (Transport ):
228+ """A Transport class that tags itself so we can detect it in tests."""
229+
230+ def __init__ (self , sock , ** kwargs ):
231+ super ().__init__ (sock , ** kwargs )
232+ # Add any custom state or markers you like:
233+ self .factory_tag = "used-tagged-transport"
234+
235+
236+ def test_filesystem_sftp_with_tagged_transport ():
237+ created = []
238+
239+ def factory (sock , ** kwargs ):
240+ t = TaggedTransport (sock , ** kwargs )
241+ created .append (t )
242+ return t
243+
244+ run_sftp_auth ("foo" , key = get_key_path ("foo" ), transport_factory = factory )
245+
246+ # Verify it was used
247+ assert len (created ) == 1 , "Custom transport factory never ran"
248+ transport = created [0 ]
249+ assert isinstance (transport , TaggedTransport )
250+ assert transport .factory_tag == "used-tagged-transport"
251+
252+ # And ensure it's still functional
253+ sftp = transport .open_sftp_client ()
254+ assert hasattr (sftp , "listdir" )
255+ sftp .close ()
256+
257+
258+ def test_filesystem_sftp_disabled_algorithms ():
259+ # we know foo’s server uses rsa keys
260+ with pytest .raises (SSHException ):
261+ run_sftp_auth (
262+ "foo" ,
263+ key = get_key_path ("foo" ),
264+ disabled_algorithms = {"pubkeys" : ["ssh-rsa" , "rsa-sha2-256" , "rsa-sha2-512" ]},
265+ )
266+
267+
268+ def test_filesystem_sftp_auth_strategy ():
269+ # Verify that passing an alternate auth_strategy makes it through config.
270+ run_sftp_auth (
271+ "foo" ,
272+ auth_strategy = Password ("foo" , lambda : "pass" ),
273+ )
0 commit comments