Compare commits

...
7 Commits
6 changed files with 81 additions and 27 deletions
+22
View File
@@ -1,3 +1,25 @@
sshyp v1.5.3
01/08/2024
the fortified flock update - patch three
this release is a quick hot-fix for some bugs that were noticed after the release of v1.5.2
fixes/optimizations:
- fixed a regression with the entry list causing entries not part of a subdirectory to appear
in-line with the entry list header
- fixed extension group ownership under FreeBSD
^ the "wheel" group is now used where previously the non-existent "root" group was used
- fixed notes sometimes being appended in the wrong position after editing an incomplete entry
- addressed flake8 suggestions (slight optimization)
- removed unnecessary empty variable declarations
^ these were previously included to silence PyCharm's PEP8 warnings
^ the project is now primarily developed in VSCodium with the flake8 linter
^ the only change to flake8 defaults in the line length limit (79->120)
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.5.2 sshyp v1.5.2
01/07/2024 01/07/2024
+1 -1
View File
@@ -1,4 +1,4 @@
.TH sshyp 1 "07 January 2024" "v1.5.2" "sshyp man page" .TH sshyp 1 "08 January 2024" "v1.5.3" "sshyp man page"
.SH NAME .SH NAME
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store. \fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
.SH SYNOPSIS .SH SYNOPSIS
+16 -23
View File
@@ -24,6 +24,8 @@ def entry_list_gen(_directory=f"{home}/.local/share/sshyp/"):
_color_alternator = 1 _color_alternator = 1
if _ran: if _ran:
print(f"\n\n\u001b[38;5;7;48;5;8m{_root.replace(f'{home}/.local/share/sshyp', '', 1)}/\u001b[0m") print(f"\n\n\u001b[38;5;7;48;5;8m{_root.replace(f'{home}/.local/share/sshyp', '', 1)}/\u001b[0m")
elif _root == f"{home}/.local/share/sshyp/" and len(_files) > 0:
print("\n\n\u001b[38;5;7;48;5;8m/\u001b[0m")
_char_counter = 0 _char_counter = 0
for _filename in sorted(_files): for _filename in sorted(_files):
if _color_alternator > 0: if _color_alternator > 0:
@@ -50,7 +52,7 @@ def entry_reader(_decrypted_entry):
if pass_show: if pass_show:
_entry_password = f'\u001b[38;5;10m{_decrypted_entry[0]}\u001b[0m' _entry_password = f'\u001b[38;5;10m{_decrypted_entry[0]}\u001b[0m'
else: else:
_entry_password = f'\u001b[38;5;3mend command in "--show" or "-s" to view\u001b[0m' _entry_password = '\u001b[38;5;3mend command in "--show" or "-s" to view\u001b[0m'
print() print()
for _num in range(len(_decrypted_entry)): for _num in range(len(_decrypted_entry)):
try: try:
@@ -67,7 +69,7 @@ def entry_reader(_decrypted_entry):
print('\u001b[38;5;7;48;5;8mnotes:\u001b[0m\n' + _decrypted_entry[_num]) print('\u001b[38;5;7;48;5;8mnotes:\u001b[0m\n' + _decrypted_entry[_num])
if _notes_flag == 1: if _notes_flag == 1:
try: try:
_line_test = _decrypted_entry[_num + 1] _ = _decrypted_entry[_num + 1]
except IndexError: except IndexError:
print() print()
except IndexError: except IndexError:
@@ -136,17 +138,15 @@ def edit_note(_note_lines, _exit_on_match=False):
return _new_note return _new_note
# encrypts an entry and cleans up the temporary files # encrypts an entry
def encrypt(_entry_data, _entry_dir, _gpg_id): def encrypt(_entry_data, _entry_dir, _gpg_id):
_bytes_data = '\n'.join(_entry_data).rstrip().encode() _bytes_data = '\n'.join(_entry_data).rstrip().encode()
_encrypted_data = run(('gpg', '-qr', str(_gpg_id), '-e'), input=_bytes_data, stdout=PIPE).stdout _encrypted_data = run(('gpg', '-qr', str(_gpg_id), '-e'), input=_bytes_data, stdout=PIPE).stdout
open(_entry_dir + '.gpg', 'wb').write(_encrypted_data) open(_entry_dir + '.gpg', 'wb').write(_encrypted_data)
# decrypts an entry to a temporary directory # decrypts an entry and returns its contents
def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None): def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None):
_contents = None
# check quick-unlock status, fetch passphrase # check quick-unlock status, fetch passphrase
if _quick_verify: if _quick_verify:
_quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id, identity) _quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id, identity)
@@ -251,13 +251,14 @@ def target_type_check(_target_name, _expected_type=True, _error=False):
s_exit(2) s_exit(2)
# ensures an edited entry is optimized for best compatibility # ensures an entry has enough lines to complete an edit and optionally carries out the edit
def line_edit(_lines, _edit_data, _edit_line): def line_edit(_lines, _edit_line, _edit_data=None):
# ensure enough lines are present for edited field # ensure enough lines are present for edited field
while len(_lines) < _edit_line + 1: while len(_lines) < _edit_line + 1:
_lines.append('') _lines.append('')
# write the edited field if _edit_data is not None:
_lines[_edit_line] = _edit_data.rstrip() # write the edited field
_lines[_edit_line] = _edit_data.rstrip()
return _lines return _lines
@@ -296,7 +297,7 @@ def print_info():
}}-}}-*]{4 * ' '}`..'..'{9 * ' '}\u001b[0m♥♥♥\u001b[0m{9 * ' '}`..'..'{6 * ' '}| }}-}}-*]{4 * ' '}`..'..'{9 * ' '}\u001b[0m♥♥♥\u001b[0m{9 * ' '}`..'..'{6 * ' '}|
{4 * ' '}◎-◎{4 * ' '}//{3 * ' '}\\\\{10 * ' '}\u001b[38;5;9m♥\u001b[0m{10 * ' '}//{3 * ' '}\\\\{5 * ' '}/|\\""") {4 * ' '}◎-◎{4 * ' '}//{3 * ' '}\\\\{10 * ' '}\u001b[38;5;9m♥\u001b[0m{10 * ' '}//{3 * ' '}\\\\{5 * ' '}/|\\""")
print(f"{_border}{_blank}\n\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{18 * ' '}\u001b[38;5;15;48;5;8msshyp " print(f"{_border}{_blank}\n\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{18 * ' '}\u001b[38;5;15;48;5;8msshyp "
f"version 1.5.2\u001b[38;5;15;48;5;15m{18 * ' '}\u001b[38;5;7;48;5;8m/\u001b[0m") f"version 1.5.3\u001b[38;5;15;48;5;15m{18 * ' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{14 * ' '}\u001b[38;5;15;48;5;8mthe fortified flock" print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{14 * ' '}\u001b[38;5;15;48;5;8mthe fortified flock"
f" update\u001b[38;5;15;48;5;15m{15 * ' '}\u001b[38;5;7;48;5;8m/\u001b[0m\n{_blank}") f" update\u001b[38;5;15;48;5;15m{15 * ' '}\u001b[38;5;7;48;5;8m/\u001b[0m\n{_blank}")
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{9 * ' '}\u001b[38;5;15;48;5;8mcopyright 2021-2024 ", print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{9 * ' '}\u001b[38;5;15;48;5;8mcopyright 2021-2024 ",
@@ -471,9 +472,6 @@ def rename():
# edits the contents of an entry # edits the contents of an entry
def edit(): def edit():
# set to avoid PEP8 warnings
_detail, _edit_line = None, None
# ensure the edit target is an entry # ensure the edit target is an entry
target_type_check(entry_name, True, True) target_type_check(entry_name, True, True)
@@ -486,10 +484,10 @@ def edit():
_detail, _edit_line = str(input('\nurl: ')), 2 _detail, _edit_line = str(input('\nurl: ')), 2
if arguments[2] in ('note', '-n'): if arguments[2] in ('note', '-n'):
_old_lines = decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled) _old_lines = decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled)
_new_lines = _old_lines[0:3] + edit_note(_old_lines[3:], True).split('\n') # pass 2 as _edit_line to simulate a full entry minus notes so that notes are appended correctly
_new_lines = line_edit(_old_lines[0:3], 2) + edit_note(_old_lines[3:], True).split('\n')
else: else:
_new_lines = line_edit(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled), _detail, _new_lines = line_edit(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled), _edit_line, _detail)
_edit_line)
print('\n\u001b[1mentry preview:\u001b[0m') print('\n\u001b[1mentry preview:\u001b[0m')
entry_reader(_new_lines) entry_reader(_new_lines)
encrypt(_new_lines, directory + entry_name, gpg_id) encrypt(_new_lines, directory + entry_name, gpg_id)
@@ -497,13 +495,11 @@ def edit():
# generates a password for a new or an existing entry # generates a password for a new or an existing entry
def gen(): def gen():
# set to avoid PEP8 warnings
_username, _url, _notes = None, None, None
# gen update # gen update
if arg_count == 3 and arguments[2] in ('update', '-u'): if arg_count == 3 and arguments[2] in ('update', '-u'):
# ensure the gen update target is an entry # ensure the gen update target is an entry
target_type_check(entry_name, True, True) target_type_check(entry_name, True, True)
_new_lines = line_edit(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled), pass_gen(), 0) _new_lines = line_edit(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled), 0, pass_gen())
# gen # gen
else: else:
# make sure the gen target does not already exist # make sure the gen target does not already exist
@@ -614,9 +610,6 @@ if __name__ == "__main__":
# set default states # set default states
ssh_error, success_flag, sync_flag, silent_sync, pass_show = False, False, False, False, False ssh_error, success_flag, sync_flag, silent_sync, pass_show = False, False, False, False, False
# set to avoid PEP8 warnings
arg_start, device_type, offline_mode_enabled = None, None, None
# retrieve typed argument # retrieve typed argument
arguments = argv[1:] arguments = argv[1:]
arg_count = len(arguments) arg_count = len(arguments)
+8 -3
View File
@@ -478,8 +478,8 @@ def extension_downloader():
if _choice == 1: if _choice == 1:
# download extension files to temporary directory # download extension files to temporary directory
_exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini" _exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini"
_ext_exe = urlretrieve(_pointer.get(_selected, 'exe'), _exe_dir) urlretrieve(_pointer.get(_selected, 'exe'), _exe_dir)
_ext_ini = urlretrieve(_pointer.get(_selected, 'ini'), _ini_dir) urlretrieve(_pointer.get(_selected, 'ini'), _ini_dir)
# set permissions under active user # set permissions under active user
chmod(_exe_dir, 0o755) chmod(_exe_dir, 0o755)
chmod(_ini_dir, 0o644) chmod(_ini_dir, 0o644)
@@ -718,7 +718,12 @@ def wrapped_entry(_gm_device_type, _gm_top_message='configuration options:'):
# install with privilege escalation (outside of curses) # install with privilege escalation (outside of curses)
from tempfile import gettempdir from tempfile import gettempdir
_exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini" _exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini"
run((_escalator, 'chown', 'root:root', _exe_dir, _ini_dir)) # PORT START TWEAK-EXT-CHOWN
if uname()[0] == 'FreeBSD':
run((_escalator, 'chown', 'root:wheel', _exe_dir, _ini_dir))
else:
run((_escalator, 'chown', 'root:root', _exe_dir, _ini_dir))
# PORT END TWEAK-EXT-CHOWN
run((_escalator, 'mv', _exe_dir, f"/usr/lib/sshyp/{_ext_name}")) run((_escalator, 'mv', _exe_dir, f"/usr/lib/sshyp/{_ext_name}"))
run((_escalator, 'mv', _ini_dir, f"/usr/lib/sshyp/extensions/{_ext_name}.ini")) run((_escalator, 'mv', _ini_dir, f"/usr/lib/sshyp/extensions/{_ext_name}.ini"))
else: else:
+6
View File
@@ -21,6 +21,7 @@ _create_generic_linux() {
cd port-jobs cd port-jobs
./CLIPTOOL.py LINUX ./CLIPTOOL.py LINUX
./CLIPBOARD.py LINUX ./CLIPBOARD.py LINUX
./CHOWN.py
./UNAME.py LINUX ./UNAME.py LINUX
./COMMENTS.py ALL ./COMMENTS.py ALL
./BLANKS.py ./BLANKS.py
@@ -158,6 +159,7 @@ urls {
./RMEXTMAN.py ./RMEXTMAN.py
./CLIPTOOL.py ./CLIPTOOL.py
./CLIPBOARD.py HAIKU ./CLIPBOARD.py HAIKU
./CHOWN.py
./UNAME.py TMP ./UNAME.py TMP
./COMMENTS.py ALL ./COMMENTS.py ALL
./BLANKS.py ./BLANKS.py
@@ -200,6 +202,7 @@ _create_deb() {
./CLIPBOARD.py WSL ./CLIPBOARD.py WSL
special=WSL-ONLY-DEBIAN special=WSL-ONLY-DEBIAN
fi fi
./CHOWN.py
./UNAME.py LINUX ./UNAME.py LINUX
./COMMENTS.py ALL ./COMMENTS.py ALL
./BLANKS.py ./BLANKS.py
@@ -253,6 +256,7 @@ _create_termux() {
./RMEXTMAN.py ./RMEXTMAN.py
./CLIPTOOL.py ./CLIPTOOL.py
./CLIPBOARD.py TERMUX ./CLIPBOARD.py TERMUX
./CHOWN.py
./UNAME.py TERMUX ./UNAME.py TERMUX
./COMMENTS.py ALL ./COMMENTS.py ALL
./BLANKS.py ./BLANKS.py
@@ -326,6 +330,7 @@ cp -r %%{_sourcedir}/usr %%{buildroot}
cd port-jobs cd port-jobs
./CLIPTOOL.py LINUX ./CLIPTOOL.py LINUX
./CLIPBOARD.py LINUX ./CLIPBOARD.py LINUX
./CHOWN.py
./UNAME.py LINUX ./UNAME.py LINUX
./COMMENTS.py ALL ./COMMENTS.py ALL
./BLANKS.py ./BLANKS.py
@@ -390,6 +395,7 @@ printf "/usr/bin/sshyp
cd port-jobs cd port-jobs
./CLIPTOOL.py LINUX ./CLIPTOOL.py LINUX
./CLIPBOARD.py BSD ./CLIPBOARD.py BSD
./CHOWN.py BSD
./UNAME.py TMP ./UNAME.py TMP
./COMMENTS.py ALL ./COMMENTS.py ALL
./BLANKS.py ./BLANKS.py
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import re
from sys import argv
# read arguments
arguments = argv[1:]
# define PORT target
string1 = '# PORT START TWEAK-EXT-CHOWN'
string2 = '# PORT END TWEAK-EXT-CHOWN'
# define replacement text depending on arguments
if len(arguments) > 0 and arguments[0] == 'BSD':
replacement = "run((_escalator, 'chown', 'root:wheel', _exe_dir, _ini_dir))"
else:
replacement = "run((_escalator, 'chown', 'root:root', _exe_dir, _ini_dir))"
# read input file
text = open('working/stweak.py', 'r').read()
# compile regex and modify text
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
# find and replace the defined PORT target
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/stweak.py', 'w').write(new_text)