mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-25 07:27:04 +00:00
fix(permissions): broadcast permission row-cache invalidations to peer regions
Every publishCacheKeys call for the u2u, u2a, and access-token row caches omitted broadcast, so a revoke only cleared the mutating region's Redis. A peer region applied the replicated generation bump, re-scanned, read the deleted row from its own still-warm 5-minute row cache, and re-warmed the flat view from it — revoked access outlived the revoke by the row-cache TTL instead of the intended 60-second bound. CacheReplicationService already consumes these events; the emits were just never sent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e47ec447b0
commit
d384a41c68
@@ -854,6 +854,35 @@ describe('PermissionStore', () => {
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('broadcasts a revoked row-cache key so peer regions drop it too', async () => {
|
||||
const issuer = await makeUser();
|
||||
const holder = await makeUser();
|
||||
await store.upsertUserUserPerm(
|
||||
holder.id,
|
||||
issuer.id,
|
||||
'fs:u:read',
|
||||
{},
|
||||
);
|
||||
|
||||
const seen: unknown[] = [];
|
||||
server.clients.event.on('outer.cacheUpdate', (_key, data) => {
|
||||
seen.push(data);
|
||||
});
|
||||
|
||||
await store.deleteUserUserPermByHolder(
|
||||
holder.id,
|
||||
'fs:u:read',
|
||||
issuer.id,
|
||||
);
|
||||
|
||||
// Without the broadcast, a peer region's warm `perms:u2u:holder:*`
|
||||
// cache keeps serving the revoked row for its full TTL — and every
|
||||
// scan there re-warms the flat view from it.
|
||||
expect(seen).toContainEqual({
|
||||
cacheKey: [`perms:u2u:holder:${holder.id}`],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns nothing for an empty permission list', async () => {
|
||||
const holder = await makeUser();
|
||||
expect(await store.readLinkedUserUserPerms(holder.id, [])).toEqual(
|
||||
|
||||
@@ -257,6 +257,7 @@ export class PermissionStore extends PuterStore {
|
||||
);
|
||||
await this.publishCacheKeys({
|
||||
keys: [this.#u2uCacheKey(holderUserId)],
|
||||
broadcast: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -280,6 +281,7 @@ export class PermissionStore extends PuterStore {
|
||||
if (!result.anyRowsAffected) return false;
|
||||
await this.publishCacheKeys({
|
||||
keys: [this.#u2uCacheKey(holderUserId)],
|
||||
broadcast: true,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -332,7 +334,8 @@ export class PermissionStore extends PuterStore {
|
||||
const keys = [
|
||||
...new Set(rows.map((r) => this.#u2uCacheKey(r.holder_user_id))),
|
||||
];
|
||||
if (keys.length > 0) await this.publishCacheKeys({ keys });
|
||||
if (keys.length > 0)
|
||||
await this.publishCacheKeys({ keys, broadcast: true });
|
||||
|
||||
return rows;
|
||||
}
|
||||
@@ -405,6 +408,7 @@ export class PermissionStore extends PuterStore {
|
||||
);
|
||||
await this.publishCacheKeys({
|
||||
keys: [this.#u2aCacheKey(userId, appId)],
|
||||
broadcast: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -419,6 +423,7 @@ export class PermissionStore extends PuterStore {
|
||||
);
|
||||
await this.publishCacheKeys({
|
||||
keys: [this.#u2aCacheKey(userId, appId)],
|
||||
broadcast: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -429,6 +434,7 @@ export class PermissionStore extends PuterStore {
|
||||
);
|
||||
await this.publishCacheKeys({
|
||||
keys: [this.#u2aCacheKey(userId, appId)],
|
||||
broadcast: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -501,7 +507,8 @@ export class PermissionStore extends PuterStore {
|
||||
.map((r) => this.#u2aCacheKey(r.user_id, r.app_id)),
|
||||
),
|
||||
];
|
||||
if (keys.length > 0) await this.publishCacheKeys({ keys });
|
||||
if (keys.length > 0)
|
||||
await this.publishCacheKeys({ keys, broadcast: true });
|
||||
|
||||
return removed;
|
||||
}
|
||||
@@ -706,6 +713,7 @@ export class PermissionStore extends PuterStore {
|
||||
async invalidateAccessTokenPerms(tokenUid: string): Promise<void> {
|
||||
await this.publishCacheKeys({
|
||||
keys: [this.#tokenCacheKey(tokenUid)],
|
||||
broadcast: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user