fix: let files in a shared folder be renamed and deleted on Stalwart 1.0, which sends rights for folders only

This commit is contained in:
Linus Rath
2026-10-29 02:11:28 +01:00
parent 87472ae937
commit 7c495d03ab
2 changed files with 26 additions and 4 deletions
+9
View File
@@ -32,6 +32,15 @@ describe('fileNodeAccess', () => {
expect(fileNodeAccess(nodes.at(-1)!, nodes)).toEqual({ rename: true, delete: true, move: false, duplicate: true });
});
it('takes the folder\'s rights for a file that names none (Stalwart 1.0)', () => {
const writable = shared(WRITE, WRITE);
const nodes = [...writable.slice(0, 2), { ...writable[2], myRights: undefined }];
expect(fileNodeAccess(nodes.at(-1)!, nodes)).toEqual({ rename: true, delete: true, move: false, duplicate: true });
const readOnly = shared(NONE, NONE);
const ro = [...readOnly.slice(0, 2), { ...readOnly[2], myRights: undefined }];
expect(fileNodeAccess(ro.at(-1)!, ro)).toEqual({ rename: false, delete: false, move: false, duplicate: false });
});
it('offers nothing inside a folder shared read-only', () => {
const nodes = shared(NONE, NONE);
expect(fileNodeAccess(nodes.at(-1)!, nodes)).toEqual({ rename: false, delete: false, move: false, duplicate: false });
+17 -4
View File
@@ -27,24 +27,37 @@ function sharedParent(node: FileNode, nodes: FileNode[]): FileNode | undefined {
return nodes.find((n) => n.id === node.parentId && !n.discoverableOnly);
}
// Stalwart 1.0 sends `myRights` on folders only; a file has the rights of
// the folder it is in. Walk up to the nearest node that names its rights.
function effectiveRights(node: FileNode, nodes: FileNode[]): FileNode['myRights'] {
const seen = new Set<string>();
let current: FileNode | undefined = node;
while (current && !seen.has(current.id)) {
if (current.myRights) return current.myRights;
seen.add(current.id);
const parentId: string | null | undefined = current.parentId;
current = parentId == null ? undefined : nodes.find((n) => n.id === parentId);
}
return undefined;
}
export function fileNodeAccess(node: FileNode, nodes: FileNode[]): FileNodeAccess {
if (!node.isShared) return ALL;
const parent = sharedParent(node, nodes);
if (!parent) return { rename: false, delete: false, move: false, duplicate: false };
const rights = node.myRights;
const rights = effectiveRights(node, nodes);
return {
rename: !!rights?.mayRename,
delete: !!rights?.mayDelete,
move: false,
duplicate: !!parent.myRights?.mayAddChildren,
duplicate: !!effectiveRights(parent, nodes)?.mayAddChildren,
};
}
/**
* Whether new folders and uploads may go into the folder `folderId` (null:
* the user's own drive root). Inside a shared folder only with its
* `mayAddChildren`; Stalwart 0.16 reports a child of a shared folder with
* every right false, so there it is offered in the shared folder itself.
* `mayAddChildren`.
*/
export function mayCreateIn(folderId: string | null, nodes: FileNode[]): boolean {
if (folderId === null) return true;