Skip to content

Commit 95e3c1f

Browse files
author
mdatelle
committed
refactor: move CustomRequest to fastify.ts and update FastifyRequest types
1 parent 7e2bcc9 commit 95e3c1f

File tree

4 files changed

+55
-28
lines changed

4 files changed

+55
-28
lines changed

api/src/types/fastify.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,30 @@ import type {
44
FastifyRequest as BaseFastifyRequest,
55
} from 'fastify';
66

7-
export type FastifyInstance = BaseFastifyInstance;
8-
export interface FastifyRequest extends BaseFastifyRequest {
9-
cookies: Record<string, string | undefined>;
10-
headers: Record<string, string | undefined>;
7+
// Common headers
8+
export interface CommonHeaders {
9+
'x-api-key'?: string;
10+
'x-csrf-token'?: string;
11+
'x-unraid-api-version'?: string;
12+
'x-flash-guid'?: string;
13+
}
14+
15+
// Common query parameters
16+
export interface CommonQuery {
17+
csrf_token?: string;
1118
}
19+
20+
// Base types
21+
type Headers = Record<string, string | string[] | undefined> & Partial<CommonHeaders>;
22+
type Query = Record<string, string | undefined> & Partial<CommonQuery>;
23+
type Cookies = Record<string, string | undefined>;
24+
25+
export type FastifyRequest = BaseFastifyRequest<{
26+
Headers: Headers;
27+
Querystring: Query;
28+
}> & {
29+
cookies: Cookies;
30+
};
31+
32+
export type FastifyInstance = BaseFastifyInstance;
1233
export type FastifyReply = BaseFastifyReply;

api/src/unraid-api/auth/auth.service.spec.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { AuthZService } from 'nest-authz';
55
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
66

77
import type { ApiKey, ApiKeyWithSecret, UserAccount } from '@app/graphql/generated/api/types.js';
8-
import type { FastifyRequest } from '@app/types/fastify.js';
98
import { Resource, Role } from '@app/graphql/generated/api/types.js';
9+
import { FastifyRequest } from '@app/types/fastify.js';
1010
import { ApiKeyService } from '@app/unraid-api/auth/api-key.service.js';
1111
import { AuthService } from '@app/unraid-api/auth/auth.service.js';
1212
import { CookieService } from '@app/unraid-api/auth/cookie.service.js';
@@ -50,17 +50,13 @@ describe('AuthService', () => {
5050
};
5151

5252
// Mock FastifyRequest object for tests
53-
const createMockRequest = (overrides = {}): FastifyRequest => {
54-
return {
55-
headers: {},
56-
query: {},
53+
const createMockRequest = (overrides = {}) =>
54+
({
55+
headers: { 'x-csrf-token': undefined },
56+
query: { csrf_token: undefined },
5757
cookies: {},
58-
id: 'test-id',
59-
params: {},
60-
raw: {} as any,
6158
...overrides,
62-
} as FastifyRequest;
63-
};
59+
}) as FastifyRequest;
6460

6561
beforeEach(async () => {
6662
const enforcer = await newEnforcer();
@@ -82,7 +78,9 @@ describe('AuthService', () => {
8278
vi.spyOn(authzService, 'getRolesForUser').mockResolvedValue([Role.ADMIN]);
8379
vi.spyOn(authService, 'validateCsrfToken').mockReturnValue(true);
8480

85-
const mockRequest = createMockRequest();
81+
const mockRequest = createMockRequest({
82+
headers: { 'x-csrf-token': 'valid-token' },
83+
});
8684
const result = await authService.validateCookiesCasbin(mockRequest);
8785

8886
expect(result).toEqual(mockUser);
@@ -92,7 +90,9 @@ describe('AuthService', () => {
9290
vi.spyOn(cookieService, 'hasValidAuthCookie').mockResolvedValue(false);
9391
vi.spyOn(authService, 'validateCsrfToken').mockReturnValue(true);
9492

95-
const mockRequest = createMockRequest();
93+
const mockRequest = createMockRequest({
94+
headers: { 'x-csrf-token': 'valid-token' },
95+
});
9696
await expect(authService.validateCookiesCasbin(mockRequest)).rejects.toThrow(
9797
UnauthorizedException
9898
);
@@ -126,11 +126,27 @@ describe('AuthService', () => {
126126
it('should throw UnauthorizedException when CSRF token is invalid', async () => {
127127
vi.spyOn(authService, 'validateCsrfToken').mockReturnValue(false);
128128

129-
const mockRequest = createMockRequest();
129+
const mockRequest = createMockRequest({
130+
headers: { 'x-csrf-token': 'invalid-token' },
131+
});
130132
await expect(authService.validateCookiesCasbin(mockRequest)).rejects.toThrow(
131133
new UnauthorizedException('Invalid CSRF token')
132134
);
133135
});
136+
137+
it('should accept CSRF token from query parameter', async () => {
138+
vi.spyOn(cookieService, 'hasValidAuthCookie').mockResolvedValue(true);
139+
vi.spyOn(authService, 'getSessionUser').mockResolvedValue(mockUser);
140+
vi.spyOn(authzService, 'getRolesForUser').mockResolvedValue([Role.ADMIN]);
141+
vi.spyOn(authService, 'validateCsrfToken').mockReturnValue(true);
142+
143+
const mockRequest = createMockRequest({
144+
query: { csrf_token: 'valid-token' },
145+
});
146+
const result = await authService.validateCookiesCasbin(mockRequest);
147+
148+
expect(result).toEqual(mockUser);
149+
});
134150
});
135151

136152
describe('syncApiKeyRoles', () => {

api/src/unraid-api/auth/auth.service.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,7 @@ export class AuthService {
5151

5252
async validateCookiesCasbin(request: FastifyRequest): Promise<UserAccount> {
5353
try {
54-
if (
55-
!this.validateCsrfToken(
56-
request.headers['x-csrf-token'] ||
57-
(request.query as { csrf_token?: string })?.csrf_token
58-
)
59-
) {
54+
if (!this.validateCsrfToken(request.headers['x-csrf-token'] || request.query.csrf_token)) {
6055
throw new UnauthorizedException('Invalid CSRF token');
6156
}
6257

api/src/unraid-api/types/request.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)