add office-aware passport policies and alert lifecycle

Enforce geofenced location evidence for passport challenges and extend admin alerting so mobile devices can review, dismiss, and act on real org and security events.
This commit is contained in:
2026-04-20 13:21:28 +00:00
parent a1a684ee81
commit e9eb9b4172
11 changed files with 548 additions and 37 deletions
+45
View File
@@ -261,6 +261,42 @@ tap.test('routes organization-scoped alerts to org admins by rule', async () =>
}
});
tap.test('uses built-in organization fallback rules for app connection events', async () => {
const { manager, users, roles, passportDevices, deliveredHints, restore } = createTestAlertManager();
try {
addUser(users, { id: 'owner-1', email: 'owner@example.com' });
addPassportDevice(passportDevices, { id: 'owner-device', userId: 'owner-1', label: 'Owner Phone' });
const ownerRole = new Role();
ownerRole.id = 'role-owner';
ownerRole.data = {
userId: 'owner-1',
organizationId: 'org-1',
roles: ['owner'],
};
roles.set(ownerRole.id, ownerRole);
const createdAlerts = await manager.createAlertsForEvent({
category: 'admin',
eventType: 'org_app_connected',
severity: 'medium',
title: 'Organization app connected',
body: 'A new app was connected.',
actorUserId: 'owner-1',
organizationId: 'org-1',
relatedEntityId: 'app-1',
relatedEntityType: 'global-app',
});
expect(createdAlerts).toHaveLength(1);
expect(createdAlerts[0].data.recipientUserId).toEqual('owner-1');
expect(deliveredHints).toHaveLength(1);
} finally {
restore();
}
});
tap.test('lists alerts, resolves hint lookups, and marks alerts seen', async () => {
const { manager, alerts, restore } = createTestAlertManager();
@@ -299,6 +335,15 @@ tap.test('lists alerts, resolves hint lookups, and marks alerts seen', async ()
expect(seenAlert.data.notification.status).toEqual('seen');
expect(seenAlert.data.seenAt).toBeGreaterThan(0);
expect(alerts.get('alert-1')?.data.notification.status).toEqual('seen');
const dismissedAlert = await manager.dismissAlert('user-1', 'hint-1');
expect(dismissedAlert.data.dismissedAt).toBeGreaterThan(0);
const defaultList = await manager.listAlertsForUser('user-1');
expect(defaultList).toHaveLength(0);
const fullList = await manager.listAlertsForUser('user-1', true);
expect(fullList).toHaveLength(1);
} finally {
restore();
}