mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-30 09:06:37 +00:00
Merge pull request #3914 from HeyPuter/juancastro/put-1869-unhandled-rejection-on-the-hottest-permission-path-can-take
fix: stop leaking an unawaited permission traversal on every check
This commit is contained in:
@@ -1712,6 +1712,32 @@ describe('PermissionService — scan paths', () => {
|
||||
expect(await permService.check(actor, permission)).toBe(false);
|
||||
});
|
||||
|
||||
// The traversal used to run beside the flat read with nobody awaiting it.
|
||||
it('answers a flat hit without running the linked traversal', async () => {
|
||||
const { row, actor } = await makeGroupedUser();
|
||||
const permission = `zztest:flat-${uuidv4()}:ii:read`;
|
||||
await server.stores.permission.setFlatUserPerm(row.id, permission, {
|
||||
permission,
|
||||
deleted: false,
|
||||
issuer_user_id: row.id,
|
||||
} as never);
|
||||
|
||||
const linked = vi
|
||||
.spyOn(server.stores.permission, 'readLinkedUserUserPerms')
|
||||
.mockRejectedValue(new Error('db wobble'));
|
||||
try {
|
||||
const reading = await permService.validateUserPerms({
|
||||
actor,
|
||||
permissions: [permission],
|
||||
});
|
||||
expect(reading).toHaveLength(1);
|
||||
expect(reading[0]).toMatchObject({ permission });
|
||||
expect(linked).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
linked.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns nothing for an actor with no user id', async () => {
|
||||
expect(
|
||||
await permService.validateUserPerms({
|
||||
|
||||
@@ -705,19 +705,20 @@ export class PermissionService extends PuterService {
|
||||
}): Promise<ReadingNode[]> {
|
||||
if (!actor.user?.id) return [];
|
||||
|
||||
const flatPromise = this.#flatValidateUserPerms(actor, permissions);
|
||||
const linkedPromise = this.#linkedValidateUserPerms(
|
||||
const flatReading = await this.#flatValidateUserPerms(
|
||||
actor,
|
||||
permissions,
|
||||
state ?? { antiCycleActors: [actor] },
|
||||
);
|
||||
|
||||
const flatReading = await flatPromise;
|
||||
if (flatReading.length > 0) {
|
||||
return flatReading[0].deleted ? [] : flatReading;
|
||||
}
|
||||
|
||||
const linkedReading = await linkedPromise;
|
||||
// Only on a miss: started beside the flat read, nothing awaits its rejection.
|
||||
const linkedReading = await this.#linkedValidateUserPerms(
|
||||
actor,
|
||||
permissions,
|
||||
state ?? { antiCycleActors: [actor] },
|
||||
);
|
||||
const flatOptions = PermissionUtil.readingToOptions(linkedReading);
|
||||
|
||||
// Warm flat KV cache for future hits (fire-and-forget, don't block
|
||||
|
||||
Reference in New Issue
Block a user