refactor(quick-260921-m34): Aufgabe 2a - gemeinsamer Aufrufer-Typ, aus den Signierstellen abgeleitet

apps/api/src/auth/types/auth-user.ts angelegt: AuthUser, AuthenticatedRequest,
LocalAuthenticatedRequest, LoginUser, JwtPayload, UploadedFileLike. Jedes Feld
traegt seine Herkunft als Kommentar.

tenantId ist string, hergeleitet und nicht gewaehlt: die Spalte User.tenantId
ist in schema.prisma Pflicht, beide Signierstellen schreiben genau sie, und
der Bestand beschreibt dasselbe Objekt in SessionUser schon so. Der
SUPER_ADMIN-Zweig in TenantGuard spricht nicht dagegen - der Waechter liest
AuthUser gar nicht, und dass es den Zweig gibt, steht als null in
AuthenticatedRequest.tenantId weiter im Typsystem. tenant.guard.ts bleibt
unberuehrt.

role ist die Aufzaehlung Role: schema.prisma deklariert die Spalte so, die
SQL-Funktion auth_lookup_user_by_username gibt sie als "Role" zurueck. Die
Handannotation role: string in AuthLookupUserByUsernameRow war eine zweite
Fassung desselben Wertes und faellt damit weg.

SessionUser und UploadedPng in bug-reports.service.ts sind jetzt Pick<> der
neuen Typen statt eigener Beschreibungen.

Fixtures in auth.controller.spec.ts ergaenzt: sie uebergaben einen Aufrufer
ohne username und ohne mustChangePassword - eine Form, die JwtStrategy nie
erzeugt. Testzahlen unveraendert.

noExplicitAny in apps/api/src: 149 -> 137. type-check 4/4, lint 5/5,
apps/api 72/1143, apps/web 73/531.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 17:03:24 +02:00
parent b188946e31
commit f2fc39f51c
10 changed files with 212 additions and 38 deletions
+8 -8
View File
@@ -36,7 +36,7 @@ describe('AuthController.me', () => {
authService.getMe.mockResolvedValue({ id: 'u1' });
const controller = new AuthController(authService, makeFakeUserService());
await controller.me({ id: 'u1', tenantId: 't1', role: 'USER' });
await controller.me({ id: 'u1', username: 'u1', tenantId: 't1', role: Role.USER, mustChangePassword: false });
expect(authService.getMe).toHaveBeenCalledWith('t1', 'u1');
});
@@ -46,7 +46,7 @@ describe('AuthController.me', () => {
authService.getMe.mockResolvedValue({ id: 'u1' });
const controller = new AuthController(authService, makeFakeUserService());
await controller.me({ id: 'u1', tenantId: 't1', role: Role.SUPER_ADMIN });
await controller.me({ id: 'u1', username: 'u1', tenantId: 't1', role: Role.SUPER_ADMIN, mustChangePassword: false });
expect(authService.getMe).toHaveBeenCalledWith('t1', 'u1');
});
@@ -56,7 +56,7 @@ describe('AuthController.me', () => {
authService.getMe.mockResolvedValue(null);
const controller = new AuthController(authService, makeFakeUserService());
const result = await controller.me({ id: 'u1', tenantId: 't1', role: 'USER' });
const result = await controller.me({ id: 'u1', username: 'u1', tenantId: 't1', role: Role.USER, mustChangePassword: false });
expect(result).toBeNull();
});
@@ -69,7 +69,7 @@ describe('AuthController.changePassword', () => {
const res = {} as any;
const result = await controller.changePassword(
{ id: 'u1', tenantId: 't1', role: 'USER' },
{ id: 'u1', username: 'u1', tenantId: 't1', role: Role.USER, mustChangePassword: false },
{ currentPassword: 'old', newPassword: 'new' } as any,
res,
);
@@ -88,7 +88,7 @@ describe('AuthController.adminResetPassword', () => {
const result = await controller.adminResetPassword(
'target',
{ newPassword: 'new-password' } as any,
{ id: 'admin-1', tenantId: 't1', role: Role.ADMIN },
{ id: 'admin-1', username: 'admin-1', tenantId: 't1', role: Role.ADMIN, mustChangePassword: false },
);
expect(authService.adminResetPassword).toHaveBeenCalledWith(
@@ -109,7 +109,7 @@ describe('AuthController.adminResetPassword', () => {
await controller.adminResetPassword(
'target',
{ newPassword: 'new-password', mustChangePassword: false } as any,
{ id: 'admin-1', tenantId: 't1', role: Role.ADMIN },
{ id: 'admin-1', username: 'admin-1', tenantId: 't1', role: Role.ADMIN, mustChangePassword: false },
);
expect(authService.adminResetPassword).toHaveBeenCalledWith(
@@ -134,7 +134,7 @@ describe('AuthController.adminResetPassword', () => {
await controller.adminResetPassword(
'target',
{ newPassword: 'new-password' } as any,
{ id: 'super-1', tenantId: 't1', role: Role.SUPER_ADMIN },
{ id: 'super-1', username: 'super-1', tenantId: 't1', role: Role.SUPER_ADMIN, mustChangePassword: false },
);
expect(userService.findByIdForPlatformAdmin).toHaveBeenCalledTimes(1);
@@ -158,7 +158,7 @@ describe('AuthController.adminResetPassword', () => {
controller.adminResetPassword(
'unknown',
{ newPassword: 'new-password' } as any,
{ id: 'super-1', tenantId: 't1', role: Role.SUPER_ADMIN },
{ id: 'super-1', username: 'super-1', tenantId: 't1', role: Role.SUPER_ADMIN, mustChangePassword: false },
),
).rejects.toThrow(new BadRequestException('User not found'));
expect(authService.adminResetPassword).not.toHaveBeenCalled();