acl ole :)

This commit is contained in:
lovebird 2026-02-21 00:11:17 +01:00
parent fa3304fe0b
commit e1c1d182e4
13 changed files with 185 additions and 20 deletions

View File

@ -11,4 +11,4 @@ export { DecoratedVfsClient } from './vfs/DecoratedVfsClient.js';
export { loadVfsSettings, vfsResource, resourceChain } from './vfs/vfs-acl.js';
export type { VfsSettings, VfsAclEntry, VfsGroup } from './vfs/vfs-acl.js';
export { DefaultSanitizers } from './vfs/sanitizers.js';
export { assertNonEmpty, cleanPath, pathSegments, normalisePath, cleanPermission, cleanPermissions, isUuid, cleanUuid, cleanId, cleanGroupName, sanitizeSubpath, sanitizeWritePath, sanitizeFilename, } from './vfs/sanitizers.js';
export { assertNonEmpty, cleanPath, pathSegments, normalisePath, cleanPermission, cleanPermissions, isUuid, cleanUuid, cleanId, cleanGroupName, sanitizeSubpath, sanitizeWritePath, sanitizeFilename, ANONYMOUS_USER_ID, AUTHENTICATED_USER_ID, } from './vfs/sanitizers.js';

View File

@ -10,5 +10,5 @@ export { AclVfsClient } from './vfs/AclVfsClient.js';
export { DecoratedVfsClient } from './vfs/DecoratedVfsClient.js';
export { loadVfsSettings, vfsResource, resourceChain } from './vfs/vfs-acl.js';
export { DefaultSanitizers } from './vfs/sanitizers.js';
export { assertNonEmpty, cleanPath, pathSegments, normalisePath, cleanPermission, cleanPermissions, isUuid, cleanUuid, cleanId, cleanGroupName, sanitizeSubpath, sanitizeWritePath, sanitizeFilename, } from './vfs/sanitizers.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFnQnBELE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRWxELE1BQU07QUFDTixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDckQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDakUsT0FBTyxFQUFFLGVBQWUsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFL0UsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDeEQsT0FBTyxFQUNILGNBQWMsRUFDZCxTQUFTLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFDdEMsZUFBZSxFQUFFLGdCQUFnQixFQUNqQyxNQUFNLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQzFDLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxnQkFBZ0IsR0FDdkQsTUFBTSxxQkFBcUIsQ0FBQyJ9
export { assertNonEmpty, cleanPath, pathSegments, normalisePath, cleanPermission, cleanPermissions, isUuid, cleanUuid, cleanId, cleanGroupName, sanitizeSubpath, sanitizeWritePath, sanitizeFilename, ANONYMOUS_USER_ID, AUTHENTICATED_USER_ID, } from './vfs/sanitizers.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFnQnBELE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRWxELE1BQU07QUFDTixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDckQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDakUsT0FBTyxFQUFFLGVBQWUsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFL0UsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDeEQsT0FBTyxFQUNILGNBQWMsRUFDZCxTQUFTLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFDdEMsZUFBZSxFQUFFLGdCQUFnQixFQUNqQyxNQUFNLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQzFDLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxnQkFBZ0IsRUFDcEQsaUJBQWlCLEVBQ2pCLHFCQUFxQixHQUN4QixNQUFNLHFCQUFxQixDQUFDIn0=

View File

@ -25,7 +25,7 @@ export declare class AclVfsClient {
/**
* @param acl Populated Acl instance (call `loadVfsSettings` first)
* @param ownerId UUID of the folder owner
* @param callerId UUID of the user performing the operation
* @param callerId UUID of the user performing the operation, or 'anonymous'
* @param fsOpts LocalVFS options (must include `root`)
*/
constructor(acl: Acl, ownerId: string, callerId: string, fsOpts: IDefaultParameters);

View File

@ -12,12 +12,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
var _AclVfsClient_instances, _AclVfsClient_acl, _AclVfsClient_local, _AclVfsClient_ownerId, _AclVfsClient_callerId, _AclVfsClient_guard;
import { LocalVFS } from './fs/Local.js';
import { resourceChain } from './vfs-acl.js';
import { cleanUuid, sanitizeSubpath } from './sanitizers.js';
import { cleanUuid, cleanId, sanitizeSubpath, ANONYMOUS_USER_ID, AUTHENTICATED_USER_ID } from './sanitizers.js';
export class AclVfsClient {
/**
* @param acl Populated Acl instance (call `loadVfsSettings` first)
* @param ownerId UUID of the folder owner
* @param callerId UUID of the user performing the operation
* @param callerId UUID of the user performing the operation, or 'anonymous'
* @param fsOpts LocalVFS options (must include `root`)
*/
constructor(acl, ownerId, callerId, fsOpts) {
@ -29,7 +29,7 @@ export class AclVfsClient {
__classPrivateFieldSet(this, _AclVfsClient_acl, acl, "f");
__classPrivateFieldSet(this, _AclVfsClient_local, new LocalVFS(fsOpts), "f");
__classPrivateFieldSet(this, _AclVfsClient_ownerId, cleanUuid(ownerId), "f");
__classPrivateFieldSet(this, _AclVfsClient_callerId, cleanUuid(callerId), "f");
__classPrivateFieldSet(this, _AclVfsClient_callerId, cleanId(callerId), "f");
}
// ── Read operations ─────────────────────────────────────────────
async stat(path) {
@ -86,17 +86,30 @@ _AclVfsClient_acl = new WeakMap(), _AclVfsClient_local = new WeakMap(), _AclVfsC
* Walk the resource chain from most-specific path to root.
* If ANY level grants the permission, access is allowed.
* This means a grant on `/` covers the entire tree.
*
* For non-anonymous callers, also checks grants given to the
* `'authenticated'` sentinel, so a single grant can cover all
* logged-in users.
*/
async function _AclVfsClient_guard(permission, path) {
const safePath = sanitizeSubpath(path);
const chain = resourceChain(__classPrivateFieldGet(this, _AclVfsClient_ownerId, "f"), safePath);
// 1. Check caller-specific grants
for (const resource of chain) {
const result = await __classPrivateFieldGet(this, _AclVfsClient_acl, "f").isAllowed(__classPrivateFieldGet(this, _AclVfsClient_callerId, "f"), resource, permission);
if (result.ok && result.data)
return;
}
// 2. For non-anonymous callers, check 'authenticated' group grants
if (__classPrivateFieldGet(this, _AclVfsClient_callerId, "f") !== ANONYMOUS_USER_ID) {
for (const resource of chain) {
const result = await __classPrivateFieldGet(this, _AclVfsClient_acl, "f").isAllowed(AUTHENTICATED_USER_ID, resource, permission);
if (result.ok && result.data)
return;
}
}
const err = new Error(`EACCES: user '${__classPrivateFieldGet(this, _AclVfsClient_callerId, "f")}' lacks '${permission}' on path '${path}'`);
err.code = 'EACCES';
throw err;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWNsVmZzQ2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Zmcy9BY2xWZnNDbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBcUJBLE9BQU8sRUFBRSxRQUFRLEVBQTJCLE1BQU0sZUFBZSxDQUFDO0FBQ2xFLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU3RCxNQUFNLE9BQU8sWUFBWTtJQU1yQjs7Ozs7T0FLRztJQUNILFlBQVksR0FBUSxFQUFFLE9BQWUsRUFBRSxRQUFnQixFQUFFLE1BQTBCOztRQVgxRSxvQ0FBVTtRQUNWLHNDQUFpQjtRQUNqQix3Q0FBaUI7UUFDakIseUNBQWtCO1FBU3ZCLHVCQUFBLElBQUkscUJBQVEsR0FBRyxNQUFBLENBQUM7UUFDaEIsdUJBQUEsSUFBSSx1QkFBVSxJQUFJLFFBQVEsQ0FBQyxNQUFNLENBQUMsTUFBQSxDQUFDO1FBQ25DLHVCQUFBLElBQUkseUJBQVksU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFBLENBQUM7UUFDbkMsdUJBQUEsSUFBSSwwQkFBYSxTQUFTLENBQUMsUUFBUSxDQUFDLE1BQUEsQ0FBQztJQUN6QyxDQUFDO0lBeUJELG1FQUFtRTtJQUVuRSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQVk7UUFDbkIsTUFBTSx1QkFBQSxJQUFJLG9EQUFPLE1BQVgsSUFBSSxFQUFRLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNoQyxPQUFPLHVCQUFBLElBQUksMkJBQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBWTtRQUN0QixNQUFNLHVCQUFBLElBQUksb0RBQU8sTUFBWCxJQUFJLEVBQVEsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ2hD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
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWNsVmZzQ2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Zmcy9BY2xWZnNDbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBcUJBLE9BQU8sRUFBRSxRQUFRLEVBQTJCLE1BQU0sZUFBZSxDQUFDO0FBQ2xFLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLGlCQUFpQixFQUFFLHFCQUFxQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFaEgsTUFBTSxPQUFPLFlBQVk7SUFNckI7Ozs7O09BS0c7SUFDSCxZQUFZLEdBQVEsRUFBRSxPQUFlLEVBQUUsUUFBZ0IsRUFBRSxNQUEwQjs7UUFYMUUsb0NBQVU7UUFDVixzQ0FBaUI7UUFDakIsd0NBQWlCO1FBQ2pCLHlDQUFrQjtRQVN2Qix1QkFBQSxJQUFJLHFCQUFRLEdBQUcsTUFBQSxDQUFDO1FBQ2hCLHVCQUFBLElBQUksdUJBQVUsSUFBSSxRQUFRLENBQUMsTUFBTSxDQUFDLE1BQUEsQ0FBQztRQUNuQyx1QkFBQSxJQUFJLHlCQUFZLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBQSxDQUFDO1FBQ25DLHVCQUFBLElBQUksMEJBQWEsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFBLENBQUM7SUFDdkMsQ0FBQztJQXNDRCxtRUFBbUU7SUFFbkUsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFZO1FBQ25CLE1BQU0sdUJBQUEsSUFBSSxvREFBTyxNQUFYLElBQUksRUFBUSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDaEMsT0FBTyx1QkFBQSxJQUFJLDJCQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTyxDQUFDLElBQVk7UUFDdEIsTUFBTSx1QkFBQSxJQUFJLG9EQUFPLE1BQVgsSUFBSSxF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

View File

@ -50,6 +50,10 @@ export declare function cleanPermission(raw: string): string;
* Validate and normalise permission arrays.
*/
export declare function cleanPermissions(raw: string[]): string[];
/** Sentinel userId for unauthenticated/anonymous access. */
export declare const ANONYMOUS_USER_ID = "anonymous";
/** Sentinel userId for any authenticated user. */
export declare const AUTHENTICATED_USER_ID = "authenticated";
/**
* Test whether a string is a valid UUID.
*
@ -64,9 +68,10 @@ export declare function isUuid(value: string): boolean;
*/
export declare function cleanUuid(raw: string): string;
/**
* Validate a user/owner identifier must be a valid UUID.
* Validate a user/owner identifier must be a valid UUID or the
* well-known `'anonymous'` sentinel.
*
* @throws Error if the identifier is not a valid UUID.
* @throws Error if the identifier is not a valid UUID or 'anonymous'.
*/
export declare function cleanId(raw: string): string;
/**
@ -102,4 +107,6 @@ export declare const DefaultSanitizers: {
readonly sanitizeSubpath: typeof sanitizeSubpath;
readonly sanitizeWritePath: typeof sanitizeWritePath;
readonly sanitizeFilename: typeof sanitizeFilename;
readonly ANONYMOUS_USER_ID: "anonymous";
readonly AUTHENTICATED_USER_ID: "authenticated";
};

View File

@ -70,6 +70,13 @@ export function cleanPermissions(raw) {
return raw.map(cleanPermission);
}
// ---------------------------------------------------------------------------
// Well-known sentinel IDs
// ---------------------------------------------------------------------------
/** Sentinel userId for unauthenticated/anonymous access. */
export const ANONYMOUS_USER_ID = 'anonymous';
/** Sentinel userId for any authenticated user. */
export const AUTHENTICATED_USER_ID = 'authenticated';
// ---------------------------------------------------------------------------
// UUID validation
// ---------------------------------------------------------------------------
/** Standard UUID v1v5 pattern (case-insensitive, lowercased on output). */
@ -96,11 +103,17 @@ export function cleanUuid(raw) {
return id;
}
/**
* Validate a user/owner identifier must be a valid UUID.
* Validate a user/owner identifier must be a valid UUID or the
* well-known `'anonymous'` sentinel.
*
* @throws Error if the identifier is not a valid UUID.
* @throws Error if the identifier is not a valid UUID or 'anonymous'.
*/
export function cleanId(raw) {
const trimmed = raw.trim().toLowerCase();
if (trimmed === ANONYMOUS_USER_ID)
return ANONYMOUS_USER_ID;
if (trimmed === AUTHENTICATED_USER_ID)
return AUTHENTICATED_USER_ID;
return cleanUuid(raw);
}
/**
@ -159,5 +172,7 @@ export const DefaultSanitizers = {
sanitizeSubpath,
sanitizeWritePath,
sanitizeFilename,
ANONYMOUS_USER_ID,
AUTHENTICATED_USER_ID,
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FuaXRpemVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZnMvc2FuaXRpemVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7OztHQVVHO0FBRUg7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQUMsR0FBVztJQUNqQyxPQUFPLEdBQUc7U0FDTCxPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQztTQUNuQixPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQztTQUNwQixPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQztTQUNuQixPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQzdCLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQUMsR0FBVztJQUNwQyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUN6QyxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGFBQWEsQ0FBQyxHQUFXO0lBQ3JDLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM3QixPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO0FBQ3JDLENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBVztJQUN2QyxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDbkMsSUFBSSxDQUFDLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGlDQUFpQyxDQUFDLENBQUM7SUFDM0QsT0FB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
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FuaXRpemVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZnMvc2FuaXRpemVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7OztHQVVHO0FBRUg7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQUMsR0FBVztJQUNqQyxPQUFPLEdBQUc7U0FDTCxPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQztTQUNuQixPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQztTQUNwQixPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQztTQUNuQixPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQzdCLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQUMsR0FBVztJQUNwQyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUN6QyxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGFBQWEsQ0FBQyxHQUFXO0lBQ3JDLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM3QixPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO0FBQ3JDLENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBVztJQUN2QyxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDbkMsSUFBSSxDQUFDLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGlDQUFpQyxDQUFDLENBQUM7SUFDM0QsT0FB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

View File

@ -106,4 +106,4 @@ export async function loadVfsSettings(acl, userDir) {
}
return settings;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmZzLWFjbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZnMvdmZzLWFjbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsT0FBTyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbkQsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUVqQyxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxjQUFjLEVBQUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUEyQnpHLDhFQUE4RTtBQUM5RSxVQUFVO0FBQ1YsOEVBQThFO0FBRTlFOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FBQyxPQUFlLEVBQUUsWUFBWSxHQUFHLEdBQUc7SUFDM0QsT0FBTyxPQUFPLE9BQU8sSUFBSSxhQUFhLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztBQUMzRCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsT0FBZSxFQUFFLE9BQWU7SUFDMUQsTUFBTSxRQUFRLEdBQUcsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRXZDLE1BQU0sS0FBSyxHQUFhLEVBQUUsQ0FBQztJQUUzQiw2Q0FBNkM7SUFDN0MsS0FBSyxJQUFJLENBQUMsR0FBRyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUN2QyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsR0FBRyxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0UsQ0FBQztJQUVELHNCQUFzQjtJQUN0QixLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcs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
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmZzLWFjbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZnMvdmZzLWFjbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsT0FBTyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbkQsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUVqQyxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxjQUFjLEVBQUUsT0FBTyxFQUFFLGdCQUFnQixFQUFxQixNQUFNLGlCQUFpQixDQUFDO0FBMkI1SCw4RUFBOEU7QUFDOUUsVUFBVTtBQUNWLDhFQUE4RTtBQUU5RTs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsT0FBZSxFQUFFLFlBQVksR0FBRyxHQUFHO0lBQzNELE9BQU8sT0FBTyxPQUFPLElBQUksYUFBYSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUM7QUFDM0QsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLE9BQWUsRUFBRSxPQUFlO0lBQzFELE1BQU0sUUFBUSxHQUFHLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUV2QyxNQUFNLEtBQUssR0FBYSxFQUFFLENBQUM7SUFFM0IsNkNBQTZDO0lBQzdDLEtBQUssSUFBSSxDQUFDLEdBQUcsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDdkMsS0FBSyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxFQUFFLEdBQUcsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNFLENBQUM7SUFFRCxzQkFBc0I7SUFDdEIsS0FBSyxDQUFDLElBQUksQ0FBQyxXQUFX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

View File

@ -33,4 +33,6 @@ export {
cleanPermission, cleanPermissions,
isUuid, cleanUuid, cleanId, cleanGroupName,
sanitizeSubpath, sanitizeWritePath, sanitizeFilename,
ANONYMOUS_USER_ID,
AUTHENTICATED_USER_ID,
} from './vfs/sanitizers.js';

View File

@ -21,7 +21,7 @@ import type { Acl } from '../Acl.js';
import type { INode } from './fs/VFS.js';
import { LocalVFS, type IDefaultParameters } from './fs/Local.js';
import { resourceChain } from './vfs-acl.js';
import { cleanUuid, sanitizeSubpath } from './sanitizers.js';
import { cleanUuid, cleanId, sanitizeSubpath, ANONYMOUS_USER_ID, AUTHENTICATED_USER_ID } from './sanitizers.js';
export class AclVfsClient {
readonly #acl: Acl;
@ -32,14 +32,14 @@ export class AclVfsClient {
/**
* @param acl Populated Acl instance (call `loadVfsSettings` first)
* @param ownerId UUID of the folder owner
* @param callerId UUID of the user performing the operation
* @param callerId UUID of the user performing the operation, or 'anonymous'
* @param fsOpts LocalVFS options (must include `root`)
*/
constructor(acl: Acl, ownerId: string, callerId: string, fsOpts: IDefaultParameters) {
this.#acl = acl;
this.#local = new LocalVFS(fsOpts);
this.#ownerId = cleanUuid(ownerId);
this.#callerId = cleanUuid(callerId);
this.#callerId = cleanId(callerId);
}
// ── Guards ──────────────────────────────────────────────────────
@ -48,16 +48,29 @@ export class AclVfsClient {
* Walk the resource chain from most-specific path to root.
* If ANY level grants the permission, access is allowed.
* This means a grant on `/` covers the entire tree.
*
* For non-anonymous callers, also checks grants given to the
* `'authenticated'` sentinel, so a single grant can cover all
* logged-in users.
*/
async #guard(permission: string, path: string): Promise<void> {
const safePath = sanitizeSubpath(path);
const chain = resourceChain(this.#ownerId, safePath);
// 1. Check caller-specific grants
for (const resource of chain) {
const result = await this.#acl.isAllowed(this.#callerId, resource, permission);
if (result.ok && result.data) return;
}
// 2. For non-anonymous callers, check 'authenticated' group grants
if (this.#callerId !== ANONYMOUS_USER_ID) {
for (const resource of chain) {
const result = await this.#acl.isAllowed(AUTHENTICATED_USER_ID, resource, permission);
if (result.ok && result.data) return;
}
}
const err = new Error(
`EACCES: user '${this.#callerId}' lacks '${permission}' on path '${path}'`,
);

View File

@ -74,6 +74,16 @@ export function cleanPermissions(raw: string[]): string[] {
return raw.map(cleanPermission);
}
// ---------------------------------------------------------------------------
// Well-known sentinel IDs
// ---------------------------------------------------------------------------
/** Sentinel userId for unauthenticated/anonymous access. */
export const ANONYMOUS_USER_ID = 'anonymous';
/** Sentinel userId for any authenticated user. */
export const AUTHENTICATED_USER_ID = 'authenticated';
// ---------------------------------------------------------------------------
// UUID validation
// ---------------------------------------------------------------------------
@ -105,11 +115,15 @@ export function cleanUuid(raw: string): string {
}
/**
* Validate a user/owner identifier must be a valid UUID.
* Validate a user/owner identifier must be a valid UUID or the
* well-known `'anonymous'` sentinel.
*
* @throws Error if the identifier is not a valid UUID.
* @throws Error if the identifier is not a valid UUID or 'anonymous'.
*/
export function cleanId(raw: string): string {
const trimmed = raw.trim().toLowerCase();
if (trimmed === ANONYMOUS_USER_ID) return ANONYMOUS_USER_ID;
if (trimmed === AUTHENTICATED_USER_ID) return AUTHENTICATED_USER_ID;
return cleanUuid(raw);
}
@ -173,4 +187,6 @@ export const DefaultSanitizers = {
sanitizeSubpath,
sanitizeWritePath,
sanitizeFilename,
ANONYMOUS_USER_ID,
AUTHENTICATED_USER_ID,
} as const;

View File

@ -15,7 +15,7 @@
import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import type { Acl } from '../Acl.js';
import { pathSegments, normalisePath, cleanGroupName, cleanId, cleanPermissions } from './sanitizers.js';
import { pathSegments, normalisePath, cleanGroupName, cleanId, cleanPermissions, ANONYMOUS_USER_ID } from './sanitizers.js';
// ---------------------------------------------------------------------------
// Types

View File

@ -0,0 +1,78 @@
/**
* VFS ACL Anonymous user e2e test
*
* Verifies that the well-known 'anonymous' userId works through the
* ACL pipeline: loadVfsSettings populates grants, and isAllowed checks
* pass/fail correctly for anonymous callers.
*/
import { describe, it, expect, beforeAll } from 'vitest';
import { resolve } from 'node:path';
import { Acl } from '../src/Acl.js';
import { MemoryBackend } from '../src/data/MemoryBackend.js';
import { loadVfsSettings, vfsResource } from '../src/vfs/vfs-acl.js';
import { ANONYMOUS_USER_ID } from '../src/vfs/sanitizers.js';
import type { AclResult } from '../src/interfaces.js';
/** Unwrap AclResult — asserts ok and returns data. */
function d<T>(result: AclResult<T>): T {
if (!result.ok) throw new Error(`Expected ok, got ${result.code}: ${result.message}`);
return result.data;
}
const OWNER_ID = '3bb4cfbf-318b-44d3-a9d3-35680e738421';
const READ_ONLY_USER = 'aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb';
const STRANGER_USER = '99999999-0000-0000-0000-ffffffffffff';
const ROOT = vfsResource(OWNER_ID, '/');
const SHARED = vfsResource(OWNER_ID, '/shared');
const USER_DIR = resolve(import.meta.dirname!, 'vfs/root/anon-test');
describe('VFS ACL — anonymous user', () => {
let acl: Acl;
beforeAll(async () => {
acl = new Acl(new MemoryBackend());
const settings = await loadVfsSettings(acl, USER_DIR);
expect(settings).not.toBeNull();
expect(settings!.owner).toBe(OWNER_ID);
});
// Anonymous gets read+list on /
it('anonymous can read on /', async () => {
expect(d(await acl.isAllowed(ANONYMOUS_USER_ID, ROOT, 'read'))).toBe(true);
});
it('anonymous can list on /', async () => {
expect(d(await acl.isAllowed(ANONYMOUS_USER_ID, ROOT, 'list'))).toBe(true);
});
// Direct isAllowed on /shared returns false — the grant is on "/" only.
// AclVfsClient.#guard walks the resource chain, but raw acl.isAllowed does not.
it('anonymous direct check on /shared returns false (grant is on /)', async () => {
expect(d(await acl.isAllowed(ANONYMOUS_USER_ID, SHARED, 'read'))).toBe(false);
});
// Anonymous CANNOT write
it('anonymous CANNOT write on /', async () => {
expect(d(await acl.isAllowed(ANONYMOUS_USER_ID, ROOT, 'write'))).toBe(false);
});
it('anonymous CANNOT delete on /', async () => {
expect(d(await acl.isAllowed(ANONYMOUS_USER_ID, ROOT, 'delete'))).toBe(false);
});
it('anonymous CANNOT mkdir on /', async () => {
expect(d(await acl.isAllowed(ANONYMOUS_USER_ID, ROOT, 'mkdir'))).toBe(false);
});
// Other users still work alongside anonymous
it('read-only user can still read on /', async () => {
expect(d(await acl.isAllowed(READ_ONLY_USER, ROOT, 'read'))).toBe(true);
});
// Stranger (no grant) is still denied
it('stranger CANNOT read on /', async () => {
expect(d(await acl.isAllowed(STRANGER_USER, ROOT, 'read'))).toBe(false);
});
});

View File

@ -0,0 +1,21 @@
{
"owner": "3bb4cfbf-318b-44d3-a9d3-35680e738421",
"acl": [
{
"userId": "anonymous",
"path": "/",
"permissions": [
"read",
"list"
]
},
{
"userId": "aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
"path": "/",
"permissions": [
"read",
"list"
]
}
]
}