refactor: Move MapperUtil functions to BaseFileIdentifierMapper

This commit is contained in:
Joachim Van Herwegen 2021-02-11 14:47:29 +01:00
parent 55fddf8e60
commit e9502e55a7
2 changed files with 55 additions and 62 deletions

View File

@ -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<ResourceLink> {
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<string> {
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');
}
}
}

View File

@ -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');
}
}