diff --git a/config/presets/init.json b/config/presets/init.json index 901aaa480..9743df183 100644 --- a/config/presets/init.json +++ b/config/presets/init.json @@ -6,12 +6,14 @@ "@type": "SequenceHandler", "SequenceHandler:_handlers": [ { + "@id": "urn:solid-server:default:LoggerInitializer", "@type": "LoggerInitializer", "LoggerInitializer:_loggerFactory": { "@id": "urn:solid-server:default:LoggerFactory" } }, { + "@id": "urn:solid-server:default:RootContainerInitializer", "@type": "RootContainerInitializer", "RootContainerInitializer:_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" @@ -21,6 +23,7 @@ } }, { + "@id": "urn:solid-server:default:AclInitializer", "@type": "AclInitializer", "AclInitializer:_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" @@ -33,6 +36,7 @@ } }, { + "@id": "urn:solid-server:default:ServerInitializer", "@type": "ServerInitializer", "ServerInitializer:_serverFactory": { "@id": "urn:solid-server:default:ServerFactory" diff --git a/src/authorization/WebAclAuthorizer.ts b/src/authorization/WebAclAuthorizer.ts index 0e4b2b102..9e8e3ae22 100644 --- a/src/authorization/WebAclAuthorizer.ts +++ b/src/authorization/WebAclAuthorizer.ts @@ -131,7 +131,7 @@ export class WebAclAuthorizer extends Authorizer { if (error instanceof NotFoundHttpError) { this.logger.debug(`No direct ACL document found for ${id.path}`); } else { - this.logger.error(`Error reading ACL for ${id.path}`, { error }); + this.logger.error(`Error reading ACL for ${id.path}: ${(error as Error).message}`, { error }); throw error; } } diff --git a/src/init/AclInitializer.ts b/src/init/AclInitializer.ts index 69cc24da6..0ad6accc4 100644 --- a/src/init/AclInitializer.ts +++ b/src/init/AclInitializer.ts @@ -5,6 +5,7 @@ import { getLoggerFor } from '../logging/LogUtil'; import type { ResourceStore } from '../storage/ResourceStore'; import { TEXT_TURTLE } from '../util/ContentTypes'; import { NotFoundHttpError } from '../util/errors/NotFoundHttpError'; +import { ensureTrailingSlash } from '../util/PathUtil'; import { guardedStreamFrom } from '../util/StreamUtil'; import { CONTENT_TYPE } from '../util/UriConstants'; import { Initializer } from './Initializer'; @@ -24,7 +25,7 @@ export class AclInitializer extends Initializer { aclManager: AclManager, ) { super(); - this.baseUrl = baseUrl; + this.baseUrl = ensureTrailingSlash(baseUrl); this.store = store; this.aclManager = aclManager; } diff --git a/src/ldp/representation/RepresentationMetadata.ts b/src/ldp/representation/RepresentationMetadata.ts index bb6d8a529..4cbe46f66 100644 --- a/src/ldp/representation/RepresentationMetadata.ts +++ b/src/ldp/representation/RepresentationMetadata.ts @@ -8,6 +8,13 @@ import { isResourceIdentifier } from './ResourceIdentifier'; export type MetadataIdentifier = ResourceIdentifier | NamedNode | BlankNode; export type MetadataOverrideValue = NamedNode | Literal | string | (NamedNode | Literal | string)[]; +/** + * Determines whether the object is a `RepresentationMetadata`. + */ +export const isRepresentationMetadata = function(object: any): object is RepresentationMetadata { + return typeof object?.setMetadata === 'function'; +}; + /** * Stores the metadata triples and provides methods for easy access. * Most functions return the metadata object to allow for chaining. @@ -49,7 +56,7 @@ export class RepresentationMetadata { this.id = DataFactory.namedNode(input.path); } else if (isTerm(input)) { this.id = input; - } else if (input instanceof RepresentationMetadata) { + } else if (isRepresentationMetadata(input)) { this.id = input.identifier; this.addQuads(input.quads()); } else { diff --git a/src/util/identifiers/SingleRootIdentifierStrategy.ts b/src/util/identifiers/SingleRootIdentifierStrategy.ts index bb16c2e01..d5625cd07 100644 --- a/src/util/identifiers/SingleRootIdentifierStrategy.ts +++ b/src/util/identifiers/SingleRootIdentifierStrategy.ts @@ -1,4 +1,5 @@ import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier'; +import { getLoggerFor } from '../../logging/LogUtil'; import { InternalServerError } from '../errors/InternalServerError'; import { ensureTrailingSlash } from '../PathUtil'; import type { IdentifierStrategy } from './IdentifierStrategy'; @@ -8,13 +9,18 @@ import type { IdentifierStrategy } from './IdentifierStrategy'; */ export class SingleRootIdentifierStrategy implements IdentifierStrategy { private readonly baseUrl: string; + protected readonly logger = getLoggerFor(this); public constructor(baseUrl: string) { this.baseUrl = ensureTrailingSlash(baseUrl); } public supportsIdentifier(identifier: ResourceIdentifier): boolean { - return identifier.path.startsWith(this.baseUrl); + const supported = identifier.path.startsWith(this.baseUrl); + this.logger.debug(supported ? + `Identifier ${identifier.path} is part of ${this.baseUrl}` : + `Identifier ${identifier.path} is not part of ${this.baseUrl}`); + return supported; } public getParentContainer(identifier: ResourceIdentifier): ResourceIdentifier { diff --git a/test/configs/BasicHandlersWithAclConfig.ts b/test/configs/BasicHandlersWithAclConfig.ts deleted file mode 100644 index 561f873f9..000000000 --- a/test/configs/BasicHandlersWithAclConfig.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { - HttpHandler, - ResourceStore, -} from '../../src/index'; -import { - AuthenticatedLdpHandler, - EmptyCredentialsExtractor, - MethodPermissionsExtractor, - RdfToQuadConverter, - QuadToRdfConverter, - WaterfallHandler, -} from '../../src/index'; -import type { ServerConfig } from './ServerConfig'; -import { - getInMemoryResourceStore, - getConvertingStore, - getBasicRequestParser, - getOperationHandler, - getWebAclAuthorizer, - getResponseWriter, -} from './Util'; - -/** - * BasicHandlersWithAclConfig works with - * - an WebAclAuthorizer - * - an InMemoryResourceStore wrapped in a converting store & wrapped in a patching store - * - GET, POST, PUT, PATCH & DELETE operation handlers - */ - -export class BasicHandlersWithAclConfig implements ServerConfig { - public store: ResourceStore; - - public constructor() { - this.store = getConvertingStore( - getInMemoryResourceStore(), - [ new QuadToRdfConverter(), - new RdfToQuadConverter() ], - ); - } - - public getHttpHandler(): HttpHandler { - const requestParser = getBasicRequestParser(); - - const credentialsExtractor = new EmptyCredentialsExtractor(); - const permissionsExtractor = new WaterfallHandler([ - new MethodPermissionsExtractor(), - ]); - - const operationHandler = getOperationHandler(this.store); - - const responseWriter = getResponseWriter(); - const authorizer = getWebAclAuthorizer(this.store); - - const handler = new AuthenticatedLdpHandler({ - requestParser, - credentialsExtractor, - permissionsExtractor, - authorizer, - operationHandler, - responseWriter, - }); - - return handler; - } -} diff --git a/test/configs/Util.ts b/test/configs/Util.ts index a39f87fe1..ba17b5eae 100644 --- a/test/configs/Util.ts +++ b/test/configs/Util.ts @@ -24,7 +24,6 @@ import { ErrorResponseWriter, GetOperationHandler, HeadOperationHandler, - InMemoryDataAccessor, LinkRelMetadataWriter, LinkTypeParser, MappedMetadataWriter, @@ -63,15 +62,6 @@ export const getRootFilePath = (subfolder: string): string => join(__dirname, '. export const getDataAccessorStore = (base: string, dataAccessor: DataAccessor): DataAccessorBasedStore => new DataAccessorBasedStore(dataAccessor, new SingleRootIdentifierStrategy(base)); -/** - * Gives an in memory resource store based on (default) base url. - * @param base - Optional base parameter for the run time config. - * - * @returns The in memory resource store. - */ -export const getInMemoryResourceStore = (base = BASE): DataAccessorBasedStore => - getDataAccessorStore(base, new InMemoryDataAccessor(BASE)); - /** * Gives a converting store given some converters. * @param store - Initial store. diff --git a/test/configs/auth-ldp-handler.json b/test/configs/auth-ldp-handler.json new file mode 100644 index 000000000..9a62b6202 --- /dev/null +++ b/test/configs/auth-ldp-handler.json @@ -0,0 +1,45 @@ +{ + "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld", + "import": [ + "files-scs:config/presets/acl.json", + "files-scs:config/presets/init.json", + "files-scs:config/presets/ldp.json", + "files-scs:config/presets/ldp/credentials-extractor.json", + "files-scs:config/presets/ldp/metadata-handler.json", + "files-scs:config/presets/ldp/operation-handler.json", + "files-scs:config/presets/ldp/permissions-extractor.json", + "files-scs:config/presets/ldp/response-writer.json", + "files-scs:config/presets/ldp/request-parser.json", + "files-scs:config/presets/representation-conversion.json", + "files-scs:config/presets/storage/backend/storage-memory.json", + "files-scs:config/presets/storage-wrapper.json", + "files-scs:config/presets/cli-params.json" + ], + "@graph": [ + { + "@id": "urn:solid-server:test:Instances", + "@type": "RecordObject", + "RecordObject:_record": [ + { + "RecordObject:_record_key": "initializer", + "RecordObject:_record_value": { "@id": "urn:solid-server:default:AclInitializer" } + }, + { + "RecordObject:_record_key": "handler", + "RecordObject:_record_value": { "@id": "urn:solid-server:default:LdpHandler" } + }, + { + "RecordObject:_record_key": "store", + "RecordObject:_record_value": { "@id": "urn:solid-server:default:ResourceStore" } + } + ] + }, + { + "@id": "urn:solid-server:default:RoutingResourceStore", + "@type": "PassthroughStore", + "PassthroughStore:_source": { + "@id": "urn:solid-server:default:MemoryResourceStore" + } + } + ] +} diff --git a/test/integration/Authorization.test.ts b/test/integration/Authorization.test.ts index 65019d85f..a73519266 100644 --- a/test/integration/Authorization.test.ts +++ b/test/integration/Authorization.test.ts @@ -1,20 +1,23 @@ import type { MockResponse } from 'node-mocks-http'; -import { RootContainerInitializer } from '../../src/init/RootContainerInitializer'; -import { BasicHandlersWithAclConfig } from '../configs/BasicHandlersWithAclConfig'; -import { BASE } from '../configs/Util'; +import type { HttpHandler, Initializer, ResourceStore } from '../../src/'; +import { BASE, instantiateFromConfig } from '../configs/Util'; import { AclTestHelper } from '../util/TestHelpers'; import { call } from '../util/Util'; describe('A server with authorization', (): void => { - const config = new BasicHandlersWithAclConfig(); - const handler = config.getHttpHandler(); - const { store } = config; - const aclHelper = new AclTestHelper(store, 'http://test.com/'); + let handler: HttpHandler; + let aclHelper: AclTestHelper; beforeAll(async(): Promise => { - // Initialize store - const initializer = new RootContainerInitializer(BASE, config.store); + let initializer: Initializer, + store: ResourceStore; + const instances = await instantiateFromConfig('urn:solid-server:test:Instances', 'auth-ldp-handler.json', { + 'urn:solid-server:default:variable:baseUrl': BASE, + }) as Record; + ({ handler, store, initializer } = instances); + await initializer.handleSafe(); + aclHelper = new AclTestHelper(store, BASE); }); it('can create new entries.', async(): Promise => { diff --git a/test/util/TestHelpers.ts b/test/util/TestHelpers.ts index 8f21a245e..67eb1ea94 100644 --- a/test/util/TestHelpers.ts +++ b/test/util/TestHelpers.ts @@ -6,8 +6,8 @@ import { Readable } from 'stream'; import * as url from 'url'; import type { MockResponse } from 'node-mocks-http'; import { createResponse } from 'node-mocks-http'; -import type { ResourceStore, PermissionSet, HttpHandler, HttpRequest } from '../../src/index'; -import { guardedStreamFrom, RepresentationMetadata } from '../../src/index'; +import type { ResourceStore, PermissionSet, HttpHandler, HttpRequest } from '../../src/'; +import { guardedStreamFrom, RepresentationMetadata, ensureTrailingSlash } from '../../src/'; import { CONTENT_TYPE } from '../../src/util/UriConstants'; import { call } from './Util'; @@ -18,7 +18,7 @@ export class AclTestHelper { public constructor(store: ResourceStore, id: string) { this.store = store; - this.id = id; + this.id = ensureTrailingSlash(id); } public async setSimpleAcl(