update 4.3

add account autosave
fix timeout
This commit is contained in:
dharm pimsen 2024-04-07 17:10:22 +07:00
parent c9ffdf5201
commit 79353cf668
19 changed files with 201 additions and 51 deletions

View File

@ -15,8 +15,6 @@ Install from github
```bash ```bash
pip install git+https://github.com/damp11113/PyserSSH.git pip install git+https://github.com/damp11113/PyserSSH.git
``` ```
## Optional Packages
- [damp11113-library](https://github.com/damp11113/damp11113-library)
# Quick Example # Quick Example
```py ```py

View File

@ -5,20 +5,17 @@ with open('README.md', 'r', encoding='utf-8') as f:
setup( setup(
name='PyserSSH', name='PyserSSH',
version='4.2.1', # update pypi (no update for 4.3) version='4.3',
license='MIT', license='MIT',
author='damp11113', author='damp11113',
author_email='damp51252@gmail.com', author_email='damp51252@gmail.com',
packages=find_packages('src'), packages=find_packages('src'),
package_dir={'': 'src'}, package_dir={'': 'src'},
url='https://github.com/damp11113/PyserSSH', url='https://github.com/damp11113/PyserSSH',
description="A easy ssh server", description="python scriptable ssh server library. based on Paramiko",
long_description=long_description, long_description=long_description,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
install_requires=[ install_requires=[
"paramiko" "paramiko"
], ]
extras_require={
"fullsyscom": ["damp11113"]
}
) )

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH
@ -60,7 +60,7 @@ try:
except: except:
os.environ["pyserssh_log"] = "NO" os.environ["pyserssh_log"] = "NO"
if os.environ["pyserssh_log"]: if os.environ["pyserssh_log"] == "NO":
logger = logging.getLogger("PyserSSH") logger = logging.getLogger("PyserSSH")
logger.disabled = True logger.disabled = True

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH
@ -26,16 +26,45 @@ SOFTWARE.
""" """
import pickle import pickle
import time
import atexit
import threading
class AccountManager: class AccountManager:
def __init__(self, anyuser=False, historylimit=10): def __init__(self, anyuser=False, historylimit=10, autosave=False, autosavedelay=60, autoload=False, autoloadfile="autosave_session.ses"):
self.accounts = {} self.accounts = {}
self.anyuser = anyuser self.anyuser = anyuser
self.historylimit = historylimit self.historylimit = historylimit
self.autosavedelay = autosavedelay
self.__autosavework = False
self.__autosaveworknexttime = 0
if self.anyuser: if self.anyuser:
print("history system can't work if 'anyuser' is enable") print("history system can't work if 'anyuser' is enable")
if autoload:
self.load(autoloadfile)
if autosave:
self.__autosavethread = threading.Thread(target=self.__autosave)
self.__autosavethread.start()
atexit.register(self.__saveexit)
def __autosave(self):
self.save("autosave_session.ses")
self.__autosaveworknexttime = time.time() + self.autosavedelay
self.__autosavework = True
while self.__autosavework:
if int(self.__autosaveworknexttime) == int(time.time()):
self.save("autosave_session.ses")
self.__autosaveworknexttime = time.time() + self.autosavedelay
def __saveexit(self):
self.__autosavework = False
self.save("autosave_session.ses")
self.__autosavethread.join()
def validate_credentials(self, username, password): def validate_credentials(self, username, password):
if username in self.accounts and self.accounts[username]["password"] == password or self.anyuser: if username in self.accounts and self.accounts[username]["password"] == password or self.anyuser:
return True return True
@ -66,11 +95,11 @@ class AccountManager:
if username in self.accounts: if username in self.accounts:
self.accounts[username]["permissions"] = new_permissions self.accounts[username]["permissions"] = new_permissions
def save_to_file(self, filename): def save(self, filename="session.ssh"):
with open(filename, 'wb') as file: with open(filename, 'wb') as file:
pickle.dump(self.accounts, file) pickle.dump(self.accounts, file)
def load_from_file(self, filename): def load(self, filename):
try: try:
with open(filename, 'rb') as file: with open(filename, 'rb') as file:
self.accounts = pickle.load(file) self.accounts = pickle.load(file)
@ -114,12 +143,24 @@ class AccountManager:
def get_user_timeout(self, username): def get_user_timeout(self, username):
if username in self.accounts and "timeout" in self.accounts[username]: if username in self.accounts and "timeout" in self.accounts[username]:
return self.accounts[username]["timeout"] return self.accounts[username]["timeout"]
return 0 return None
def set_user_timeout(self, username, timeout=0): def set_user_timeout(self, username, timeout=None):
if username in self.accounts: if username in self.accounts:
self.accounts[username]["timeout"] = timeout self.accounts[username]["timeout"] = timeout
def get_user_last_login(self, username):
if username in self.accounts and "lastlogin" in self.accounts[username]:
return self.accounts[username]["lastlogin"]
return None
def set_user_last_login(self, username, ip, timelogin=time.time()):
if username in self.accounts:
self.accounts[username]["lastlogin"] = {
"ip": ip,
"time": timelogin
}
def add_history(self, username, command): def add_history(self, username, command):
if not self.anyuser: if not self.anyuser:
if username in self.accounts: if username in self.accounts:
@ -158,4 +199,4 @@ class AccountManager:
if username in self.accounts and "lastcommand" in self.accounts[username]: if username in self.accounts and "lastcommand" in self.accounts[username]:
command = self.accounts[username]["lastcommand"] command = self.accounts[username]["lastcommand"]
return command return command
return None # User or history not found return None # User or history not found

View File

@ -1,3 +1,30 @@
"""
PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import inspect import inspect
import shlex import shlex

View File

@ -0,0 +1,38 @@
"""
PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
"""
note
ansi cursor arrow
up - \x1b[A
down - \x1b[B
left - \x1b[D
right - \x1b[C
https://en.wikipedia.org/wiki/ANSI_escape_code
"""

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH
@ -102,17 +102,18 @@ def wait_input(client, prompt="", defaultvalue=None, cursor_scroll=False, echo=T
channel.sendall(b'\r\n') channel.sendall(b'\r\n')
except socket.timeout: except socket.timeout:
channel.setblocking(False)
channel.settimeout(None)
channel.sendall(b'\r\n')
output = "" output = ""
except Exception: except Exception:
channel.setblocking(False)
channel.settimeout(None)
channel.sendall(b'\r\n')
raise raise
else: else:
output = buffer.decode('utf-8') output = buffer.decode('utf-8')
if timeout != 0:
channel.settimeout(0)
channel.setblocking(False)
channel.sendall(b'\r\n')
# Return default value if specified and no input given # Return default value if specified and no input given
if defaultvalue is not None and not output.strip(): if defaultvalue is not None and not output.strip():
return defaultvalue return defaultvalue
@ -144,14 +145,14 @@ def wait_inputkey(client, prompt="", raw=False, timeout=0):
return byte return byte
except socket.timeout: except socket.timeout:
channel.settimeout(0)
channel.setblocking(False) channel.setblocking(False)
channel.settimeout(None)
channel.send("\r\n") channel.send("\r\n")
return None return None
except Exception: except Exception:
if timeout != 0: channel.setblocking(False)
channel.settimeout(0) channel.settimeout(None)
channel.setblocking(False) channel.send("\r\n")
raise raise
def wait_choose(client, choose, prompt="", timeout=0): def wait_choose(client, choose, prompt="", timeout=0):
@ -193,12 +194,12 @@ def wait_choose(client, choose, prompt="", timeout=0):
if chooseindex > chooselen: if chooseindex > chooselen:
chooseindex = chooselen chooseindex = chooselen
except socket.timeout: except socket.timeout:
channel.settimeout(0)
channel.setblocking(False) channel.setblocking(False)
channel.settimeout(None)
channel.send("\r\n") channel.send("\r\n")
return chooseindex return chooseindex
except Exception: except Exception:
if timeout != 0: channel.setblocking(False)
channel.settimeout(0) channel.settimeout(None)
channel.setblocking(False) channel.send("\r\n")
raise raise

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH
@ -147,6 +147,8 @@ class Server:
client_handler["last_activity_time"] = time.time() client_handler["last_activity_time"] = time.time()
client_handler["last_login_time"] = time.time() client_handler["last_login_time"] = time.time()
self.accounts.set_user_last_login(self.client_handlers[channel.getpeername()]["current_user"], peername[0])
#if not any(bh_session.remote_version.split("-")[2].startswith(prefix) for prefix in sftpclient): #if not any(bh_session.remote_version.split("-")[2].startswith(prefix) for prefix in sftpclient):
if not channel.out_window_size == bh_session.default_window_size: if not channel.out_window_size == bh_session.default_window_size:
while self.client_handlers[channel.getpeername()]["windowsize"] == {}: while self.client_handlers[channel.getpeername()]["windowsize"] == {}:
@ -166,7 +168,8 @@ class Server:
client_handler["connecttype"] = "ssh" client_handler["connecttype"] = "ssh"
if self.enainputsystem: if self.enainputsystem:
try: try:
if self.accounts.get_user_timeout(self.client_handlers[channel.getpeername()]["current_user"]) != 0: if self.accounts.get_user_timeout(self.client_handlers[channel.getpeername()]["current_user"]) != None:
channel.setblocking(False)
channel.settimeout(self.accounts.get_user_timeout(self.client_handlers[channel.getpeername()]["current_user"])) channel.settimeout(self.accounts.get_user_timeout(self.client_handlers[channel.getpeername()]["current_user"]))
channel.send(replace_enter_with_crlf(self.accounts.get_prompt(self.client_handlers[channel.getpeername()]["current_user"]) + " ").encode('utf-8')) channel.send(replace_enter_with_crlf(self.accounts.get_prompt(self.client_handlers[channel.getpeername()]["current_user"]) + " ").encode('utf-8'))

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -0,0 +1,38 @@
"""
PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
"""
note
ansi cursor arrow
up - \x1b[A
down - \x1b[B
left - \x1b[D
right - \x1b[C
https://en.wikipedia.org/wiki/ANSI_escape_code
"""

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH
@ -43,7 +43,14 @@ def expect(self, chan, peername, echo=True):
currentuser = self.client_handlers[chan.getpeername()] currentuser = self.client_handlers[chan.getpeername()]
try: try:
while True: while True:
byte = chan.recv(1) try:
byte = chan.recv(1)
except socket.timeout:
chan.setblocking(False)
chan.settimeout(None)
chan.close()
raise EOFError()
self._handle_event("onrawtype", self.client_handlers[chan.getpeername()], byte) self._handle_event("onrawtype", self.client_handlers[chan.getpeername()], byte)
self.client_handlers[chan.getpeername()]["last_activity_time"] = time.time() self.client_handlers[chan.getpeername()]["last_activity_time"] = time.time()
@ -151,9 +158,9 @@ def expect(self, chan, peername, echo=True):
self.accounts.add_history(currentuser["current_user"], command) self.accounts.add_history(currentuser["current_user"], command)
if command.strip() != "": if command.strip() != "":
if self.accounts.get_user_timeout(self.client_handlers[chan.getpeername()]["current_user"]) != 0: if self.accounts.get_user_timeout(self.client_handlers[chan.getpeername()]["current_user"]) != None:
chan.settimeout(0)
chan.setblocking(False) chan.setblocking(False)
chan.settimeout(None)
try: try:
if self.enasyscom: if self.enasyscom:
@ -179,13 +186,13 @@ def expect(self, chan, peername, echo=True):
except: except:
logger.error("Send error") logger.error("Send error")
if self.accounts.get_user_timeout(self.client_handlers[chan.getpeername()]["current_user"]) != 0: chan.setblocking(False)
chan.settimeout(None)
if self.accounts.get_user_timeout(self.client_handlers[chan.getpeername()]["current_user"]) != None:
chan.setblocking(False)
chan.settimeout(self.accounts.get_user_timeout(self.client_handlers[chan.getpeername()]["current_user"])) chan.settimeout(self.accounts.get_user_timeout(self.client_handlers[chan.getpeername()]["current_user"]))
except socket.timeout:
chan.settimeout(0)
chan.setblocking(False)
chan.close()
except Exception as e: except Exception as e:
logger.error(str(e)) logger.error(str(e))
finally: finally:

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH

View File

@ -1,5 +1,5 @@
""" """
PyserSSH - A SSH server. For more info visit https://github.com/damp11113/PyserSSH PyserSSH - A Scriptable SSH server. For more info visit https://github.com/damp11113/PyserSSH
Copyright (C) 2023-2024 damp11113 (MIT) Copyright (C) 2023-2024 damp11113 (MIT)
Visit https://github.com/damp11113/PyserSSH Visit https://github.com/damp11113/PyserSSH