mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-04 00:47:01 +01:00
`:encrypt` on a folder sealed the files in it once; whatever arrived afterwards was plaintext. A folder or space that is encrypted now carries `sealed` (0015), and so does every folder under it, so "is this place sealed?" is one column of the parent. What lands there is sealed on the way in, or refused with 423 while the vault is shut: - uploads, with or without `?encrypt`, encrypted as they stream - imports, sealed where they lie before any row names them - SFTP writes, sealed out of their `.part`, and refused at the open when the vault is shut - files and folders moved in, through the web or SFTP: seal::enter seals them first and the move happens only if that worked Triggers make it hold for any path: a folder made inside a sealed one is sealed, and a file node whose media is not encrypted cannot be inserted into or moved into a sealed folder (a 400, not a 500). Decrypting the sealed folder lifts the flag on its subtree and opens its files; a file or folder inside it cannot be decrypted on its own. An empty folder can be sealed. Documents are not files and stay as they are. The list shows a lock on sealed folders, the touch menu offers to unseal one, and an upload or import into one asks for the vault password first. Tests cover sealing, inheritance, the trigger, moves with the vault open and shut, unsealing, an SFTP write and an import.
40 lines
1.8 KiB
SQL
40 lines
1.8 KiB
SQL
-- A sealed folder or space keeps what lands in it sealed: an upload, an
|
|
-- import, an SFTP write, a file moved in. The flag is on every folder of a
|
|
-- sealed subtree, not only on the one `:encrypt` was given, so "is this
|
|
-- place sealed?" is one column of the parent rather than a walk up the tree.
|
|
ALTER TABLE nodes ADD COLUMN sealed INTEGER NOT NULL DEFAULT 0;
|
|
|
|
-- A folder made inside a sealed one is sealed from the start, whatever made it.
|
|
CREATE TRIGGER nodes_sealed_inherit AFTER INSERT ON nodes
|
|
WHEN new.kind = 'folder' AND (SELECT sealed FROM nodes WHERE id = new.parent_id) = 1
|
|
BEGIN
|
|
UPDATE nodes SET sealed = 1 WHERE id = new.id;
|
|
END;
|
|
|
|
-- The last word on it: whichever path a file takes into a sealed place, it
|
|
-- does not get there as plaintext. The code seals first and so never meets
|
|
-- these; a path that forgot to fails here instead of leaving the bytes open.
|
|
CREATE TRIGGER nodes_sealed_insert BEFORE INSERT ON nodes
|
|
WHEN new.kind = 'file'
|
|
AND (SELECT sealed FROM nodes WHERE id = new.parent_id) = 1
|
|
AND (SELECT encrypted FROM media WHERE id = new.media_id) = 0
|
|
BEGIN
|
|
SELECT RAISE(ABORT, 'sealed folder: a file here must be encrypted');
|
|
END;
|
|
|
|
CREATE TRIGGER nodes_sealed_update BEFORE UPDATE OF parent_id, media_id ON nodes
|
|
WHEN new.kind = 'file'
|
|
AND (SELECT sealed FROM nodes WHERE id = new.parent_id) = 1
|
|
AND (SELECT encrypted FROM media WHERE id = new.media_id) = 0
|
|
BEGIN
|
|
SELECT RAISE(ABORT, 'sealed folder: a file here must be encrypted');
|
|
END;
|
|
|
|
-- A folder moves into a sealed place only once it is sealed itself.
|
|
CREATE TRIGGER nodes_sealed_move BEFORE UPDATE OF parent_id ON nodes
|
|
WHEN new.kind = 'folder' AND new.sealed = 0
|
|
AND (SELECT sealed FROM nodes WHERE id = new.parent_id) = 1
|
|
BEGIN
|
|
SELECT RAISE(ABORT, 'sealed folder: a folder moved here must be sealed first');
|
|
END;
|