Merge origin/juancastro/put-2167-turn-new-notifications-into-pushes into juancastro/put-2169-rate-limits-for-app-pushes

This commit is contained in:
Juan Castro committed 2026-10-26 11:55:59 -04:00
commit 9d110e6322
3 files changed
+67 -1

No files matched your search

@@ -51,6 +51,11 @@ import { PuterServer } from '../../server.js';
import { createSecret as otpCreateSecret } from '../../services/auth/OTPUtil.js';
import { kvSharePermission } from '../../services/kvShare/kvShares.js';
import { FULL_API_ACCESS } from '../../services/permission/consts.js';
import {
hasPushConsent,
NOTIFICATIONS_PUSH_PERMISSION,
type PushConsentDeps,
} from '../../services/push/pushConsent.js';
import { setupTestServer } from '../../testUtil.js';
import { resetCardVerificationStatusCache } from '../../util/cardFallback.js';
import { FS_READ_LIMIT } from '../fs/limits.js';
@@ -2179,6 +2184,58 @@ describe('AuthController grant flows', () => {
).toContain(permission);
});
it('grant-user-app: grants an app push consent with notifications:push', async () => {
const app = await server.stores.app.create(
{
name: `tgp-${uuidv4()}`,
title: 'TestGrantPushApp',
index_url: 'https://example.test/index.html',
},
{ ownerUserId: issuer.id },
);
const deps = {
stores: server.stores,
services: { permission: server.services.permission },
} as unknown as PushConsentDeps;
expect(await hasPushConsent(deps, issuer.id, app.uid)).toBe(false);
// Anything past the bare permission names nothing a prompt describes.
await expect(
inCtx(issuerActor, () =>
controller.handleGrantUserApp(
makeReq(
{
app_uid: app.uid,
permission: `${NOTIFICATIONS_PUSH_PERMISSION}:extra`,
},
{ actor: issuerActor },
),
makeRes(),
),
),
).rejects.toMatchObject({
statusCode: 400,
legacyCode: 'permission_not_grantable',
});
expect(await hasPushConsent(deps, issuer.id, app.uid)).toBe(false);
const res = makeRes();
await inCtx(issuerActor, () =>
controller.handleGrantUserApp(
makeReq(
{
app_uid: app.uid,
permission: NOTIFICATIONS_PUSH_PERMISSION,
},
{ actor: issuerActor },
),
res,
),
);
expect(res.body).toEqual({});
expect(await hasPushConsent(deps, issuer.id, app.uid)).toBe(true);
});
it('grant-user-app: resolves `origin` to the app when app_uid is omitted', async () => {
// The GUI permission dialog and puter.perms.grantOrigin() identify
// third-party sites by origin rather than app uid.
@@ -95,6 +95,7 @@ describe('parseAppGrantablePermission', () => {
},
],
['events:background', { family: 'backgroundEvents' }],
['notifications:push', { family: 'pushNotifications' }],
])('accepts %s', (permission, parsed) => {
expect(parse(permission)).toEqual(parsed);
});
@@ -118,6 +119,9 @@ describe('parseAppGrantablePermission', () => {
'*',
'events',
'events:background:extra',
'notifications',
'notifications:push:extra',
'notifications:other',
// Modeless, or a mode that does not exist.
'fs',
`fs:${FILE}`,
@@ -24,6 +24,7 @@ import {
type KvShareAccess,
kvShareRoot,
} from '../kvShare/kvShares.js';
import { NOTIFICATIONS_PUSH_PERMISSION } from '../push/pushConsent.js';
import {
APP_DATA_CLASSES,
APP_DATA_KV_OPS,
@@ -62,7 +63,8 @@ export type AppGrantablePermission =
appUid: string;
segments: string[];
}
| { family: 'backgroundEvents' };
| { family: 'backgroundEvents' }
| { family: 'pushNotifications' };
/** One component as it is written canonically: `\` as `\\`, `:` as `\C`. */
const escapeComponent = (component: string): string =>
@@ -117,6 +119,9 @@ export const parseAppGrantablePermission = (
if (permission === 'events:background') {
return { family: 'backgroundEvents' };
}
if (permission === NOTIFICATIONS_PUSH_PERMISSION) {
return { family: 'pushNotifications' };
}
const parts = PermissionUtil.split(permission);
// Checks that run on the raw string, after this one, match only the