diff --git a/src/storage/mapping/BaseFileIdentifierMapper.ts b/src/storage/mapping/BaseFileIdentifierMapper.ts index 49daab823..e950b336c 100644 --- a/src/storage/mapping/BaseFileIdentifierMapper.ts +++ b/src/storage/mapping/BaseFileIdentifierMapper.ts @@ -1,15 +1,18 @@ import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier'; import { getLoggerFor } from '../../logging/LogUtil'; import { APPLICATION_OCTET_STREAM } from '../../util/ContentTypes'; +import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError'; +import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError'; import { + decodeUriPathComponents, encodeUriPathComponents, ensureTrailingSlash, isContainerIdentifier, + joinFilePath, normalizeFilePath, trimTrailingSlashes, } from '../../util/PathUtil'; import type { FileIdentifierMapper, ResourceLink } from './FileIdentifierMapper'; -import { getAbsolutePath, getRelativePath, validateRelativePath } from './MapperUtil'; /** * Base class for {@link FileIdentifierMapper} implementations. @@ -34,10 +37,10 @@ export class BaseFileIdentifierMapper implements FileIdentifierMapper { * @returns A ResourceLink with all the necessary metadata. */ public async mapUrlToFilePath(identifier: ResourceIdentifier, contentType?: string): Promise { - const path = getRelativePath(this.baseRequestURI, identifier); - validateRelativePath(path, identifier); + const path = this.getRelativePath(identifier); + this.validateRelativePath(path, identifier); - const filePath = getAbsolutePath(this.rootFilepath, path); + const filePath = this.getAbsolutePath(path); return isContainerIdentifier(identifier) ? this.mapUrlToContainerPath(identifier, filePath) : this.mapUrlToDocumentPath(identifier, filePath, contentType); @@ -142,4 +145,52 @@ export class BaseFileIdentifierMapper implements FileIdentifierMapper { protected async getContentTypeFromPath(filePath: string): Promise { return APPLICATION_OCTET_STREAM; } + + /** + * Get the absolute file path based on the rootFilepath. + * @param path - The relative file path. + * + * @returns Absolute path of the file. + */ + protected getAbsolutePath(path: string): string { + return joinFilePath(this.rootFilepath, path); + } + + /** + * Strips the baseRequestURI from the identifier. + * @param identifier - Incoming identifier. + * + * @throws {@link NotFoundHttpError} + * If the identifier does not match the baseRequestURI. + * + * @returns A string representing the relative path. + */ + protected getRelativePath(identifier: ResourceIdentifier): string { + if (!identifier.path.startsWith(this.baseRequestURI)) { + this.logger.warn(`The URL ${identifier.path} is outside of the scope ${this.baseRequestURI}`); + throw new NotFoundHttpError(); + } + return decodeUriPathComponents(identifier.path.slice(this.baseRequestURI.length)); + } + + /** + * Check if the given relative path is valid. + * + * @throws {@link BadRequestHttpError} + * If the relative path is invalid. + * + * @param path - A relative path, as generated by {@link getRelativePath}. + * @param identifier - A resource identifier. + */ + protected validateRelativePath(path: string, identifier: ResourceIdentifier): void { + if (!path.startsWith('/')) { + this.logger.warn(`URL ${identifier.path} needs a / after the base`); + throw new BadRequestHttpError('URL needs a / after the base'); + } + + if (path.includes('/..')) { + this.logger.warn(`Disallowed /.. segment in URL ${identifier.path}.`); + throw new BadRequestHttpError('Disallowed /.. segment in URL'); + } + } } diff --git a/src/storage/mapping/MapperUtil.ts b/src/storage/mapping/MapperUtil.ts deleted file mode 100644 index 5d919ea98..000000000 --- a/src/storage/mapping/MapperUtil.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier'; -import { getLoggerFor } from '../../logging/LogUtil'; -import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError'; -import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError'; -import { joinFilePath, decodeUriPathComponents } from '../../util/PathUtil'; - -const logger = getLoggerFor('MapperUtil'); - -/** - * Get the absolute file path based on the rootFilepath of the store. - * @param rootFilepath - The root file path. - * @param path - The relative file path. - * @param identifier - Optional identifier to add to the path. - * - * @returns Absolute path of the file. - */ -export function getAbsolutePath(rootFilepath: string, path: string, identifier = ''): string { - return joinFilePath(rootFilepath, path, identifier); -} - -/** - * Strips the baseRequestURI from the identifier and checks if the stripped base URI matches the store's one. - * @param baseRequestURI - Base URL for requests. - * @param identifier - Incoming identifier. - * - * @throws {@link NotFoundHttpError} - * If the identifier does not match the baseRequestURI path of the store. - * - * @returns A string representing the relative path. - */ -export function getRelativePath(baseRequestURI: string, identifier: ResourceIdentifier): string { - if (!identifier.path.startsWith(baseRequestURI)) { - logger.warn(`The URL ${identifier.path} is outside of the scope ${baseRequestURI}`); - throw new NotFoundHttpError(); - } - return decodeUriPathComponents(identifier.path.slice(baseRequestURI.length)); -} - -/** - * Check if the given relative path is valid. - * - * @throws {@link BadRequestHttpError} - * If the relative path is invalid. - * - * @param path - A relative path, as generated by {@link getRelativePath}. - * @param identifier - A resource identifier. - */ -export function validateRelativePath(path: string, identifier: ResourceIdentifier): void { - if (!path.startsWith('/')) { - logger.warn(`URL ${identifier.path} needs a / after the base`); - throw new BadRequestHttpError('URL needs a / after the base'); - } - - if (path.includes('/..')) { - logger.warn(`Disallowed /.. segment in URL ${identifier.path}.`); - throw new BadRequestHttpError('Disallowed /.. segment in URL'); - } -}