test: Use Components.js in Authorization.

This commit is contained in:
Ruben Verborgh 2020-12-21 01:15:50 +01:00
parent d1d29a3f52
commit 1cf1167261
10 changed files with 82 additions and 91 deletions

View File

@ -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"

View File

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

View File

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

View File

@ -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 {

View File

@ -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 {

View File

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

View File

@ -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.

View File

@ -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"
}
}
]
}

View File

@ -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<void> => {
// 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<string, any>;
({ handler, store, initializer } = instances);
await initializer.handleSafe();
aclHelper = new AclTestHelper(store, BASE);
});
it('can create new entries.', async(): Promise<void> => {

View File

@ -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(