mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
refactor: abstract parts of ExtensionBasedMapper into MapperUtil
This commit is contained in:
parent
2c46d70780
commit
971e4178d1
@ -7,13 +7,13 @@ import { APPLICATION_OCTET_STREAM, TEXT_TURTLE } from '../../util/ContentTypes';
|
|||||||
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||||
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
||||||
import {
|
import {
|
||||||
decodeUriPathComponents,
|
|
||||||
encodeUriPathComponents,
|
encodeUriPathComponents,
|
||||||
ensureTrailingSlash,
|
ensureTrailingSlash,
|
||||||
isContainerIdentifier,
|
isContainerIdentifier,
|
||||||
trimTrailingSlashes,
|
trimTrailingSlashes,
|
||||||
} from '../../util/PathUtil';
|
} from '../../util/PathUtil';
|
||||||
import type { FileIdentifierMapper, ResourceLink } from '../FileIdentifierMapper';
|
import type { FileIdentifierMapper, ResourceLink } from '../FileIdentifierMapper';
|
||||||
|
import { getAbsolutePath, getRelativePath, validateRelativePath } from './MapperUtil';
|
||||||
|
|
||||||
const { join: joinPath, normalize: normalizePath } = posix;
|
const { join: joinPath, normalize: normalizePath } = posix;
|
||||||
|
|
||||||
@ -60,19 +60,10 @@ export class ExtensionBasedMapper implements FileIdentifierMapper {
|
|||||||
* @returns A ResourceLink with all the necessary metadata.
|
* @returns A ResourceLink with all the necessary metadata.
|
||||||
*/
|
*/
|
||||||
public async mapUrlToFilePath(identifier: ResourceIdentifier, contentType?: string): Promise<ResourceLink> {
|
public async mapUrlToFilePath(identifier: ResourceIdentifier, contentType?: string): Promise<ResourceLink> {
|
||||||
const path = this.getRelativePath(identifier);
|
const path = getRelativePath(this.baseRequestURI, identifier, this.logger);
|
||||||
|
validateRelativePath(path, identifier, this.logger);
|
||||||
|
|
||||||
if (!path.startsWith('/')) {
|
let filePath = getAbsolutePath(this.rootFilepath, path);
|
||||||
this.logger.warn(`URL ${identifier.path} needs a / after the base`);
|
|
||||||
throw new UnsupportedHttpError('URL needs a / after the base');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.includes('/..')) {
|
|
||||||
this.logger.warn(`Disallowed /.. segment in URL ${identifier.path}.`);
|
|
||||||
throw new UnsupportedHttpError('Disallowed /.. segment in URL');
|
|
||||||
}
|
|
||||||
|
|
||||||
let filePath = this.getAbsolutePath(path);
|
|
||||||
|
|
||||||
// Container
|
// Container
|
||||||
if (isContainerIdentifier(identifier)) {
|
if (isContainerIdentifier(identifier)) {
|
||||||
@ -200,32 +191,4 @@ export class ExtensionBasedMapper implements FileIdentifierMapper {
|
|||||||
const extension = /\.([^./]+)$/u.exec(path);
|
const extension = /\.([^./]+)$/u.exec(path);
|
||||||
return extension && extension[1];
|
return extension && extension[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the absolute file path based on the rootFilepath of the store.
|
|
||||||
* @param path - The relative file path.
|
|
||||||
* @param identifier - Optional identifier to add to the path.
|
|
||||||
*
|
|
||||||
* @returns Absolute path of the file.
|
|
||||||
*/
|
|
||||||
private getAbsolutePath(path: string, identifier = ''): string {
|
|
||||||
return joinPath(this.rootFilepath, path, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strips the baseRequestURI from the identifier and checks if the stripped base URI matches the store's one.
|
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
private 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
60
src/storage/mapping/MapperUtil.ts
Normal file
60
src/storage/mapping/MapperUtil.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { posix } from 'path';
|
||||||
|
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||||
|
import type { Logger } from '../../logging/Logger';
|
||||||
|
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||||
|
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
||||||
|
import { decodeUriPathComponents } from '../../util/Util';
|
||||||
|
|
||||||
|
const { join: joinPath } = posix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 const getAbsolutePath = (rootFilepath: string, path: string, identifier = ''): string =>
|
||||||
|
joinPath(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.
|
||||||
|
* @param logger - A logger instance.
|
||||||
|
*
|
||||||
|
* @throws {@link NotFoundHttpError}
|
||||||
|
* If the identifier does not match the baseRequestURI path of the store.
|
||||||
|
*
|
||||||
|
* @returns A string representing the relative path.
|
||||||
|
*/
|
||||||
|
export const getRelativePath = (baseRequestURI: string, identifier: ResourceIdentifier, logger: Logger): 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 UnsupportedHttpError}
|
||||||
|
* If the relative path is invalid.
|
||||||
|
*
|
||||||
|
* @param path - A relative path, as generated by {@link getRelativePath}.
|
||||||
|
* @param identifier - A resource identifier.
|
||||||
|
* @param logger - A logger instance.
|
||||||
|
*/
|
||||||
|
export const validateRelativePath = (path: string, identifier: ResourceIdentifier, logger: Logger): void => {
|
||||||
|
if (!path.startsWith('/')) {
|
||||||
|
logger.warn(`URL ${identifier.path} needs a / after the base`);
|
||||||
|
throw new UnsupportedHttpError('URL needs a / after the base');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.includes('/..')) {
|
||||||
|
logger.warn(`Disallowed /.. segment in URL ${identifier.path}.`);
|
||||||
|
throw new UnsupportedHttpError('Disallowed /.. segment in URL');
|
||||||
|
}
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user