feat: add reencrypt command

This commit is contained in:
cătălin 2023-07-24 19:52:25 +02:00
commit 654d996a53
Signed by: catalin
GPG key ID: 0178DF42F43E5FD2
7 changed files with 60 additions and 6 deletions

View file

@ -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")

View 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)

View file

@ -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())

View file

@ -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]

View file

@ -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

View file

@ -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():

View 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""