Compare commits

...

8 Commits

Author SHA1 Message Date
723c5b6677 [+] pack .whl release 2025-03-04 18:49:12 +03:00
aaf8b12549 [+] check invalid password 2025-03-04 18:39:48 +03:00
06e79d0679 [+] fix secret_check 2025-03-04 18:38:52 +03:00
ff786e3ce6 [+] update pr34
1. add dotfiles deploy via .tar.xz;
  2. update .sway/config;
  3. update test_crypto.py;
2025-03-04 15:44:20 +03:00
add9d858d8 [+] improve crypto 2025-03-03 18:00:51 +03:00
731c507b95 [+] add tests for pr34 2025-03-03 17:57:14 +03:00
62063a1448 [+] update .p43
1. partially improve crypto;
  2. fix m.py;
2025-03-03 12:53:58 +03:00
1fb4e4efc5 [+] update pr34
1. partially add crypto logic;
  2. fetch updated commands from laptop;
  3. update Makefile;
2025-03-03 09:51:26 +03:00
15 changed files with 365 additions and 60 deletions

2
.gitattributes vendored

@ -0,0 +1,2 @@
releases/tar/** filter=lfs diff=lfs merge=lfs -text
releases/whl/** filter=lfs diff=lfs merge=lfs -text

2
.gitignore vendored

@ -13,3 +13,5 @@ d2/book1/books
.vscode/* .vscode/*
!.vscode/launch.json !.vscode/launch.json
python/build python/build
.*.kate-swp
!releases/whl/*.whl

@ -1,10 +1,13 @@
.PHONY: python_clean_online_fxreader_vpn .PHONY: python_clean_online_fxreader_vpn
host_deps: host_deps:
./m host_deps ./m.py host_deps
python_lint: python_lint:
./m mypy -- -f vscode 2>&1 | less ./m.py mypy -- -f vscode 2>&1 | less
python_tests:
./m.py tests
#python_clean_online_fxreader_vpn: #python_clean_online_fxreader_vpn:
# rm -fr \ # rm -fr \
@ -48,6 +51,12 @@ python_put_dist:
done done
ln -sf $(INSTALL_ROOT)/env3/bin/online-fxreader-pr34-commands $(INSTALL_ROOT)/commands ln -sf $(INSTALL_ROOT)/env3/bin/online-fxreader-pr34-commands $(INSTALL_ROOT)/commands
PYTHON_PROJECTS_NAMES ?= online.fxreader.pr34
python_whl:
for f in $(PYTHON_PROJECTS_NAMES); do \
./m.py deploy:wheel -o releases/whl -p $$f; \
done
python_put: python_put_dist python_put_env python_put: python_put_dist python_put_env
dotfiles_put: dotfiles_put:
@ -77,3 +86,12 @@ dotfiles_fetch:
commands install -f -p ~ -s ~/.config/katerc -t dotfiles commands install -f -p ~ -s ~/.config/katerc -t dotfiles
commands install -f -p ~ -s ~/.config/Code\ -\ OSS/User/keybindings.json -t dotfiles commands install -f -p ~ -s ~/.config/Code\ -\ OSS/User/keybindings.json -t dotfiles
commands install -f -p ~ -s ~/.config/Code\ -\ OSS/User/settings.json -t dotfiles commands install -f -p ~ -s ~/.config/Code\ -\ OSS/User/settings.json -t dotfiles
DOTFILES_VERSION ?= 0.1
dotfiles_deploy:
mkdir -p releases/tar
tar -cvf - \
dotfiles \
| xz --compress -9 --stdout > \
releases/tar/dotfiles-$(DOTFILES_VERSION).tar.xz

@ -1 +1 @@
Subproject commit 7552bcde773823c51c82daa5d9b23ea045df9ec2 Subproject commit 41fb87dc48ca2b0710e8489fc2de64f1fc82cf15

@ -19,7 +19,8 @@ set $term weston-terminal
# on the original workspace that the command was run on. # on the original workspace that the command was run on.
#for_window [app_id="^launcher$"] floating enable, sticky enable, resize set 30 ppt 60 ppt, border pixel 10 #for_window [app_id="^launcher$"] floating enable, sticky enable, resize set 30 ppt 60 ppt, border pixel 10
#set $menu exec $term --class=launcher -e /usr/bin/sway-launcher-desktop #set $menu exec $term --class=launcher -e /usr/bin/sway-launcher-desktop
set $dmenu_path /usr/bin/bemenu-run #set $dmenu_path /usr/bin/bemenu-run
set $dmenu_path rofi -modes run -show run
set $menu $dmenu_path | xargs swaymsg exec -- set $menu $dmenu_path | xargs swaymsg exec --
@ -64,10 +65,12 @@ bindgesture swipe:4:right workspace prev
for_window [shell="xwayland"] title_format "[XWayland] %title" for_window [shell="xwayland"] title_format "[XWayland] %title"
#set $lock_cmd \
# loginctl list-sessions | \
# tail '-n' +2 | head -n -2 | awk '{print $1}' | \
# xargs loginctl lock-session
set $lock_cmd \ set $lock_cmd \
loginctl list-sessions | \ zsh -c "commands loginctl --action lock-session"
tail '-n' +2 | head -n -2 | awk '{print $1}' | \
xargs loginctl lock-session
bindgesture swipe:4:up exec $lock_cmd bindgesture swipe:4:up exec $lock_cmd
@ -76,14 +79,14 @@ bindgesture swipe:4:up exec $lock_cmd
# #
# Basics: # Basics:
# #
bindsym $mod+Shift+l exec $lock_cmd bindsym Shift+$mod+l exec $lock_cmd
bindsym Shift+mod1+1 \ bindsym --locked Shift+mod1+1 \
exec ~/.local/bin/commands \ exec ~/.local/bin/commands \
desktop-services \ desktop-services \
--cpufreq-action performance --cpufreq-action performance
bindsym Shift+mod1+2 \ bindsym --locked Shift+mod1+2 \
exec ~/.local/bin/commands \ exec ~/.local/bin/commands \
desktop-services \ desktop-services \
--cpufreq-action powersave --cpufreq-action powersave
@ -140,7 +143,11 @@ floating_modifier $mod normal
bindsym $mod+Shift+c reload bindsym $mod+Shift+c reload
# Exit sway (logs you out of your Wayland session) # Exit sway (logs you out of your Wayland session)
bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' bindsym $mod+Shift+e \
exec swaynag -t warning \
-m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' \
-b 'Yes, exit sway' \
'swaymsg exit'
# #
# Moving around: # Moving around:
# #
@ -224,7 +231,8 @@ bindsym $mod+p floating toggle
## Swap focus between the tiling area and the floating area ## Swap focus between the tiling area and the floating area
#bindsym $mod+space focus mode_toggle #bindsym $mod+space focus mode_toggle
bindsym --release Print exec bash -c "commands wl-screenshot" bindsym --release Print exec zsh -c "commands wl-screenshot"
bindsym --release $mod+s exec zsh -c "commands wl-screenshot"
# Move focus to the parent container # Move focus to the parent container
#bindsym $mod+a focus parent #bindsym $mod+a focus parent
@ -265,6 +273,40 @@ mode "resize" {
} }
bindsym $mod+r mode "resize" bindsym $mod+r mode "resize"
set $black #000000
set $red #ff0000
set $green #00ff00
set $blue #0000ff
set $white #ffffff
set $grey #757575
set $pale_green #9df882
set $pale_green2 #6baf54
set $dark_green #1a7000
set $pale_blue #7da9f9
set $dark_blue #005ba6
set $pale_greenblue #2da078
set $pale_greenblue2 #66c473
set $yellow #fffd0d
set $dark_yellow #908f00
set $color1 #18ff00
set $color2 #000000
set $color3 #ff00ff
set $color4 #ff0000
set $color5 #00000000
set $color6 #00000000
set $color7 #00000000
set $border_focused $pale_green
set $border_unfocused $color2
set $background_focused $pale_greenblue2
set $background_unfocused $grey
set $child_border_focused $white
set $child_border_unfocused $color2
set $bright_text $white
set $dark_text $black
# #
# Status Bar: # Status Bar:
# #
@ -284,27 +326,34 @@ bar {
height 16 height 16
colors { colors {
statusline #565656 statusline $bright_text
background #dfdfdf background $pale_green2
inactive_workspace #dfdfdf #dfdfdf #000000 inactive_workspace $black $white $dark_text
active_workspace #dfdfdf #efefef #000000 active_workspace $black $white $bright_text
focused_workspace #dfdfdf #efefef #000000 focused_workspace $dark_yellow $yellow $dark_text
} }
} }
client.focused #f3f3f3 #dfdfdf #565656 #f3f3f3 #f3f3f3
client.unfocused #f3f3f3 #dfdfdf #565656 #f3f3f3 #f3f3f3 #client.focused #f3f3f3 #dfdfdfdd #565656 #f3f3f3 #f3f3f3
client.focused $border_focused $background_focused $white $white $child_border_focused
client.unfocused $border_unfocused $background_unfocused $white $white $child_border_unfocused
for_window [all] border 1
#font pango:Helvetica Neue 10 #font pango:Helvetica Neue 10
font pango:Terminus 10 font pango:Terminus 12
titlebar_padding 1 4 titlebar_padding 32 1
titlebar_border_thickness 1
title_align center title_align center
#for_window [class=".*"] title_format "<b>%title</b>" #for_window [class=".*"] title_format "<b>%title</b>"
for_window [class="^firefox$"] floating enable for_window [class="^firefox$"] floating enable
for_window [all] opacity set 0.95
input * { input * {
xkb_layout "us,ru" xkb_layout "us,ru"
xkb_options "grp:win_space_toggle" xkb_options "grp:win_space_toggle"
@ -313,4 +362,3 @@ input type:keyboard xkb_model "pc101"
include /etc/sway/config.d/* include /etc/sway/config.d/*
include ~/.sway/config.d/* include ~/.sway/config.d/*

73
m.py

@ -17,6 +17,9 @@ logger = logging.getLogger(__name__)
class PyProject: class PyProject:
dependencies: dict[str, list[str]] dependencies: dict[str, list[str]]
early_features: Optional[list[str]] = None early_features: Optional[list[str]] = None
pip_find_links: Optional[list[pathlib.Path]] = None
runtime_libdirs: Optional[list[pathlib.Path]] = None
runtime_preload: Optional[list[pathlib.Path]] = None
def pyproject_load( def pyproject_load(
d: pathlib.Path, d: pathlib.Path,
@ -65,6 +68,26 @@ def pyproject_load(
if 'early_features' in content['tool'][tool_name]: if 'early_features' in content['tool'][tool_name]:
res.early_features = content['tool'][tool_name]['early_features'] res.early_features = content['tool'][tool_name]['early_features']
if 'pip_find_links' in content['tool'][tool_name]:
res.pip_find_links = [
d.parent / pathlib.Path(o)
for o in content['tool'][tool_name]['pip_find_links']
]
if 'runtime_libdirs' in content['tool'][tool_name]:
res.runtime_libdirs = [
d.parent / pathlib.Path(o)
# pathlib.Path(o)
for o in content['tool'][tool_name]['runtime_libdirs']
]
if 'runtime_preload' in content['tool'][tool_name]:
res.runtime_preload = [
d.parent / pathlib.Path(o)
# pathlib.Path(o)
for o in content['tool'][tool_name]['runtime_preload']
]
return res return res
@dataclasses.dataclass @dataclasses.dataclass
@ -92,8 +115,21 @@ def env_bootstrap(
bootstrap_settings: BootstrapSettings, bootstrap_settings: BootstrapSettings,
pyproject: PyProject, pyproject: PyProject,
) -> None: ) -> None:
pip_find_links : list[pathlib.Path] = []
if not pyproject.pip_find_links is None:
pip_find_links.extend(pyproject.pip_find_links)
pip_find_links_args = sum([
['-f', str(o),]
for o in pip_find_links
], [])
subprocess.check_call([ subprocess.check_call([
'uv', 'venv', '--seed', 'uv', 'venv',
*pip_find_links_args,
# '--seed',
'--offline',
str(bootstrap_settings.env_path) str(bootstrap_settings.env_path)
]) ])
@ -101,31 +137,36 @@ def env_bootstrap(
'uv', 'uv',
'pip', 'pip',
'install', 'install',
*pip_find_links_args,
'-p', '-p',
bootstrap_settings.python_path, bootstrap_settings.python_path,
'uv', '--offline',
'uv', 'pip',
]) ])
subprocess.check_call([ subprocess.check_call([
bootstrap_settings.python_path, bootstrap_settings.python_path,
'-m', '-m',
'uv', 'pip', 'install', 'uv', 'pip', 'install',
*pip_find_links_args,
'--offline',
'build', 'setuptools', 'meson-python', 'pybind11', 'build', 'setuptools', 'meson-python', 'pybind11',
]) ])
early_wheels = glob.glob( # early_wheels = glob.glob(
str( # str(
pathlib.Path(__file__).parent / 'deps' / 'dist' / 'early' / '*.whl' # pathlib.Path(__file__).parent / 'deps' / 'dist' / 'early' / '*.whl'
) # )
) # )
if len(early_wheels) > 0: # if len(early_wheels) > 0:
subprocess.check_call([ # subprocess.check_call([
bootstrap_settings.python_path, # bootstrap_settings.python_path,
'-m', # '-m',
'uv', 'pip', 'install', # 'uv', 'pip', 'install',
*early_wheels, # '--offline',
]) # *early_wheels,
# ])
if pyproject.early_features: if pyproject.early_features:
early_dependencies = sum([ early_dependencies = sum([
@ -136,11 +177,15 @@ def env_bootstrap(
logger.info(dict( logger.info(dict(
early_dependencies=early_dependencies, early_dependencies=early_dependencies,
)) ))
if len(early_dependencies) > 0: if len(early_dependencies) > 0:
subprocess.check_call([ subprocess.check_call([
bootstrap_settings.python_path, bootstrap_settings.python_path,
'-m', '-m',
'uv', 'pip', 'install', 'uv', 'pip', 'install',
*pip_find_links_args,
# '-f', str(pathlib.Path(__file__).parent / 'deps' / 'dist'),
'--offline',
*early_dependencies, *early_dependencies,
]) ])

@ -26,6 +26,7 @@ logger = logging.getLogger(__name__)
class Command(enum.StrEnum): class Command(enum.StrEnum):
mypy = 'mypy' mypy = 'mypy'
deploy_wheel = 'deploy:wheel' deploy_wheel = 'deploy:wheel'
tests = 'tests'
@dataclasses.dataclass @dataclasses.dataclass
class Settings( class Settings(
@ -145,6 +146,15 @@ class CLI(_cli.CLI):
self.mypy( self.mypy(
argv=args, argv=args,
) )
elif options.command is Command.tests:
for k, v in self.projects.items():
subprocess.check_call([
sys.executable,
'-m',
'unittest',
'online.fxreader.pr34.tests.test_crypto',
*args,
], cwd=str(v.source_dir))
else: else:
raise NotImplementedError raise NotImplementedError

@ -971,7 +971,10 @@ def resilient_vlc(stream=None):
pass pass
time.sleep(1.0) time.sleep(1.0)
def sway_sock(): def sway_sock(
wait: bool = False,
) -> Optional[str]:
while True:
import glob import glob
uid = os.stat(os.environ['HOME']).st_uid uid = os.stat(os.environ['HOME']).st_uid
t1 = glob.glob( t1 = glob.glob(
@ -986,8 +989,16 @@ def sway_sock():
os.stat(o).st_mtime os.stat(o).st_mtime
for o in t1 for o in t1
] ]
t3 = sorted(enumerate(t1), key=lambda x: t2[x[0]])[-1][0] sorted_entries = sorted(enumerate(t1), key=lambda x: t2[x[0]])
if len(sorted_entries) > 0:
t3 = sorted_entries[-1][0]
return t1[t3] return t1[t3]
else:
if wait:
time.sleep(0.1)
continue
else:
return None
def eternal_firefox( def eternal_firefox(
tabs=None, tabs=None,
@ -2084,6 +2095,25 @@ def scrap_yt_music(argv: list[str]) -> None:
for o in context['workers']: for o in context['workers']:
o.join() o.join()
def loginctl(argv: list[str]) -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
'--action',
choices=[
'lock-session',
],
)
options = parser.parse_args(argv)
if options.action == 'lock-session':
subprocess.check_call(r'''
loginctl list-sessions -j | jq -r ".[] | select(.uid==$UID) | \
.session" | loginctl lock-session
''', shell=True, timeout=1)
else:
raise NotImplementedError
def desktop_services(argv): def desktop_services(argv):
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.add_option( parser.add_option(
@ -2606,7 +2636,15 @@ echo 6500 > /sys/bus/platform/devices/applesmc.768/fan1_output;
else: else:
pass pass
os.environ['SWAYSOCK'] = sway_sock() #logger.info(dict(
# environ=os.environ,
# #session=subprocess.check_output(['loginctl', 'show-session'], timeout=1,),
#))
sway_sock_res = sway_sock(True)
if sway_sock_res:
os.environ['SWAYSOCK'] = sway_sock_res
assert all([ assert all([
env_name in os.environ env_name in os.environ
@ -2614,7 +2652,8 @@ echo 6500 > /sys/bus/platform/devices/applesmc.768/fan1_output;
'GTK_IM_MODULE', 'GTK_IM_MODULE',
'XMODIFIERS', 'XMODIFIERS',
'QT_IM_MODULE', 'QT_IM_MODULE',
'I3SOCK', #'I3SOCK',
#'XDG_SEAT',
'SWAYSOCK', 'SWAYSOCK',
'WAYLAND_DISPLAY', 'WAYLAND_DISPLAY',
] ]
@ -2684,7 +2723,8 @@ echo 6500 > /sys/bus/platform/devices/applesmc.768/fan1_output;
unlock='echo unlock; pkill --signal SIGINT swaylock; swaymsg "output * dpms on";', unlock='echo unlock; pkill --signal SIGINT swaylock; swaymsg "output * dpms on";',
unlock2='pkill --signal SIGINT swaylock;', unlock2='pkill --signal SIGINT swaylock;',
resume='echo resume; swaymsg "output * dpms on";', resume='echo resume; swaymsg "output * dpms on";',
before_sleep='echo before_sleep; loginctl lock-session;', before_sleep='echo before_sleep; commands loginctl lock-session;',
#before_sleep='echo blah;',
after_resume='echo after_resume; pkill --signal SIGUSR1 swayidle;', after_resume='echo after_resume; pkill --signal SIGUSR1 swayidle;',
) )
self.last_force_idle = None self.last_force_idle = None
@ -3886,6 +3926,7 @@ class Command(enum.StrEnum):
socat_ssh = 'socat-ssh' socat_ssh = 'socat-ssh'
gnome_shortcuts = 'gnome-shortcuts' gnome_shortcuts = 'gnome-shortcuts'
sway_sock = 'sway_sock' sway_sock = 'sway_sock'
loginctl = 'loginctl'
suspend_timer = 'suspend-timer' suspend_timer = 'suspend-timer'
desktop_services = 'desktop-services' desktop_services = 'desktop-services'
pm_service = 'pm-service' pm_service = 'pm-service'
@ -4027,6 +4068,8 @@ def commands_cli(
gnome_shortcuts(args) gnome_shortcuts(args)
elif options.command is Command.sway_sock: elif options.command is Command.sway_sock:
print(sway_sock()) print(sway_sock())
elif options.command is Command.loginctl:
loginctl(args)
elif options.command is Command.suspend_timer: elif options.command is Command.suspend_timer:
suspend_timer(args) suspend_timer(args)
elif options.command is Command.desktop_services: elif options.command is Command.desktop_services:

@ -0,0 +1,90 @@
import base64
import os
import cryptography.hazmat.primitives.kdf.scrypt
from typing import (Literal, overload, Optional,)
class PasswordUtils:
@overload
@classmethod
def secret_hash(
cls,
secret: str | bytes,
mode: Literal['base64'],
salt: Optional[bytes] = None,
) -> tuple[str, str]: ...
@overload
@classmethod
def secret_hash(
cls,
secret: str | bytes,
mode: Literal['bytes'],
salt: Optional[bytes] = None,
) -> tuple[bytes, bytes]: ...
@classmethod
def secret_hash(
cls,
secret: str | bytes,
mode: Literal['bytes', 'base64'],
salt: Optional[bytes] = None,
) -> tuple[str, str] | tuple[bytes, bytes]:
if salt is None:
salt = os.urandom(16)
if isinstance(secret, str):
secret = secret.encode('utf-8')
# derive
kdf = cls._scrypt_init(salt=salt)
hashed_secret = kdf.derive(secret)
if mode == 'bytes':
return (salt, hashed_secret)
elif mode == 'base64':
res_tuple = tuple((
base64.b64encode(o).decode('utf-8')
for o in (salt, hashed_secret,)
))
return (res_tuple[0], res_tuple[1])
else:
raise NotImplementedError
@classmethod
def _scrypt_init(
cls,
salt: bytes
) -> cryptography.hazmat.primitives.kdf.scrypt.Scrypt:
return cryptography.hazmat.primitives.kdf.scrypt.Scrypt(
salt=salt,
length=32,
n=2**14,
r=8,
p=1,
)
@classmethod
def secret_check(
cls,
secret: str | bytes,
salt: str | bytes,
hashed_secret: str | bytes,
) -> bool:
if isinstance(salt, str):
salt = base64.b64decode(salt)
if isinstance(secret, str):
secret = secret.encode('utf-8')
if isinstance(hashed_secret, str):
hashed_secret = base64.b64decode(hashed_secret)
kdf = cls._scrypt_init(salt=salt)
try:
kdf.verify(secret, hashed_secret)
return True
except cryptography.exceptions.InvalidKey:
return False

@ -0,0 +1,36 @@
from online.fxreader.pr34.commands_typed import crypto
import unittest
class TestCrypto(unittest.TestCase):
def test_password_utils(self) -> None:
salt = b'asdfasdfasdf'
secret = 'blah'
hash_res = crypto.PasswordUtils.secret_hash(
secret,
mode='bytes',
salt=salt,
)
self.assertEqual(
hash_res,
(
salt,
b'\xdak\xd15\xfa\x8e\xc8\r\xc3\xd2c\xf1m\xb0\xbf\xe6\x98\x01$!j\xc8\xc0Hh\x84\xea,\x91\x8b\x08\xce',
),
)
check_res = crypto.PasswordUtils.secret_check(
secret,
*hash_res,
)
self.assertTrue(check_res)
self.assertFalse(
crypto.PasswordUtils.secret_check(
secret + 'asdfasdfsdf',
*hash_res,
)
)

@ -11,8 +11,13 @@ dependencies = [
] ]
[project.optional-dependencies] [project.optional-dependencies]
crypto = [
'cryptography',
]
early = [ early = [
'numpy' 'numpy',
'cryptography',
] ]
[tool.online-fxreader-pr34] [tool.online-fxreader-pr34]

BIN
releases/tar/dotfiles-0.1.tar.xz (Stored with Git LFS) Normal file

Binary file not shown.

BIN
releases/whl/online_fxreader_pr34-0.1.4.13-py3-none-any.whl (Stored with Git LFS) Normal file

Binary file not shown.