From the logic audit (HEAD 04f5f7e):
- P0 CORS: enableCors methods lacked PUT — browsers could not chunk-upload
at all (cross-origin preflight refused)
- P0 chunked: validateUpload enforced the 2 GiB whole-body cap on chunked
completes, so every >2 GiB chunked upload failed after full upload
- P1 presignPut: the SDK's flexible-checksum default added an
x-amz-checksum-crc32 header browsers cannot reproduce; S3Client now uses
requestChecksumCalculation WHEN_REQUIRED
- P1 refresh rotation race: concurrent refreshes both succeeded (consume
ignored rowCount) forking the token family; consume now returns whether
it won and the loser revokes the chain
- P2 logout: awaits the revocation and ignores missing/forged bearers
honestly
65 lines
3.5 KiB
PL/PgSQL
65 lines
3.5 KiB
PL/PgSQL
-- Folder trees and personal files are metadata over the existing payload keys.
|
|
CREATE TABLE file_folders (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
group_id uuid NULL REFERENCES groups(id) ON DELETE CASCADE,
|
|
owner_id uuid NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
parent_id uuid NULL REFERENCES file_folders(id) ON DELETE CASCADE,
|
|
name text NOT NULL CHECK (btrim(name) <> '' AND char_length(name) <= 255),
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT file_folders_scope CHECK ((group_id IS NULL) <> (owner_id IS NULL)),
|
|
UNIQUE (id, workspace_id),
|
|
FOREIGN KEY (group_id, workspace_id) REFERENCES groups(id, workspace_id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TRIGGER trg_file_folders_ws_immutable BEFORE UPDATE ON file_folders
|
|
FOR EACH ROW EXECUTE FUNCTION reject_workspace_change();
|
|
ALTER TABLE file_folders ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE file_folders FORCE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation ON file_folders
|
|
USING (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid)
|
|
WITH CHECK (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid);
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON file_folders TO sp_app;
|
|
|
|
ALTER TABLE chat_media_archive
|
|
ALTER COLUMN group_id DROP NOT NULL,
|
|
ADD COLUMN folder_id uuid NULL REFERENCES file_folders(id) ON DELETE SET NULL,
|
|
ADD COLUMN created_by uuid NULL,
|
|
-- Required even at the personal root, which has no folder from which to infer ownership.
|
|
ADD COLUMN owner_id uuid NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
ADD CONSTRAINT chat_media_archive_scope CHECK ((group_id IS NULL) <> (owner_id IS NULL));
|
|
|
|
-- 045 may already have introduced this setting.
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS member_delete_own boolean NOT NULL DEFAULT true;
|
|
|
|
-- Only exact, known identities are mapped. New authenticated writes supply
|
|
-- created_by; Matrix ingest also resolves the configured homeserver in code.
|
|
UPDATE chat_media_archive a SET created_by = (
|
|
SELECT u.id FROM users u
|
|
WHERE (a.sender_mxid = '@' || u.username || ':tmbh.local' OR a.sender_mxid = u.external_id)
|
|
AND EXISTS (SELECT 1 FROM memberships m WHERE m.workspace_id = a.workspace_id AND m.user_id = u.id)
|
|
ORDER BY (a.sender_mxid = u.external_id) DESC, u.id LIMIT 1
|
|
) WHERE a.created_by IS NULL;
|
|
|
|
-- Other ingest clients (including archive-bot) write this table directly.
|
|
-- Keep exact known sender mappings attributed on those inserts too. Unknown
|
|
-- remote identities stay NULL, so they never acquire member delete rights.
|
|
CREATE FUNCTION archive_file_creator() RETURNS trigger
|
|
LANGUAGE plpgsql SET search_path = public AS $$
|
|
BEGIN
|
|
IF NEW.created_by IS NULL THEN
|
|
SELECT u.id INTO NEW.created_by FROM users u
|
|
WHERE (NEW.sender_mxid = '@' || u.username || ':tmbh.local' OR NEW.sender_mxid = u.external_id)
|
|
AND EXISTS (SELECT 1 FROM memberships m WHERE m.workspace_id = NEW.workspace_id AND m.user_id = u.id)
|
|
ORDER BY (NEW.sender_mxid = u.external_id) DESC, u.id LIMIT 1;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
CREATE TRIGGER trg_chat_media_archive_creator BEFORE INSERT ON chat_media_archive
|
|
FOR EACH ROW EXECUTE FUNCTION archive_file_creator();
|
|
|
|
CREATE INDEX file_folders_scope_parent_idx ON file_folders (workspace_id, group_id, owner_id, parent_id);
|
|
CREATE INDEX chat_media_archive_folder_idx ON chat_media_archive (workspace_id, folder_id);
|
|
CREATE INDEX chat_media_archive_personal_idx ON chat_media_archive (workspace_id, owner_id, created_at DESC, id DESC)
|
|
WHERE group_id IS NULL;
|