diff --git a/api/src/unraid-api/auth/auth.module.ts b/api/src/unraid-api/auth/auth.module.ts index a6e1e81d1..fb97414e5 100644 --- a/api/src/unraid-api/auth/auth.module.ts +++ b/api/src/unraid-api/auth/auth.module.ts @@ -3,10 +3,15 @@ import { AuthService } from './auth.service'; import { UsersModule } from '@app/unraid-api/users/users.module'; import { PassportModule } from '@nestjs/passport'; import { ServerHeaderStrategy } from '@app/unraid-api/auth/header.strategy'; -import { CookieService } from './cookie.service'; +import { CookieService, SESSION_COOKIE_OPTIONS } from './cookie.service'; @Module({ imports: [UsersModule, PassportModule], - providers: [AuthService, ServerHeaderStrategy, CookieService], + providers: [ + AuthService, + ServerHeaderStrategy, + CookieService, + { provide: SESSION_COOKIE_OPTIONS, useValue: CookieService.defaultOpts() }, + ], }) export class AuthModule {} diff --git a/api/src/unraid-api/auth/cookie.service.ts b/api/src/unraid-api/auth/cookie.service.ts index 688d4cdb8..d74630043 100644 --- a/api/src/unraid-api/auth/cookie.service.ts +++ b/api/src/unraid-api/auth/cookie.service.ts @@ -1,13 +1,33 @@ import { fileExists } from '@app/core/utils/files/file-exists'; import { batchProcess } from '@app/utils'; -import { Injectable } from '@nestjs/common'; +import { Injectable, Inject } from '@nestjs/common'; import { join } from 'path'; +/** token for dependency injection of a session cookie options object */ +export const SESSION_COOKIE_OPTIONS = 'SESSION_COOKIE_OPTIONS'; + +type SessionCookieOptions = { + namePrefix: string; + sessionDir: string; +}; + @Injectable() export class CookieService { + constructor( + @Inject(SESSION_COOKIE_OPTIONS) readonly opts: SessionCookieOptions = CookieService.defaultOpts() + ) {} + + /** + * @returns new SessionCookieOptions with `namePrefix: 'unraid_', sessionDir: '/var/lib/php'` + */ + static defaultOpts(): SessionCookieOptions { + return { namePrefix: 'unraid_', sessionDir: '/var/lib/php' }; + } + /** * Given a cookies object, returns true if any of the cookies are a valid unraid session cookie. * @param cookies an object of cookie name => cookie value + * @param opts optional overrides for the session directory & prefix of the session cookie to look for * @returns true if any of the cookies are a valid unraid session cookie, false otherwise */ async hasValidAuthCookie(cookies: object): Promise { @@ -17,20 +37,32 @@ export class CookieService { return data.some((valid) => valid); } - async isValidAuthCookie(cookieName: string, cookieValue: string): Promise { - if (!cookieName.startsWith('unraid_')) { + /** + * Checks if a given details point to a valid unraid session cookie. + * + * A valid cookie is one where the name starts with the configured prefix + * and the value corresponds to an existing session file on disk. + * + * @param cookieName the name of the cookie to check + * @param cookieValue the value of the cookie to check + * @returns true if the cookie is valid, false otherwise + */ + private async isValidAuthCookie(cookieName: string, cookieValue: string): Promise { + const { namePrefix } = this.opts; + if (!cookieName.startsWith(namePrefix)) { return false; } - return fileExists(this.makeSessionFilePath(cookieValue)); + return fileExists(this.getSessionFilePath(cookieValue)); } /** * Given a session id, returns the full path to the session file on disk. * * @param sessionId the session id, as read from the session cookie. + * @param basePath path to the directory of session files. * @returns the full path to the session file on disk. */ - private makeSessionFilePath(sessionId: string): string { - return join('/var/lib/php', `sess_${sessionId}`); + public getSessionFilePath(sessionId: string): string { + return join(this.opts.sessionDir, `sess_${sessionId}`); } }