Skip to content

Commit c304e80

Browse files
added more type hints
1 parent 32b2ec4 commit c304e80

File tree

23 files changed

+450
-186
lines changed

23 files changed

+450
-186
lines changed

ssh_proxy_server/authentication.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import socket
66

77
from typing import (
8+
TYPE_CHECKING,
89
Optional,
910
List,
1011
Tuple,
@@ -20,9 +21,12 @@
2021
from sshpubkeys import SSHKey # type: ignore
2122
from typeguard import typechecked
2223

24+
import ssh_proxy_server
2325
from ssh_proxy_server.clients.ssh import SSHClient, AuthenticationMethod
2426
from ssh_proxy_server.exceptions import MissingHostException
25-
from ssh_proxy_server.session import Session
27+
28+
if TYPE_CHECKING:
29+
from ssh_proxy_server.session import Session
2630

2731

2832
@typechecked
@@ -62,8 +66,8 @@ def parse_service_accept(self, m: paramiko.message.Message) -> None:
6266

6367
# For compatibility with paramiko, we need to generate a random private key and replace
6468
# the public key with our data.
65-
key = paramiko.RSAKey.generate(2048)
66-
key.public_blob = public_key # type: ignore
69+
key: PKey = paramiko.RSAKey.generate(2048)
70+
key.public_blob = public_key
6771
transport.auth_publickey(username, key)
6872
valid_key = True
6973
except paramiko.ssh_exception.AuthenticationException:
@@ -166,7 +170,7 @@ def parser_arguments(cls) -> None:
166170
)
167171

168172
@typechecked
169-
def __init__(self, session: Session) -> None:
173+
def __init__(self, session: 'ssh_proxy_server.session.Session') -> None:
170174
super().__init__()
171175
self.session = session
172176

@@ -223,7 +227,7 @@ def authenticate(
223227
self.session.username_provided = username
224228
self.session.password_provided = password
225229
if username:
226-
remote_credentials = self.get_remote_host_credentials(username, password, key)
230+
remote_credentials: RemoteCredentials = self.get_remote_host_credentials(username, password, key)
227231
self.session.username = remote_credentials.username
228232
self.session.password = remote_credentials.password
229233
self.session.key = remote_credentials.key

ssh_proxy_server/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
Authenticator,
2020
AuthenticatorPassThrough
2121
)
22-
from ssh_proxy_server.interfaces import (
22+
from ssh_proxy_server.interfaces.server import (
2323
BaseServerInterface,
2424
ServerInterface
2525
)

ssh_proxy_server/clients/sftp.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import logging
22
import os
3+
from typing import (
4+
Optional
5+
)
36

47
import paramiko
58

@@ -14,7 +17,7 @@ def __init__(self, host, port, method, password, user, key, session):
1417
self.subsystem_count = 0
1518

1619
@classmethod
17-
def from_client(cls, ssh_client):
20+
def from_client(cls, ssh_client: Optional[SSHClient]):
1821
if ssh_client is None:
1922
logging.error('error creating sftp client - no ssh client!')
2023
return None
@@ -32,6 +35,9 @@ def from_client(cls, ssh_client):
3235
ssh_client.key,
3336
ssh_client.session
3437
)
38+
if ssh_client.transport is None:
39+
logging.debug("ssh_client does not have a transport")
40+
return None
3541
sftp._sftp = paramiko.SFTPClient.from_transport(ssh_client.transport)
3642
sftp.connected = True
3743
return sftp

ssh_proxy_server/clients/ssh.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
from typing import (
55
TYPE_CHECKING,
6+
Optional,
67
Text
78
)
9+
from paramiko.pkey import PKey
810

911
from typeguard import typechecked
1012
import paramiko
@@ -34,7 +36,16 @@ class SSHClient(BaseSSHClient):
3436
CIPHERS = None
3537

3638
#@typechecked
37-
def __init__(self, host, port, method, password, user, key, session: 'ssh_proxy_server.session.Session') -> None:
39+
def __init__(
40+
self,
41+
host: Text,
42+
port: int,
43+
method: AuthenticationMethod,
44+
password: Optional[Text],
45+
user: Text,
46+
key: Optional[PKey],
47+
session: 'ssh_proxy_server.session.Session'
48+
) -> None:
3849
self.session = session
3950
self.host = host
4051
self.port = port

ssh_proxy_server/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ class MissingHostException(Exception):
1616

1717
class KeyGenerationError(Exception):
1818
pass
19+
20+
21+
class MissingClient(Exception):
22+
pass

ssh_proxy_server/forwarders/agent.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import time
2+
from typing import Tuple, List, Union
23

3-
from paramiko.agent import Agent, AgentServerProxy, AgentClientProxy
4+
from paramiko.agent import Agent, AgentKey, AgentServerProxy, AgentClientProxy
5+
from paramiko.transport import Transport
6+
from paramiko.channel import Channel
47
import os
58

9+
from typeguard import typechecked
10+
611
class AgentProxy(object):
712

8-
def __init__(self, transport) -> None:
9-
self.agents = []
13+
@typechecked
14+
def __init__(self, transport: Transport) -> None:
15+
self.agents: List[Union[Agent, AgentClientProxy]] = []
1016
self.transport = transport
1117
a = AgentServerProxy(self.transport)
1218
os.environ.update(a.get_env())
@@ -18,18 +24,22 @@ def __init__(self, transport) -> None:
1824
# agent is still sending over the channel
1925
# agent.close()
2026

21-
def get_keys(self):
27+
@typechecked
28+
def get_keys(self) -> Tuple[AgentKey, ...]:
2229
return self.keys
2330

24-
def forward_agent(self, chanClient):
25-
chanClient.request_forward_agent(self._forward_agent_handler)
31+
@typechecked
32+
def forward_agent(self, chanClient: Channel) -> bool:
33+
return chanClient.request_forward_agent(self._forward_agent_handler)
2634

27-
def _forward_agent_handler(self, chanRemote):
35+
@typechecked
36+
def _forward_agent_handler(self, chanRemote: Channel) -> None:
2837
agent = AgentServerProxy(self.transport)
2938
os.environ.update(agent.get_env())
3039
time.sleep(0.1)
3140
self.agents.append(AgentClientProxy(chanRemote))
3241

33-
def close(self):
42+
@typechecked
43+
def close(self) -> None:
3444
for a in self.agents:
3545
a.close()

ssh_proxy_server/forwarders/base.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
import logging
1+
from typing import (
2+
TYPE_CHECKING
3+
)
24

35
from enhancements.modules import BaseModule
6+
import paramiko
7+
from typeguard import typechecked
8+
9+
import ssh_proxy_server
10+
from ssh_proxy_server.exceptions import MissingClient
11+
if TYPE_CHECKING:
12+
from ssh_proxy_server.session import Session
413

514

615
class BaseForwarder(BaseModule):
@@ -11,29 +20,35 @@ class BaseForwarder(BaseModule):
1120
# Slow file transmission
1221
BUF_LEN = 65536*100
1322

14-
def __init__(self, session):
23+
@typechecked
24+
def __init__(self, session: 'ssh_proxy_server.session.Session') -> None:
1525
super().__init__()
16-
self.server_channel = session.ssh_client.transport.open_session()
26+
if session.ssh_client is None or session.ssh_client.transport is None:
27+
raise MissingClient("session.ssh_client is None")
28+
self.server_channel: paramiko.Channel = session.ssh_client.transport.open_session()
1729
if session.agent is not None:
1830
session.agent.forward_agent(self.server_channel)
19-
self.channel = None
20-
self.session = session
31+
self.channel: paramiko.Channel = None
32+
self.session: 'Session' = session
2133

2234
# pass environment variables from client to server
2335
for env_name, env_value in self.session.env_requests.items():
2436
self.server_channel.set_environment_variable(env_name, env_value)
2537

26-
def forward(self):
38+
@typechecked
39+
def forward(self) -> None:
2740
raise NotImplementedError
2841

29-
def close_session(self, channel):
42+
@typechecked
43+
def close_session(self, channel: paramiko.Channel) -> None:
3044
channel.lock.acquire()
3145
if not channel.closed:
3246
channel.lock.release()
3347
channel.close()
3448
if channel.lock.locked():
3549
channel.lock.release()
3650

37-
def _closed(self, channel):
51+
@typechecked
52+
def _closed(self, channel: paramiko.Channel) -> bool:
3853
#return channel.closed or channel.eof_received or channel.eof_sent or not channel.active
3954
return channel.closed or not channel.active

ssh_proxy_server/forwarders/sftp.py

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,41 @@
11
import logging
2+
from typing import (
3+
TYPE_CHECKING,
4+
Optional,
5+
Union, Type
6+
)
7+
28
import paramiko
39
from enhancements.modules import BaseModule
10+
from typeguard import typechecked
11+
12+
import ssh_proxy_server
13+
from ssh_proxy_server.interfaces.sftp import BaseSFTPServerInterface
14+
15+
if TYPE_CHECKING:
16+
from ssh_proxy_server.session import Session
417

518

619
class SFTPHandlerBasePlugin(BaseModule):
720

8-
def __init__(self, sftp, filename):
21+
@typechecked
22+
def __init__(self, sftp, filename) -> None:
923
super().__init__()
1024
self.filename = filename
1125
self.sftp = sftp
1226

1327
@classmethod
14-
def get_interface(cls):
28+
@typechecked
29+
def get_interface(cls) -> Optional[Type[BaseSFTPServerInterface]]:
1530
return None
1631

1732
@classmethod
18-
def get_file_handle(cls):
19-
return None
33+
@typechecked
34+
def get_file_handle(cls) -> Type['SFTPBaseHandle']:
35+
return SFTPBaseHandle
2036

21-
def close(self):
37+
@typechecked
38+
def close(self) -> None:
2239
pass
2340

2441
def handle_data(self, data, *, offset=None, length=None):
@@ -32,24 +49,34 @@ class SFTPHandlerPlugin(SFTPHandlerBasePlugin):
3249

3350
class SFTPBaseHandle(paramiko.SFTPHandle):
3451

35-
def __init__(self, session, plugin, filename, flags=0):
52+
@typechecked
53+
def __init__(
54+
self, session: 'ssh_proxy_server.session.Session', plugin, filename, flags: int = 0
55+
) -> None:
3656
super().__init__(flags)
3757
self.session = session
3858
self.plugin = plugin(self, filename)
39-
self.writefile = None
40-
self.readfile = None
59+
self.writefile: Optional[paramiko.sftp_file.SFTPFile] = None
60+
self.readfile: Optional[paramiko.sftp_file.SFTPFile] = None
4161

42-
def close(self):
62+
@typechecked
63+
def close(self) -> None:
4364
super().close()
4465
self.plugin.close()
4566

46-
def read(self, offset, length):
67+
@typechecked
68+
def read(self, offset, length) -> Union[bytes, int]:
4769
logging.debug("R_OFFSET: %s", offset)
70+
if self.readfile is None:
71+
return paramiko.sftp.SFTP_FAILURE
4872
data = self.readfile.read(length)
4973
return self.plugin.handle_data(data, length=length)
5074

51-
def write(self, offset, data):
75+
@typechecked
76+
def write(self, offset: int, data: bytes) -> int:
5277
logging.debug("W_OFFSET: %s", offset)
5378
data = self.plugin.handle_data(data, offset=offset)
79+
if self.writefile is None:
80+
return paramiko.sftp.SFTP_FAILURE
5481
self.writefile.write(data)
55-
return paramiko.SFTP_OK
82+
return paramiko.sftp.SFTP_OK

0 commit comments

Comments
 (0)