wip league admin tools
This commit is contained in:
67
apps/api/src/domain/auth/ActorFromSession.test.ts
Normal file
67
apps/api/src/domain/auth/ActorFromSession.test.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { requestContextMiddleware } from '@adapters/http/RequestContext';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import { getActorFromRequestContext } from './getActorFromRequestContext';
|
||||
import { requireLeagueAdminOrOwner } from '../league/LeagueAuthorization';
|
||||
|
||||
async function withRequestContext<T>(req: Record<string, unknown>, fn: () => Promise<T>): Promise<T> {
|
||||
const res = {};
|
||||
|
||||
return await new Promise<T>((resolve, reject) => {
|
||||
requestContextMiddleware(req as any, res as any, () => {
|
||||
fn().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('ActorFromSession', () => {
|
||||
it('derives actor from authenticated session (request.user), not request payload', async () => {
|
||||
const req: any = {
|
||||
user: { userId: 'driver-from-session' },
|
||||
body: { driverId: 'driver-from-body' },
|
||||
};
|
||||
|
||||
await withRequestContext(req, async () => {
|
||||
const actor = getActorFromRequestContext();
|
||||
expect(actor).toEqual({ userId: 'driver-from-session', driverId: 'driver-from-session' });
|
||||
});
|
||||
});
|
||||
|
||||
it('permission helper invokes league admin check using session-derived actor (ignores payload)', async () => {
|
||||
const getLeagueAdminPermissionsUseCase = {
|
||||
execute: vi.fn(async () => Result.ok(undefined)),
|
||||
};
|
||||
|
||||
const req: any = {
|
||||
user: { userId: 'driver-from-session' },
|
||||
body: { performerDriverId: 'driver-from-body' },
|
||||
};
|
||||
|
||||
await withRequestContext(req, async () => {
|
||||
await expect(
|
||||
requireLeagueAdminOrOwner('league-1', getLeagueAdminPermissionsUseCase as any),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
expect(getLeagueAdminPermissionsUseCase.execute).toHaveBeenCalledWith({
|
||||
leagueId: 'league-1',
|
||||
performerDriverId: 'driver-from-session',
|
||||
});
|
||||
});
|
||||
|
||||
it('permission helper rejects when league admin check fails', async () => {
|
||||
const getLeagueAdminPermissionsUseCase = {
|
||||
execute: vi.fn(async () =>
|
||||
Result.err({ code: 'USER_NOT_MEMBER', details: { message: 'nope' } } as any),
|
||||
),
|
||||
};
|
||||
|
||||
const req: any = { user: { userId: 'driver-from-session' } };
|
||||
|
||||
await withRequestContext(req, async () => {
|
||||
await expect(
|
||||
requireLeagueAdminOrOwner('league-1', getLeagueAdminPermissionsUseCase as any),
|
||||
).rejects.toThrow('Forbidden');
|
||||
});
|
||||
});
|
||||
});
|
||||
25
apps/api/src/domain/auth/getActorFromRequestContext.ts
Normal file
25
apps/api/src/domain/auth/getActorFromRequestContext.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { getHttpRequestContext } from '@adapters/http/RequestContext';
|
||||
|
||||
export type Actor = {
|
||||
userId: string;
|
||||
driverId: string;
|
||||
};
|
||||
|
||||
type AuthenticatedRequest = {
|
||||
user?: { userId: string };
|
||||
};
|
||||
|
||||
export function getActorFromRequestContext(): Actor {
|
||||
const ctx = getHttpRequestContext();
|
||||
const req = ctx.req as unknown as AuthenticatedRequest;
|
||||
|
||||
const userId = req.user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('Unauthorized');
|
||||
}
|
||||
|
||||
// Current canonical mapping:
|
||||
// - The authenticated session identity is `userId`.
|
||||
// - In the current system, that `userId` is also treated as the performer `driverId`.
|
||||
return { userId, driverId: userId };
|
||||
}
|
||||
Reference in New Issue
Block a user