mirror of
https://github.com/rwinkhart/sshyp.git
synced 2026-09-03 15:47:18 -04:00
Added basic functionality for new tweak menu + clean-up and optimization of all config menus
Former-commit-id: 59ecc6e80136bd1482aee48cff1b2b1167679836 Former-commit-id: 26043f564acf54fb97bdb3036ce48d57ee786f97
This commit is contained in:
+107
-95
@@ -1,37 +1,36 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from curses import curs_set, newwin, A_REVERSE, KEY_UP, KEY_DOWN, noecho, nocbreak, endwin, initscr, cbreak
|
from curses import A_REVERSE, KEY_DOWN, KEY_UP, cbreak, curs_set, endwin, initscr, newwin, noecho, nocbreak
|
||||||
from curses.textpad import rectangle, Textbox
|
from curses.textpad import rectangle, Textbox
|
||||||
from os import environ, listdir, remove, symlink
|
from os import environ, listdir, remove, symlink
|
||||||
|
from os.path import exists, expanduser, isfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from random import randint
|
from random import randint
|
||||||
from re import sub
|
from re import sub
|
||||||
from shutil import get_terminal_size, which
|
from shutil import get_terminal_size, which
|
||||||
from sshync import make_profile
|
from sshync import make_profile
|
||||||
from sshyp import copy_id_check, string_gen
|
from sshyp import copy_id_check, string_gen
|
||||||
from subprocess import run, PIPE
|
from subprocess import PIPE, run
|
||||||
# PORT START UNAME-IMPORT
|
# PORT START UNAME-IMPORT
|
||||||
from os import uname
|
from os import uname
|
||||||
from os.path import exists, expanduser, isfile
|
|
||||||
# PORT END UNAME-IMPORT
|
# PORT END UNAME-IMPORT
|
||||||
home = expanduser("~")
|
home, stdscr, sshyp_data = expanduser("~"), initscr(), []
|
||||||
sshyp_data = []
|
|
||||||
|
|
||||||
|
|
||||||
# creates a radio selection between the provided options
|
# creates a radio selection between the provided options
|
||||||
def settings_radio(_stdscr, _options, _pretext):
|
def curses_radio(_options, _pretext):
|
||||||
curs_set(0)
|
curs_set(0)
|
||||||
_selected = 0
|
_selected = 0
|
||||||
while True:
|
while True:
|
||||||
_stdscr.clear()
|
stdscr.clear()
|
||||||
_stdscr.addstr(0, 0, _pretext)
|
stdscr.addstr(0, 0, _pretext)
|
||||||
for _i, _option in enumerate(_options):
|
for _i, _option in enumerate(_options):
|
||||||
_y = _i + 2
|
_y = _i + 2
|
||||||
if _i == _selected:
|
if _i == _selected:
|
||||||
_stdscr.addstr(_y, 0, "[*] " + _option, A_REVERSE)
|
stdscr.addstr(_y, 0, "[*] " + _option, A_REVERSE)
|
||||||
else:
|
else:
|
||||||
_stdscr.addstr(_y, 0, "[ ] " + _option)
|
stdscr.addstr(_y, 0, "[ ] " + _option)
|
||||||
_stdscr.refresh()
|
stdscr.refresh()
|
||||||
_key = _stdscr.getch()
|
_key = stdscr.getch()
|
||||||
# update _selected based on user input
|
# update _selected based on user input
|
||||||
if _key == KEY_UP:
|
if _key == KEY_UP:
|
||||||
_selected = (_selected - 1) % len(_options)
|
_selected = (_selected - 1) % len(_options)
|
||||||
@@ -39,19 +38,19 @@ def settings_radio(_stdscr, _options, _pretext):
|
|||||||
_selected = (_selected + 1) % len(_options)
|
_selected = (_selected + 1) % len(_options)
|
||||||
elif _key == ord('\n'):
|
elif _key == ord('\n'):
|
||||||
break
|
break
|
||||||
_stdscr.refresh()
|
stdscr.refresh()
|
||||||
curs_set(1)
|
curs_set(1)
|
||||||
return _selected
|
return _selected
|
||||||
|
|
||||||
|
|
||||||
# creates a text-box input
|
# creates a text-box input
|
||||||
def settings_text(_stdscr, _pretext):
|
def curses_text(_pretext):
|
||||||
_stdscr.clear()
|
stdscr.clear()
|
||||||
_stdscr.addstr(0, 0, _pretext)
|
stdscr.addstr(0, 0, _pretext)
|
||||||
_term_columns = get_terminal_size()[0]
|
_term_columns = get_terminal_size()[0]
|
||||||
_editwin = newwin(1, _term_columns - 2, 3, 1)
|
_editwin = newwin(1, _term_columns - 2, 3, 1)
|
||||||
rectangle(_stdscr, 2, 0, 4, _term_columns - 1)
|
rectangle(stdscr, 2, 0, 4, _term_columns - 1)
|
||||||
_stdscr.refresh()
|
stdscr.refresh()
|
||||||
_box = Textbox(_editwin)
|
_box = Textbox(_editwin)
|
||||||
# let the user edit until ctrl+g/enter is struck
|
# let the user edit until ctrl+g/enter is struck
|
||||||
_box.edit()
|
_box.edit()
|
||||||
@@ -60,36 +59,36 @@ def settings_text(_stdscr, _pretext):
|
|||||||
|
|
||||||
|
|
||||||
# cleanly exit curses
|
# cleanly exit curses
|
||||||
def settings_terminate(_stdscr):
|
def curses_terminate():
|
||||||
nocbreak()
|
nocbreak()
|
||||||
_stdscr.keypad(False) # TODO Needed??
|
|
||||||
noecho()
|
|
||||||
endwin()
|
endwin()
|
||||||
|
|
||||||
|
|
||||||
# device+sync type selection
|
# device+sync type selection
|
||||||
def install_type(_stdscr):
|
def install_type():
|
||||||
_offline_mode = False
|
_offline_mode = False
|
||||||
# PORT START TWEAK-DEVTYPE
|
# PORT START TWEAK-DEVTYPE
|
||||||
_install_type = settings_radio(_stdscr, ('server', 'client (ssh-synchronized)', 'client (offline)'),
|
_install_type = curses_radio(('server', 'client (ssh-synchronized)', 'client (offline)'),
|
||||||
'device + sync type configuration')
|
'device + sync type configuration')
|
||||||
if _install_type == 0:
|
if _install_type == 0:
|
||||||
_dev_type = 'server'
|
_dev_type = 'server'
|
||||||
Path(f"{home}/.config/sshyp/deleted").mkdir(mode=0o700, exist_ok=True)
|
Path(f"{home}/.config/sshyp/deleted").mkdir(mode=0o700, exist_ok=True)
|
||||||
Path(f"{home}/.config/sshyp/whitelist").mkdir(mode=0o700, exist_ok=True)
|
Path(f"{home}/.config/sshyp/whitelist").mkdir(mode=0o700, exist_ok=True)
|
||||||
settings_terminate(_stdscr)
|
curses_terminate()
|
||||||
print(f"\nmake sure the ssh service is running and properly configured")
|
print(f"\nmake sure the ssh service is running and properly configured")
|
||||||
else:
|
else:
|
||||||
_dev_type = 'client'
|
_dev_type = 'client'
|
||||||
if _install_type == 2:
|
if _install_type == 2:
|
||||||
_offline_mode = True
|
_offline_mode = True
|
||||||
|
if isfile(f"{home}/.config/sshyp/sshyp.sshync"):
|
||||||
|
remove(f"{home}/.config/sshyp/sshyp.sshync")
|
||||||
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)
|
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
# PORT END TWEAK-DEVTYPE
|
# PORT END TWEAK-DEVTYPE
|
||||||
return _dev_type, _offline_mode
|
return _dev_type, _offline_mode
|
||||||
|
|
||||||
|
|
||||||
# gpg configuration
|
# gpg configuration
|
||||||
def gpg_config(_stdscr):
|
def gpg_config():
|
||||||
# gpg key selection
|
# gpg key selection
|
||||||
_uid_list = [_item for _item in run(['gpg', '-k', '--with-colons'],
|
_uid_list = [_item for _item in run(['gpg', '-k', '--with-colons'],
|
||||||
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
|
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
|
||||||
@@ -97,7 +96,7 @@ def gpg_config(_stdscr):
|
|||||||
for _uid in _uid_list:
|
for _uid in _uid_list:
|
||||||
_clean_uid_list.append(sub(r':+', ':', _uid).split(':')[4])
|
_clean_uid_list.append(sub(r':+', ':', _uid).split(':')[4])
|
||||||
_clean_uid_list.append('auto-generate')
|
_clean_uid_list.append('auto-generate')
|
||||||
_gpg_id_sel = settings_radio(_stdscr, _clean_uid_list, 'gpg key selection')
|
_gpg_id_sel = curses_radio(_clean_uid_list, 'gpg key selection')
|
||||||
_gpg_id = _clean_uid_list[_gpg_id_sel]
|
_gpg_id = _clean_uid_list[_gpg_id_sel]
|
||||||
if _gpg_id == 'auto-generate':
|
if _gpg_id == 'auto-generate':
|
||||||
print('\na unique gpg key is being generated for you...')
|
print('\na unique gpg key is being generated for you...')
|
||||||
@@ -113,19 +112,19 @@ def gpg_config(_stdscr):
|
|||||||
|
|
||||||
|
|
||||||
# text editor configuration
|
# text editor configuration
|
||||||
def editor_config(_stdscr):
|
def editor_config():
|
||||||
_editor = settings_text(_stdscr, 'enter the name of your preferred text editor:\n\n\n\n\n'
|
_editor = curses_text('enter the name of your preferred text editor:\n\n\n\n\n'
|
||||||
'(ctrl+g/enter to confirm)\n\nexample input: vim')
|
'(ctrl+g/enter to confirm)\n\nexample input: vim')
|
||||||
return _editor
|
return _editor
|
||||||
|
|
||||||
|
|
||||||
# ssh+sshync configuration
|
# ssh+sshync configuration
|
||||||
def ssh_config(_stdscr):
|
def ssh_config():
|
||||||
# ssh+sshync configuration
|
# ssh+sshync configuration
|
||||||
_uiport = settings_text(_stdscr, 'enter the username, ip, and ssh port of your sshyp server:\n\n\n\n\n('
|
_uiport = curses_text('enter the username, ip, and ssh port of your sshyp server:\n\n\n\n\n('
|
||||||
'ctrl+g/enter to confirm)\n\nexample inputs:\n\n ipv4: user@10.10.10.'
|
'ctrl+g/enter to confirm)\n\nexample inputs:\n\n ipv4: user@10.10.10.'
|
||||||
'10:22\n ipv6: user@[2000:2000:2000:2000:2000:2000:2000:2000]:22\n '
|
'10:22\n ipv6: user@[2000:2000:2000:2000:2000:2000:2000:2000]:22\n '
|
||||||
'domain: user@mydomain.com:22').lstrip('[').replace(']', '')
|
'domain: user@mydomain.com:22').lstrip('[').replace(']', '')
|
||||||
_uiport_split = _uiport.split('@')
|
_uiport_split = _uiport.split('@')
|
||||||
_username_ssh = _uiport_split[0]
|
_username_ssh = _uiport_split[0]
|
||||||
_iport = _uiport_split[1].rsplit(':', 1)
|
_iport = _uiport_split[1].rsplit(':', 1)
|
||||||
@@ -138,22 +137,24 @@ def ssh_config(_stdscr):
|
|||||||
|
|
||||||
|
|
||||||
# device id configuration
|
# device id configuration
|
||||||
def dev_id_config(_stdscr):
|
def dev_id_config(_ip, _username_ssh, _port):
|
||||||
for _id in listdir(f"{home}/.config/sshyp/devices"):
|
for _id in listdir(f"{home}/.config/sshyp/devices"):
|
||||||
remove(f"{home}/.config/sshyp/devices/{_id}")
|
remove(f"{home}/.config/sshyp/devices/{_id}")
|
||||||
_device_id_prefix = settings_text(_stdscr, 'name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\n'
|
_device_id_prefix = curses_text('name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\n'
|
||||||
'important:\u001b[0m this id \u001b[4;1mmust\u001b[0m be '
|
'important:\u001b[0m this id \u001b[4;1mmust\u001b[0m be '
|
||||||
'unique amongst your client devices\n\nthis is used to keep '
|
'unique amongst your client devices\n\nthis is used to keep '
|
||||||
'track of database syncing and quick-unlock permissions\n')
|
'track of database syncing and quick-unlock permissions\n')
|
||||||
_device_id_suffix = string_gen('f', randint(24, 48))
|
_device_id_suffix = string_gen('f', randint(24, 48))
|
||||||
_device_id = _device_id_prefix + '-' + _device_id_suffix
|
_device_id = _device_id_prefix + '-' + _device_id_suffix
|
||||||
open(f"{home}/.config/sshyp/devices/{_device_id}", 'w')
|
open(f"{home}/.config/sshyp/devices/{_device_id}", 'w')
|
||||||
|
# test server connection and attempt to register device id
|
||||||
|
copy_id_check(_ip, _username_ssh, _port, _device_id)
|
||||||
return _device_id
|
return _device_id
|
||||||
|
|
||||||
|
|
||||||
# quick-unlock configuration
|
# quick-unlock configuration
|
||||||
def quick_unlock_config(_stdscr):
|
def quick_unlock_config():
|
||||||
_quick_unlock_sel = settings_radio(_stdscr, ('yes', 'no'), 'enable quick-unlock?')
|
_quick_unlock_sel = curses_radio(('yes', 'no'), 'enable quick-unlock?')
|
||||||
if _quick_unlock_sel == 0:
|
if _quick_unlock_sel == 0:
|
||||||
_enabled = 'yes'
|
_enabled = 'yes'
|
||||||
else:
|
else:
|
||||||
@@ -161,6 +162,45 @@ def quick_unlock_config(_stdscr):
|
|||||||
return _enabled
|
return _enabled
|
||||||
|
|
||||||
|
|
||||||
|
# runs secondary configuration menu - clients only
|
||||||
|
def global_menu(_post_setup):
|
||||||
|
# curses initialization
|
||||||
|
noecho()
|
||||||
|
cbreak()
|
||||||
|
stdscr.keypad(True)
|
||||||
|
_options, _choice = [], 4
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not _post_setup:
|
||||||
|
_options.extend(['change device/synchronization types', 'change gpg key', 're-configure ssh',
|
||||||
|
'change device name'])
|
||||||
|
_message, _choice = 'all configuration options:', 0
|
||||||
|
_options.extend(['[OPTIONAL, RECOMMENDED] set custom text editor',
|
||||||
|
'[OPTIONAL, RECOMMENDED] enable/disable quick-unlock',
|
||||||
|
'[OPTIONAL, NOT IMPLEMENTED] su security mode',
|
||||||
|
'[OPTIONAL, NOT IMPLEMENTED] extensions and updates', 'exit/done'])
|
||||||
|
_message = 'required configuration complete - additional configuration options:'
|
||||||
|
_choice += curses_radio(_options, _message)
|
||||||
|
|
||||||
|
if _choice == 0:
|
||||||
|
install_type()
|
||||||
|
elif _choice == 1:
|
||||||
|
gpg_config()
|
||||||
|
elif _choice == 2:
|
||||||
|
ssh_config()
|
||||||
|
elif _choice == 3:
|
||||||
|
dev_id_config()
|
||||||
|
elif _choice == 4:
|
||||||
|
editor_config()
|
||||||
|
elif _choice == 5:
|
||||||
|
quick_unlock_config()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
curses_terminate()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
curses_terminate()
|
||||||
|
|
||||||
|
|
||||||
# runs initial configuration wizard
|
# runs initial configuration wizard
|
||||||
def initial_setup():
|
def initial_setup():
|
||||||
# config directory creation
|
# config directory creation
|
||||||
@@ -178,21 +218,19 @@ def initial_setup():
|
|||||||
# PORT END UNAME-TMP
|
# PORT END UNAME-TMP
|
||||||
|
|
||||||
# curses initialization
|
# curses initialization
|
||||||
_stdscr = initscr()
|
|
||||||
noecho()
|
|
||||||
cbreak()
|
cbreak()
|
||||||
_stdscr.keypad(True)
|
stdscr.keypad(True)
|
||||||
|
|
||||||
# curses menu tree
|
# curses menu tree
|
||||||
try:
|
try:
|
||||||
# device+sync type selection
|
# device+sync type selection
|
||||||
_dev_sync_types = install_type(_stdscr)
|
_dev_sync_types = install_type()
|
||||||
sshyp_data.append(_dev_sync_types[0])
|
sshyp_data.append(_dev_sync_types[0])
|
||||||
|
|
||||||
if _dev_sync_types[0] == 'client':
|
if _dev_sync_types[0] == 'client':
|
||||||
|
|
||||||
# gpg configuration
|
# gpg configuration
|
||||||
sshyp_data.append(gpg_config(_stdscr))
|
sshyp_data.append(gpg_config())
|
||||||
|
|
||||||
# lock file generation (requires gpg configuration)
|
# lock file generation (requires gpg configuration)
|
||||||
if isfile(f"{home}/.config/sshyp/lock.gpg"):
|
if isfile(f"{home}/.config/sshyp/lock.gpg"):
|
||||||
@@ -201,32 +239,27 @@ def initial_setup():
|
|||||||
run(['gpg', '-qr', str(sshyp_data[1]), '-e', f"{home}/.config/sshyp/lock"])
|
run(['gpg', '-qr', str(sshyp_data[1]), '-e', f"{home}/.config/sshyp/lock"])
|
||||||
remove(f"{home}/.config/sshyp/lock")
|
remove(f"{home}/.config/sshyp/lock")
|
||||||
|
|
||||||
# text editor configuration
|
# set default text editor to value of EDITOR environment variable, otherwise default to nano
|
||||||
sshyp_data.append(editor_config(_stdscr))
|
if 'EDITOR' in environ:
|
||||||
|
sshyp_data.append(environ['EDITOR'])
|
||||||
|
else:
|
||||||
|
sshyp_data.append('nano')
|
||||||
|
|
||||||
# online (synchronized mode) configuration
|
# online (synchronized mode) configuration
|
||||||
if not _dev_sync_types[1]:
|
if not _dev_sync_types[1]:
|
||||||
|
|
||||||
# ssh+sshync configuration
|
# ssh+sshync configuration
|
||||||
_ip, _username_ssh, _port = ssh_config(_stdscr)
|
_ip, _username_ssh, _port = ssh_config()
|
||||||
|
|
||||||
# device id configuration
|
# device id configuration
|
||||||
_device_id = dev_id_config(_stdscr)
|
_device_id = dev_id_config(_ip, _username_ssh, _port)
|
||||||
|
|
||||||
# quick-unlock configuration
|
|
||||||
sshyp_data.append(quick_unlock_config(_stdscr))
|
|
||||||
|
|
||||||
# cleanly exit curses
|
# cleanly exit curses
|
||||||
settings_terminate(_stdscr)
|
curses_terminate()
|
||||||
|
|
||||||
# test server connection and attempt to register device id
|
|
||||||
copy_id_check(_ip, _username_ssh, _port, _device_id)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if isfile(f"{home}/.config/sshyp/sshyp.sshync"):
|
|
||||||
remove(f"{home}/.config/sshyp/sshyp.sshync")
|
|
||||||
# cleanly exit curses
|
# cleanly exit curses
|
||||||
settings_terminate(_stdscr)
|
curses_terminate()
|
||||||
|
|
||||||
# PORT START CLIPTOOL
|
# PORT START CLIPTOOL
|
||||||
# check for clipboard tool and display warning if missing
|
# check for clipboard tool and display warning if missing
|
||||||
@@ -241,39 +274,18 @@ def initial_setup():
|
|||||||
f'"{_clipboard_package}" is installed\u001b[0m')
|
f'"{_clipboard_package}" is installed\u001b[0m')
|
||||||
# PORT END CLIPTOOL
|
# PORT END CLIPTOOL
|
||||||
|
|
||||||
|
# write main config file (sshyp-data)
|
||||||
|
with open(f"{home}/.config/sshyp/sshyp-data", 'w') as _config_file:
|
||||||
|
_lines = 0
|
||||||
|
for _item in sshyp_data:
|
||||||
|
_lines += 1
|
||||||
|
_config_file.write(str(_item) + '\n')
|
||||||
|
while _lines < 4:
|
||||||
|
_lines += 1
|
||||||
|
_config_file.write('n')
|
||||||
|
|
||||||
|
global_menu(True)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# cleanly exit curses
|
# cleanly exit curses
|
||||||
settings_terminate(_stdscr)
|
curses_terminate()
|
||||||
|
|
||||||
# write main config file (sshyp-data)
|
|
||||||
with open(f"{home}/.config/sshyp/sshyp-data", 'w') as _config_file:
|
|
||||||
_lines = 0
|
|
||||||
for _item in sshyp_data:
|
|
||||||
_lines += 1
|
|
||||||
_config_file.write(str(_item) + '\n')
|
|
||||||
while _lines < 4:
|
|
||||||
_lines += 1
|
|
||||||
_config_file.write('n')
|
|
||||||
print('\nconfiguration complete\n')
|
|
||||||
|
|
||||||
|
|
||||||
# runs secondary configuration menu
|
|
||||||
def global_menu(_post_setup):
|
|
||||||
# curses initialization
|
|
||||||
_stdscr = initscr()
|
|
||||||
noecho()
|
|
||||||
cbreak()
|
|
||||||
_stdscr.keypad(True)
|
|
||||||
|
|
||||||
# a client device is assumed, the server should never show this menu
|
|
||||||
_options = []
|
|
||||||
if not _post_setup:
|
|
||||||
_options = ['change device/synchronization types', 'change gpg key', 're-configure ssh', 'change device name']
|
|
||||||
_message = 'all configuration options'
|
|
||||||
_options.extend(['[OPTIONAL, RECOMMENDED] set custom text editor',
|
|
||||||
'[OPTIONAL, RECOMMENDED] enable/disable quick-unlock',
|
|
||||||
'[OPTIONAL, NOT IMPLEMENTED] su security mode',
|
|
||||||
'[OPTIONAL, NOT IMPLEMENTED] extensions and updates', 'exit'])
|
|
||||||
_message = 'additional configuration options'
|
|
||||||
|
|
||||||
_choice = settings_radio(_stdscr, _options, _message)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user