mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 08:15:55 +00:00
test: check that pairing, the policy Bearer check and the SSO hand-off answer 503 while the shared store is down #1066
This commit is contained in:
@@ -43,6 +43,9 @@ vi.mock('@/lib/admin/audit', () => ({ auditLog: vi.fn() }));
|
||||
|
||||
import { GET } from '@/app/api/admin/policy/route';
|
||||
import { resetBearerIdentityCacheForTests } from '@/lib/auth/bearer-identity';
|
||||
import { setStoreForTests } from '@/lib/shared-store';
|
||||
import { resetLocalStoreForTests } from '@/lib/shared-store/local';
|
||||
import { switchableSharedStore } from '@/lib/__tests__/helpers/shared-store-down';
|
||||
|
||||
const SERVER = 'https://mail.example.org';
|
||||
const GOOD_TOKEN = 'good-token-abc';
|
||||
@@ -194,3 +197,21 @@ describe('GET /api/admin/policy with a Bearer token', () => {
|
||||
expect(JSON.stringify(logs.calls)).not.toContain(GOOD_TOKEN);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/admin/policy with a Bearer token while the shared store is down (#1066)', () => {
|
||||
afterEach(() => {
|
||||
setStoreForTests(null);
|
||||
resetLocalStoreForTests();
|
||||
});
|
||||
|
||||
it('answers 503: the refused-token budget can not be counted, so the token is not checked', async () => {
|
||||
const store = switchableSharedStore();
|
||||
store.down = true;
|
||||
setStoreForTests(store);
|
||||
const res = await get({ authorization: `Bearer ${GOOD_TOKEN}` });
|
||||
expect(res.status).toBe(503);
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
// Without a token the public subset needs no store.
|
||||
expect((await get()).status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,6 +59,9 @@ import { openPhoneRefreshToken } from '@/lib/auth/pair-bundle';
|
||||
import { POST as createPOST } from '@/app/api/auth/pair/create/route';
|
||||
import { POST as redeemPOST } from '@/app/api/auth/pair/redeem/route';
|
||||
import { GET as statusGET } from '@/app/api/auth/pair/status/route';
|
||||
import { setStoreForTests } from '@/lib/shared-store';
|
||||
import { resetLocalStoreForTests } from '@/lib/shared-store/local';
|
||||
import { switchableSharedStore } from './helpers/shared-store-down';
|
||||
|
||||
const SERVER = 'https://mail.example.org';
|
||||
const CUSTOM_SERVER = 'https://custom.example';
|
||||
@@ -852,3 +855,20 @@ describe('pair/create for a server the app refuses', () => {
|
||||
expect((await create({ password: PASSWORD })).status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pair/create while the shared store is down (#1066)', () => {
|
||||
afterEach(() => {
|
||||
setStoreForTests(null);
|
||||
resetLocalStoreForTests();
|
||||
});
|
||||
|
||||
it('answers 503 to a password step-up and sends nothing to the mail server', async () => {
|
||||
const store = switchableSharedStore();
|
||||
store.down = true;
|
||||
setStoreForTests(store);
|
||||
const res = await create({ password: PASSWORD });
|
||||
expect(res.status).toBe(503);
|
||||
expect(res.body.error).toBe('temporarily_unavailable');
|
||||
expect(calls).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,6 +62,9 @@ import { openPhoneRefreshToken } from '@/lib/auth/pair-bundle';
|
||||
import { POST as completePOST } from '@/app/api/auth/reauth/sso/complete/route';
|
||||
import { POST as createPOST } from '@/app/api/auth/pair/create/route';
|
||||
import { POST as redeemPOST } from '@/app/api/auth/pair/redeem/route';
|
||||
import { setStoreForTests } from '@/lib/shared-store';
|
||||
import { resetLocalStoreForTests } from '@/lib/shared-store/local';
|
||||
import { switchableSharedStore } from './helpers/shared-store-down';
|
||||
|
||||
const SERVER = 'https://mail.example.org';
|
||||
const WEBMAIL = 'https://webmail.example';
|
||||
@@ -338,3 +341,21 @@ describe('reauth/sso/complete freshness', () => {
|
||||
expect((await complete()).status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reauth/sso/complete while the shared store is down (#1066)', () => {
|
||||
afterEach(() => {
|
||||
setStoreForTests(null);
|
||||
resetLocalStoreForTests();
|
||||
});
|
||||
|
||||
it('answers 503 and sets no pairing proof when the grant can not be kept', async () => {
|
||||
const store = switchableSharedStore();
|
||||
store.down = true;
|
||||
setStoreForTests(store);
|
||||
signIn(2);
|
||||
pending({ slot: 2 });
|
||||
const res = await complete();
|
||||
expect(res.status).toBe(503);
|
||||
expect(readPairReauthFromStore(cookieStore as never)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @vitest-environment node
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
// POST /api/auth/sso/complete. A pairing step-up (purpose `reauth`)
|
||||
@@ -39,6 +39,9 @@ import { openPhoneRefreshToken } from '@/lib/auth/pair-bundle';
|
||||
import { POST } from '@/app/api/auth/sso/complete/route';
|
||||
import { createHash } from 'crypto';
|
||||
import { confirmHandoff, redeemHandoff, resetHandoffStoreForTests } from '@/lib/auth/handoff-store';
|
||||
import { setStoreForTests } from '@/lib/shared-store';
|
||||
import { resetLocalStoreForTests } from '@/lib/shared-store/local';
|
||||
import { switchableSharedStore } from './helpers/shared-store-down';
|
||||
|
||||
function pending(extra: Record<string, unknown> = {}) {
|
||||
jar.set('sso_pending', encryptPayload({
|
||||
@@ -198,3 +201,26 @@ describe('/api/auth/sso/complete for an app that sent a PKCE challenge', () => {
|
||||
expect(res.body.mobile_handoff_code).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('sso/complete mobile hand-off while the shared store is down (#1066)', () => {
|
||||
afterEach(() => {
|
||||
setStoreForTests(null);
|
||||
resetLocalStoreForTests();
|
||||
});
|
||||
|
||||
it('answers 503 instead of a hand-off code that could never be redeemed', async () => {
|
||||
const store = switchableSharedStore();
|
||||
store.down = true;
|
||||
setStoreForTests(store);
|
||||
tokenEndpoint = 'https://mail.example.org/auth/token';
|
||||
pending({
|
||||
redirect_uri: 'https://webmail.example/mail/en/auth/callback',
|
||||
mobile_redirect_uri: 'bulwarkmobile://auth/callback',
|
||||
mobile_state: 'm-1',
|
||||
mobile_code_challenge: createHash('sha256').update('x'.repeat(50)).digest('base64url'),
|
||||
});
|
||||
const res = await complete();
|
||||
expect(res.status).toBe(503);
|
||||
expect(JSON.stringify(res.body)).not.toMatch(/"at"|"rt"|mobile_handoff_code/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user