feat: add reencrypt command
This commit is contained in:
parent
02ca346eae
commit
654d996a53
7 changed files with 60 additions and 6 deletions
|
|
@ -1,5 +1,4 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Callable
|
||||
|
||||
from halig.settings import Settings
|
||||
|
||||
|
|
@ -14,5 +13,5 @@ class BaseCommand(ICommand):
|
|||
def __init__(self, settings: Settings, *args, **kwargs):
|
||||
self.settings = settings
|
||||
|
||||
def traverse_notebooks(self, callback_on_item: Callable):
|
||||
"""Traverse root_path"""
|
||||
def traverse(self):
|
||||
return self.settings.notebooks_root_path.glob("./**/*.age")
|
||||
|
|
|
|||
17
halig/commands/reencrypt.py
Normal file
17
halig/commands/reencrypt.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
from halig.commands.base import BaseCommand
|
||||
from halig.encryption import Encryptor
|
||||
from halig.settings import Settings
|
||||
|
||||
|
||||
class ReencryptCommand(BaseCommand):
|
||||
def __init__(self, settings: Settings, *args, **kwargs):
|
||||
super().__init__(settings, *args, **kwargs)
|
||||
self.encryptor = Encryptor(settings)
|
||||
|
||||
def run(self):
|
||||
for note_path in self.traverse():
|
||||
with note_path.open("rb") as fr:
|
||||
orig_data = self.encryptor.decrypt(fr.read())
|
||||
new_data = self.encryptor.encrypt(orig_data)
|
||||
with note_path.open("wb") as fw:
|
||||
fw.write(new_data)
|
||||
|
|
@ -76,7 +76,7 @@ class SearchCommand(BaseCommand):
|
|||
)
|
||||
|
||||
def _index_notebooks(self):
|
||||
for note_path in self.settings.notebooks_root_path.glob("./**/*.age"):
|
||||
for note_path in self.traverse():
|
||||
updated_at = note_path.stat().st_mtime
|
||||
with note_path.open("rb") as f:
|
||||
body = self.encryptor.decrypt(f.read())
|
||||
|
|
|
|||
|
|
@ -35,4 +35,6 @@ class Encryptor:
|
|||
return rage_encrypt(data, self.recipients) # type: ignore[no-any-return]
|
||||
|
||||
def decrypt(self, data: bytes) -> bytes:
|
||||
if not len(data):
|
||||
return data
|
||||
return rage_decrypt(data, self.identities) # type: ignore[no-any-return]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ COMMANDS_EDIT_HELP = "Edit or add a note into a notebook"
|
|||
COMMANDS_SHOW_HELP = "Show a note's contents"
|
||||
COMMANDS_VERSION = "Show halig's version"
|
||||
COMMANDS_IMPORT_HELP = "Encrypt existing unencrypted files"
|
||||
COMMANDS_SEARCH_HELP = """Perform a full-text search against all your notes,
|
||||
which are indexed into a SQLite FTS5 database located at `~/.cache/halig/halig.db`
|
||||
"""
|
||||
COMMANDS_REENCRYPT_HELP = """Reencrypt all available notes. This operation is useful
|
||||
when new public keys have been added to the config file and you want the notes
|
||||
to be seen by the new pairing private keys"""
|
||||
|
||||
# OPTIONS
|
||||
OPTION_CONFIG_HELP = "Configuration file. Must be YAML and schema compatible"
|
||||
|
|
@ -13,6 +19,10 @@ OPTION_LEVEL_HELP = (
|
|||
OPTION_UNLINK_HELP = """Setting this will remove the original markdown files;
|
||||
only the newly encrypted .age files will be preserved. Backup your data first
|
||||
"""
|
||||
OPTION_INDEX_HELP = """Index the SQLite database with your notes contents. The first
|
||||
time you perform a search, this flag should be set. Afterwards, you should only index
|
||||
when new notes have been added or older ones have been changed, since it's a slow
|
||||
operation"""
|
||||
# ARGUMENTS
|
||||
ARGUMENT_EDIT_NOTE_HELP = """A valid, settings-relative path.
|
||||
Be aware that valid can also mean implicit notes, that is, pointing to a
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from halig.__version__ import __version__
|
|||
from halig.commands.edit import EditCommand
|
||||
from halig.commands.import_unencrypted import ImportCommand
|
||||
from halig.commands.notebooks import NotebooksCommand
|
||||
from halig.commands.reencrypt import ReencryptCommand
|
||||
from halig.commands.search import SearchCommand
|
||||
from halig.commands.show import ShowCommand
|
||||
from halig.settings import load_from_file
|
||||
|
|
@ -115,10 +116,10 @@ def import_unencrypted(
|
|||
command.run()
|
||||
|
||||
|
||||
@app.command()
|
||||
@app.command(help=literals.COMMANDS_SEARCH_HELP)
|
||||
def search(
|
||||
term: str,
|
||||
index: bool = False,
|
||||
index: bool = Option(False, help=literals.OPTION_INDEX_HELP), # noqa: B008
|
||||
):
|
||||
settings = load_from_file()
|
||||
command = SearchCommand(
|
||||
|
|
@ -129,6 +130,15 @@ def search(
|
|||
command.run()
|
||||
|
||||
|
||||
@app.command(help=literals.COMMANDS_REENCRYPT_HELP)
|
||||
def reencrypt():
|
||||
settings = load_from_file()
|
||||
command = ReencryptCommand(
|
||||
settings=settings,
|
||||
)
|
||||
command.run()
|
||||
|
||||
|
||||
@app.command(help=literals.COMMANDS_VERSION)
|
||||
@capture
|
||||
def version():
|
||||
|
|
|
|||
16
tests/commands/test_reencrypt.py
Normal file
16
tests/commands/test_reencrypt.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import pytest
|
||||
|
||||
from halig.commands.reencrypt import ReencryptCommand
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def reencrypt_command(settings):
|
||||
return ReencryptCommand(settings)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('notes')
|
||||
def test_reencrypt(reencrypt_command):
|
||||
reencrypt_command.run()
|
||||
for note_path in reencrypt_command.traverse():
|
||||
with note_path.open("rb") as f:
|
||||
assert reencrypt_command.encryptor.decrypt(f.read()) == b""
|
||||
Loading…
Add table
Add a link
Reference in a new issue