mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Integrate acl with rest of server
This commit is contained in:
parent
0545ca121e
commit
769b49293c
@ -1,3 +1,6 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { DATA_TYPE_BINARY } from '../src/util/ContentTypes';
|
||||||
|
import streamifyArray from 'streamify-array';
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import {
|
import {
|
||||||
AcceptPreferenceParser,
|
AcceptPreferenceParser,
|
||||||
@ -9,14 +12,16 @@ import {
|
|||||||
QuadToTurtleConverter,
|
QuadToTurtleConverter,
|
||||||
Representation,
|
Representation,
|
||||||
RepresentationConvertingStore,
|
RepresentationConvertingStore,
|
||||||
SimpleAuthorizer,
|
SimpleAclAuthorizer,
|
||||||
SimpleBodyParser,
|
SimpleBodyParser,
|
||||||
SimpleCredentialsExtractor,
|
SimpleCredentialsExtractor,
|
||||||
SimpleDeleteOperationHandler,
|
SimpleDeleteOperationHandler,
|
||||||
|
SimpleExtensionAclManager,
|
||||||
SimpleGetOperationHandler,
|
SimpleGetOperationHandler,
|
||||||
SimplePatchOperationHandler,
|
SimplePatchOperationHandler,
|
||||||
SimplePermissionsExtractor,
|
SimplePermissionsExtractor,
|
||||||
SimplePostOperationHandler,
|
SimplePostOperationHandler,
|
||||||
|
SimplePutOperationHandler,
|
||||||
SimpleRequestParser,
|
SimpleRequestParser,
|
||||||
SimpleResourceStore,
|
SimpleResourceStore,
|
||||||
SimpleResponseWriter,
|
SimpleResponseWriter,
|
||||||
@ -25,6 +30,7 @@ import {
|
|||||||
SimpleTargetExtractor,
|
SimpleTargetExtractor,
|
||||||
SingleThreadedResourceLocker,
|
SingleThreadedResourceLocker,
|
||||||
TurtleToQuadConverter,
|
TurtleToQuadConverter,
|
||||||
|
UrlContainerManager,
|
||||||
} from '..';
|
} from '..';
|
||||||
|
|
||||||
const { argv } = yargs
|
const { argv } = yargs
|
||||||
@ -36,6 +42,8 @@ const { argv } = yargs
|
|||||||
|
|
||||||
const { port } = argv;
|
const { port } = argv;
|
||||||
|
|
||||||
|
const base = `http://localhost:${port}/`;
|
||||||
|
|
||||||
// This is instead of the dependency injection that still needs to be added
|
// This is instead of the dependency injection that still needs to be added
|
||||||
const bodyParser = new CompositeAsyncHandler<HttpRequest, Representation | undefined>([
|
const bodyParser = new CompositeAsyncHandler<HttpRequest, Representation | undefined>([
|
||||||
new SimpleSparqlUpdateBodyParser(),
|
new SimpleSparqlUpdateBodyParser(),
|
||||||
@ -49,10 +57,9 @@ const requestParser = new SimpleRequestParser({
|
|||||||
|
|
||||||
const credentialsExtractor = new SimpleCredentialsExtractor();
|
const credentialsExtractor = new SimpleCredentialsExtractor();
|
||||||
const permissionsExtractor = new SimplePermissionsExtractor();
|
const permissionsExtractor = new SimplePermissionsExtractor();
|
||||||
const authorizer = new SimpleAuthorizer();
|
|
||||||
|
|
||||||
// Will have to see how to best handle this
|
// Will have to see how to best handle this
|
||||||
const store = new SimpleResourceStore(`http://localhost:${port}/`);
|
const store = new SimpleResourceStore(base);
|
||||||
const converter = new CompositeAsyncHandler([
|
const converter = new CompositeAsyncHandler([
|
||||||
new TurtleToQuadConverter(),
|
new TurtleToQuadConverter(),
|
||||||
new QuadToTurtleConverter(),
|
new QuadToTurtleConverter(),
|
||||||
@ -62,11 +69,16 @@ const locker = new SingleThreadedResourceLocker();
|
|||||||
const patcher = new SimpleSparqlUpdatePatchHandler(convertingStore, locker);
|
const patcher = new SimpleSparqlUpdatePatchHandler(convertingStore, locker);
|
||||||
const patchingStore = new PatchingStore(convertingStore, patcher);
|
const patchingStore = new PatchingStore(convertingStore, patcher);
|
||||||
|
|
||||||
|
const aclManager = new SimpleExtensionAclManager();
|
||||||
|
const containerManager = new UrlContainerManager(base);
|
||||||
|
const authorizer = new SimpleAclAuthorizer(aclManager, containerManager, patchingStore);
|
||||||
|
|
||||||
const operationHandler = new CompositeAsyncHandler([
|
const operationHandler = new CompositeAsyncHandler([
|
||||||
new SimpleDeleteOperationHandler(patchingStore),
|
new SimpleDeleteOperationHandler(patchingStore),
|
||||||
new SimpleGetOperationHandler(patchingStore),
|
new SimpleGetOperationHandler(patchingStore),
|
||||||
new SimplePatchOperationHandler(patchingStore),
|
new SimplePatchOperationHandler(patchingStore),
|
||||||
new SimplePostOperationHandler(patchingStore),
|
new SimplePostOperationHandler(patchingStore),
|
||||||
|
new SimplePutOperationHandler(patchingStore),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const responseWriter = new SimpleResponseWriter();
|
const responseWriter = new SimpleResponseWriter();
|
||||||
@ -82,6 +94,40 @@ const httpHandler = new AuthenticatedLdpHandler({
|
|||||||
|
|
||||||
const httpServer = new ExpressHttpServer(httpHandler);
|
const httpServer = new ExpressHttpServer(httpHandler);
|
||||||
|
|
||||||
httpServer.listen(port);
|
// Set up acl so everything can still be done by default
|
||||||
|
// Note that this will need to be adapted to go through all the correct channels later on
|
||||||
|
const aclSetup = async(): Promise<void> => {
|
||||||
|
const acl = `@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
||||||
|
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
|
||||||
|
|
||||||
process.stdout.write(`Running at http://localhost:${port}/\n`);
|
<#authorization>
|
||||||
|
a acl:Authorization;
|
||||||
|
acl:agentClass foaf:Agent;
|
||||||
|
acl:mode acl:Read;
|
||||||
|
acl:mode acl:Write;
|
||||||
|
acl:mode acl:Append;
|
||||||
|
acl:mode acl:Delete;
|
||||||
|
acl:mode acl:Control;
|
||||||
|
acl:accessTo <${base}>;
|
||||||
|
acl:default <${base}>.`;
|
||||||
|
await store.setRepresentation(
|
||||||
|
await aclManager.getAcl({ path: base }),
|
||||||
|
{
|
||||||
|
dataType: DATA_TYPE_BINARY,
|
||||||
|
data: streamifyArray([ acl ]),
|
||||||
|
metadata: {
|
||||||
|
raw: [],
|
||||||
|
profiles: [],
|
||||||
|
contentType: 'text/turtle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
aclSetup().then((): void => {
|
||||||
|
httpServer.listen(port);
|
||||||
|
|
||||||
|
process.stdout.write(`Running at ${base}\n`);
|
||||||
|
}).catch((error): void => {
|
||||||
|
process.stderr.write(`${error}\n`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
8
index.ts
8
index.ts
@ -4,8 +4,11 @@ export * from './src/authentication/CredentialsExtractor';
|
|||||||
export * from './src/authentication/SimpleCredentialsExtractor';
|
export * from './src/authentication/SimpleCredentialsExtractor';
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
|
export * from './src/authorization/AclManager';
|
||||||
export * from './src/authorization/Authorizer';
|
export * from './src/authorization/Authorizer';
|
||||||
|
export * from './src/authorization/SimpleAclAuthorizer';
|
||||||
export * from './src/authorization/SimpleAuthorizer';
|
export * from './src/authorization/SimpleAuthorizer';
|
||||||
|
export * from './src/authorization/SimpleExtensionAclManager';
|
||||||
|
|
||||||
// LDP/HTTP
|
// LDP/HTTP
|
||||||
export * from './src/ldp/http/AcceptPreferenceParser';
|
export * from './src/ldp/http/AcceptPreferenceParser';
|
||||||
@ -30,6 +33,7 @@ export * from './src/ldp/operations/SimpleDeleteOperationHandler';
|
|||||||
export * from './src/ldp/operations/SimpleGetOperationHandler';
|
export * from './src/ldp/operations/SimpleGetOperationHandler';
|
||||||
export * from './src/ldp/operations/SimplePatchOperationHandler';
|
export * from './src/ldp/operations/SimplePatchOperationHandler';
|
||||||
export * from './src/ldp/operations/SimplePostOperationHandler';
|
export * from './src/ldp/operations/SimplePostOperationHandler';
|
||||||
|
export * from './src/ldp/operations/SimplePutOperationHandler';
|
||||||
|
|
||||||
// LDP/Permissions
|
// LDP/Permissions
|
||||||
export * from './src/ldp/permissions/PermissionSet';
|
export * from './src/ldp/permissions/PermissionSet';
|
||||||
@ -67,6 +71,7 @@ export * from './src/storage/patch/SimpleSparqlUpdatePatchHandler';
|
|||||||
// Storage
|
// Storage
|
||||||
export * from './src/storage/AtomicResourceStore';
|
export * from './src/storage/AtomicResourceStore';
|
||||||
export * from './src/storage/Conditions';
|
export * from './src/storage/Conditions';
|
||||||
|
export * from './src/storage/ContainerManager';
|
||||||
export * from './src/storage/Lock';
|
export * from './src/storage/Lock';
|
||||||
export * from './src/storage/LockingResourceStore';
|
export * from './src/storage/LockingResourceStore';
|
||||||
export * from './src/storage/PassthroughStore';
|
export * from './src/storage/PassthroughStore';
|
||||||
@ -77,10 +82,13 @@ export * from './src/storage/ResourceMapper';
|
|||||||
export * from './src/storage/ResourceStore';
|
export * from './src/storage/ResourceStore';
|
||||||
export * from './src/storage/SingleThreadedResourceLocker';
|
export * from './src/storage/SingleThreadedResourceLocker';
|
||||||
export * from './src/storage/SimpleResourceStore';
|
export * from './src/storage/SimpleResourceStore';
|
||||||
|
export * from './src/storage/UrlContainerManager';
|
||||||
|
|
||||||
// Util/Errors
|
// Util/Errors
|
||||||
|
export * from './src/util/errors/ForbiddenHttpError';
|
||||||
export * from './src/util/errors/HttpError';
|
export * from './src/util/errors/HttpError';
|
||||||
export * from './src/util/errors/NotFoundHttpError';
|
export * from './src/util/errors/NotFoundHttpError';
|
||||||
|
export * from './src/util/errors/UnauthorizedHttpError';
|
||||||
export * from './src/util/errors/UnsupportedHttpError';
|
export * from './src/util/errors/UnsupportedHttpError';
|
||||||
export * from './src/util/errors/UnsupportedMediaTypeHttpError';
|
export * from './src/util/errors/UnsupportedMediaTypeHttpError';
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
describe('A basic test', (): void => {
|
|
||||||
it('to have something pass.', async(): Promise<void> => {
|
|
||||||
expect(true).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,11 +1,10 @@
|
|||||||
import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser';
|
import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser';
|
||||||
import { AuthenticatedLdpHandler } from '../../src/ldp/AuthenticatedLdpHandler';
|
import { AuthenticatedLdpHandler } from '../../src/ldp/AuthenticatedLdpHandler';
|
||||||
import { BodyParser } from '../../src/ldp/http/BodyParser';
|
import { BodyParser } from '../../src/ldp/http/BodyParser';
|
||||||
|
import { call } from '../util/Util';
|
||||||
import { CompositeAsyncHandler } from '../../src/util/CompositeAsyncHandler';
|
import { CompositeAsyncHandler } from '../../src/util/CompositeAsyncHandler';
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import { HttpHandler } from '../../src/server/HttpHandler';
|
|
||||||
import { HttpRequest } from '../../src/server/HttpRequest';
|
import { HttpRequest } from '../../src/server/HttpRequest';
|
||||||
import { IncomingHttpHeaders } from 'http';
|
import { MockResponse } from 'node-mocks-http';
|
||||||
import { Operation } from '../../src/ldp/operations/Operation';
|
import { Operation } from '../../src/ldp/operations/Operation';
|
||||||
import { Parser } from 'n3';
|
import { Parser } from 'n3';
|
||||||
import { PatchingStore } from '../../src/storage/PatchingStore';
|
import { PatchingStore } from '../../src/storage/PatchingStore';
|
||||||
@ -28,35 +27,11 @@ import { SimpleSparqlUpdateBodyParser } from '../../src/ldp/http/SimpleSparqlUpd
|
|||||||
import { SimpleSparqlUpdatePatchHandler } from '../../src/storage/patch/SimpleSparqlUpdatePatchHandler';
|
import { SimpleSparqlUpdatePatchHandler } from '../../src/storage/patch/SimpleSparqlUpdatePatchHandler';
|
||||||
import { SimpleTargetExtractor } from '../../src/ldp/http/SimpleTargetExtractor';
|
import { SimpleTargetExtractor } from '../../src/ldp/http/SimpleTargetExtractor';
|
||||||
import { SingleThreadedResourceLocker } from '../../src/storage/SingleThreadedResourceLocker';
|
import { SingleThreadedResourceLocker } from '../../src/storage/SingleThreadedResourceLocker';
|
||||||
import streamifyArray from 'streamify-array';
|
|
||||||
import { TurtleToQuadConverter } from '../../src/storage/conversion/TurtleToQuadConverter';
|
import { TurtleToQuadConverter } from '../../src/storage/conversion/TurtleToQuadConverter';
|
||||||
import { createResponse, MockResponse } from 'node-mocks-http';
|
|
||||||
import { namedNode, quad } from '@rdfjs/data-model';
|
import { namedNode, quad } from '@rdfjs/data-model';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
|
||||||
const call = async(handler: HttpHandler, requestUrl: url.URL, method: string,
|
describe('An integrated AuthenticatedLdpHandler', (): void => {
|
||||||
headers: IncomingHttpHeaders, data: string[]): Promise<MockResponse<any>> => {
|
|
||||||
const request = streamifyArray(data) as HttpRequest;
|
|
||||||
request.url = requestUrl.pathname;
|
|
||||||
request.method = method;
|
|
||||||
request.headers = headers;
|
|
||||||
request.headers.host = requestUrl.host;
|
|
||||||
const response: MockResponse<any> = createResponse({ eventEmitter: EventEmitter });
|
|
||||||
|
|
||||||
const endPromise = new Promise((resolve): void => {
|
|
||||||
response.on('end', (): void => {
|
|
||||||
expect(response._isEndCalled()).toBeTruthy();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await handler.handleSafe({ request, response });
|
|
||||||
await endPromise;
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('An AuthenticatedLdpHandler', (): void => {
|
|
||||||
describe('with simple handlers', (): void => {
|
describe('with simple handlers', (): void => {
|
||||||
const requestParser = new SimpleRequestParser({
|
const requestParser = new SimpleRequestParser({
|
||||||
targetExtractor: new SimpleTargetExtractor(),
|
targetExtractor: new SimpleTargetExtractor(),
|
||||||
@ -88,7 +63,7 @@ describe('An AuthenticatedLdpHandler', (): void => {
|
|||||||
|
|
||||||
it('can add, read and delete data based on incoming requests.', async(): Promise<void> => {
|
it('can add, read and delete data based on incoming requests.', async(): Promise<void> => {
|
||||||
// POST
|
// POST
|
||||||
let requestUrl = new url.URL('http://test.com/');
|
let requestUrl = new URL('http://test.com/');
|
||||||
let response: MockResponse<any> = await call(
|
let response: MockResponse<any> = await call(
|
||||||
handler,
|
handler,
|
||||||
requestUrl,
|
requestUrl,
|
||||||
@ -102,7 +77,7 @@ describe('An AuthenticatedLdpHandler', (): void => {
|
|||||||
expect(id).toContain(url.format(requestUrl));
|
expect(id).toContain(url.format(requestUrl));
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
requestUrl = new url.URL(id);
|
requestUrl = new URL(id);
|
||||||
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
||||||
expect(response.statusCode).toBe(200);
|
expect(response.statusCode).toBe(200);
|
||||||
expect(response._getData()).toContain('<http://test.com/s> <http://test.com/p> <http://test.com/o>.');
|
expect(response._getData()).toContain('<http://test.com/s> <http://test.com/p> <http://test.com/o>.');
|
||||||
@ -166,7 +141,7 @@ describe('An AuthenticatedLdpHandler', (): void => {
|
|||||||
|
|
||||||
it('can handle simple SPARQL updates.', async(): Promise<void> => {
|
it('can handle simple SPARQL updates.', async(): Promise<void> => {
|
||||||
// POST
|
// POST
|
||||||
let requestUrl = new url.URL('http://test.com/');
|
let requestUrl = new URL('http://test.com/');
|
||||||
let response: MockResponse<any> = await call(
|
let response: MockResponse<any> = await call(
|
||||||
handler,
|
handler,
|
||||||
requestUrl,
|
requestUrl,
|
||||||
@ -181,7 +156,7 @@ describe('An AuthenticatedLdpHandler', (): void => {
|
|||||||
expect(id).toContain(url.format(requestUrl));
|
expect(id).toContain(url.format(requestUrl));
|
||||||
|
|
||||||
// PATCH
|
// PATCH
|
||||||
requestUrl = new url.URL(id);
|
requestUrl = new URL(id);
|
||||||
response = await call(
|
response = await call(
|
||||||
handler,
|
handler,
|
||||||
requestUrl,
|
requestUrl,
|
||||||
@ -196,7 +171,7 @@ describe('An AuthenticatedLdpHandler', (): void => {
|
|||||||
expect(response._getHeaders().location).toBe(id);
|
expect(response._getHeaders().location).toBe(id);
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
requestUrl = new url.URL(id);
|
requestUrl = new URL(id);
|
||||||
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
||||||
expect(response.statusCode).toBe(200);
|
expect(response.statusCode).toBe(200);
|
||||||
expect(response._getData()).toContain('<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.');
|
expect(response._getData()).toContain('<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.');
|
||||||
|
181
test/integration/Authorization.test.ts
Normal file
181
test/integration/Authorization.test.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser';
|
||||||
|
import { AuthenticatedLdpHandler } from '../../src/ldp/AuthenticatedLdpHandler';
|
||||||
|
import { BodyParser } from '../../src/ldp/http/BodyParser';
|
||||||
|
import { call } from '../util/Util';
|
||||||
|
import { CompositeAsyncHandler } from '../../src/util/CompositeAsyncHandler';
|
||||||
|
import { DATA_TYPE_BINARY } from '../../src/util/ContentTypes';
|
||||||
|
import { MockResponse } from 'node-mocks-http';
|
||||||
|
import { Operation } from '../../src/ldp/operations/Operation';
|
||||||
|
import { PermissionSet } from '../../src/ldp/permissions/PermissionSet';
|
||||||
|
import { QuadToTurtleConverter } from '../../src/storage/conversion/QuadToTurtleConverter';
|
||||||
|
import { RepresentationConvertingStore } from '../../src/storage/RepresentationConvertingStore';
|
||||||
|
import { ResourceStore } from '../../src/storage/ResourceStore';
|
||||||
|
import { ResponseDescription } from '../../src/ldp/operations/ResponseDescription';
|
||||||
|
import { SimpleAclAuthorizer } from '../../src/authorization/SimpleAclAuthorizer';
|
||||||
|
import { SimpleBodyParser } from '../../src/ldp/http/SimpleBodyParser';
|
||||||
|
import { SimpleCredentialsExtractor } from '../../src/authentication/SimpleCredentialsExtractor';
|
||||||
|
import { SimpleDeleteOperationHandler } from '../../src/ldp/operations/SimpleDeleteOperationHandler';
|
||||||
|
import { SimpleExtensionAclManager } from '../../src/authorization/SimpleExtensionAclManager';
|
||||||
|
import { SimpleGetOperationHandler } from '../../src/ldp/operations/SimpleGetOperationHandler';
|
||||||
|
import { SimplePermissionsExtractor } from '../../src/ldp/permissions/SimplePermissionsExtractor';
|
||||||
|
import { SimplePostOperationHandler } from '../../src/ldp/operations/SimplePostOperationHandler';
|
||||||
|
import { SimplePutOperationHandler } from '../../src/ldp/operations/SimplePutOperationHandler';
|
||||||
|
import { SimpleRequestParser } from '../../src/ldp/http/SimpleRequestParser';
|
||||||
|
import { SimpleResourceStore } from '../../src/storage/SimpleResourceStore';
|
||||||
|
import { SimpleResponseWriter } from '../../src/ldp/http/SimpleResponseWriter';
|
||||||
|
import { SimpleTargetExtractor } from '../../src/ldp/http/SimpleTargetExtractor';
|
||||||
|
import streamifyArray from 'streamify-array';
|
||||||
|
import { TurtleToQuadConverter } from '../../src/storage/conversion/TurtleToQuadConverter';
|
||||||
|
import { UrlContainerManager } from '../../src/storage/UrlContainerManager';
|
||||||
|
|
||||||
|
const setAcl = async(store: ResourceStore, id: string, permissions: PermissionSet, control: boolean,
|
||||||
|
access: boolean, def: boolean, agent?: string, agentClass?: 'agent' | 'authenticated'): Promise<void> => {
|
||||||
|
const acl: string[] = [
|
||||||
|
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.\n',
|
||||||
|
'@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n',
|
||||||
|
'<http://test.com/#auth> a acl:Authorization',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const perm of [ 'Read', 'Append', 'Write', 'Delete' ]) {
|
||||||
|
if (permissions[perm.toLowerCase() as keyof PermissionSet]) {
|
||||||
|
acl.push(`;\n acl:mode acl:${perm}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (control) {
|
||||||
|
acl.push(';\n acl:mode acl:Control');
|
||||||
|
}
|
||||||
|
if (access) {
|
||||||
|
acl.push(`;\n acl:accessTo <${id}>`);
|
||||||
|
}
|
||||||
|
if (def) {
|
||||||
|
acl.push(`;\n acl:default <${id}>`);
|
||||||
|
}
|
||||||
|
if (agent) {
|
||||||
|
acl.push(`;\n acl:agent <${agent}>`);
|
||||||
|
}
|
||||||
|
if (agentClass) {
|
||||||
|
acl.push(`;\n acl:agentClass ${agentClass === 'agent' ? 'foaf:Agent' : 'foaf:AuthenticatedAgent'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
acl.push('.');
|
||||||
|
|
||||||
|
const representation = {
|
||||||
|
data: streamifyArray(acl),
|
||||||
|
dataType: DATA_TYPE_BINARY,
|
||||||
|
metadata: {
|
||||||
|
raw: [],
|
||||||
|
profiles: [],
|
||||||
|
contentType: 'text/turtle',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return store.setRepresentation({ path: `${id}.acl` }, representation);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('A server with authorization', (): void => {
|
||||||
|
const bodyParser: BodyParser = new SimpleBodyParser();
|
||||||
|
const requestParser = new SimpleRequestParser({
|
||||||
|
targetExtractor: new SimpleTargetExtractor(),
|
||||||
|
preferenceParser: new AcceptPreferenceParser(),
|
||||||
|
bodyParser,
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = new SimpleResourceStore('http://test.com/');
|
||||||
|
const converter = new CompositeAsyncHandler([
|
||||||
|
new QuadToTurtleConverter(),
|
||||||
|
new TurtleToQuadConverter(),
|
||||||
|
]);
|
||||||
|
const convertingStore = new RepresentationConvertingStore(store, converter);
|
||||||
|
|
||||||
|
const credentialsExtractor = new SimpleCredentialsExtractor();
|
||||||
|
const permissionsExtractor = new SimplePermissionsExtractor();
|
||||||
|
const authorizer = new SimpleAclAuthorizer(
|
||||||
|
new SimpleExtensionAclManager(),
|
||||||
|
new UrlContainerManager('http://test.com/'),
|
||||||
|
convertingStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const operationHandler = new CompositeAsyncHandler<Operation, ResponseDescription>([
|
||||||
|
new SimpleGetOperationHandler(convertingStore),
|
||||||
|
new SimplePostOperationHandler(convertingStore),
|
||||||
|
new SimpleDeleteOperationHandler(convertingStore),
|
||||||
|
new SimplePutOperationHandler(convertingStore),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const responseWriter = new SimpleResponseWriter();
|
||||||
|
|
||||||
|
const handler = new AuthenticatedLdpHandler({
|
||||||
|
requestParser,
|
||||||
|
credentialsExtractor,
|
||||||
|
permissionsExtractor,
|
||||||
|
authorizer,
|
||||||
|
operationHandler,
|
||||||
|
responseWriter,
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can create new entries.', async(): Promise<void> => {
|
||||||
|
await setAcl(convertingStore,
|
||||||
|
'http://test.com/',
|
||||||
|
{ read: true, write: true, append: true },
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
'agent');
|
||||||
|
|
||||||
|
// POST
|
||||||
|
let requestUrl = new URL('http://test.com/');
|
||||||
|
let response: MockResponse<any> = await call(
|
||||||
|
handler,
|
||||||
|
requestUrl,
|
||||||
|
'POST',
|
||||||
|
{ 'content-type': 'text/turtle' },
|
||||||
|
[ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
|
||||||
|
);
|
||||||
|
expect(response.statusCode).toBe(200);
|
||||||
|
|
||||||
|
// PUT
|
||||||
|
requestUrl = new URL('http://test.com/foo/bar');
|
||||||
|
response = await call(
|
||||||
|
handler,
|
||||||
|
requestUrl,
|
||||||
|
'PUT',
|
||||||
|
{ 'content-type': 'text/turtle' },
|
||||||
|
[ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
|
||||||
|
);
|
||||||
|
expect(response.statusCode).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can not create new entries if not allowed.', async(): Promise<void> => {
|
||||||
|
await setAcl(convertingStore,
|
||||||
|
'http://test.com/',
|
||||||
|
{ read: true, write: true, append: true },
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
'authenticated');
|
||||||
|
|
||||||
|
// POST
|
||||||
|
let requestUrl = new URL('http://test.com/');
|
||||||
|
let response: MockResponse<any> = await call(
|
||||||
|
handler,
|
||||||
|
requestUrl,
|
||||||
|
'POST',
|
||||||
|
{ 'content-type': 'text/turtle' },
|
||||||
|
[ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
|
||||||
|
);
|
||||||
|
expect(response.statusCode).toBe(401);
|
||||||
|
|
||||||
|
// PUT
|
||||||
|
requestUrl = new URL('http://test.com/foo/bar');
|
||||||
|
response = await call(
|
||||||
|
handler,
|
||||||
|
requestUrl,
|
||||||
|
'PUT',
|
||||||
|
{ 'content-type': 'text/turtle' },
|
||||||
|
[ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
|
||||||
|
);
|
||||||
|
expect(response.statusCode).toBe(401);
|
||||||
|
});
|
||||||
|
});
|
@ -81,6 +81,10 @@ describe('ExpressHttpServer', (): void => {
|
|||||||
handler.handle = async(): Promise<void> => {
|
handler.handle = async(): Promise<void> => {
|
||||||
throw new Error('dummyError');
|
throw new Error('dummyError');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Prevent test from writing to stderr
|
||||||
|
jest.spyOn(process.stderr, 'write').mockImplementation((): boolean => true);
|
||||||
|
|
||||||
const res = await request(server).get('/').expect(500);
|
const res = await request(server).get('/').expect(500);
|
||||||
expect(res.text).toContain('dummyError');
|
expect(res.text).toContain('dummyError');
|
||||||
});
|
});
|
||||||
|
28
test/util/Util.ts
Normal file
28
test/util/Util.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { HttpHandler } from '../../src/server/HttpHandler';
|
||||||
|
import { HttpRequest } from '../../src/server/HttpRequest';
|
||||||
|
import { IncomingHttpHeaders } from 'http';
|
||||||
|
import streamifyArray from 'streamify-array';
|
||||||
|
import { createResponse, MockResponse } from 'node-mocks-http';
|
||||||
|
|
||||||
|
export const call = async(handler: HttpHandler, requestUrl: URL, method: string,
|
||||||
|
headers: IncomingHttpHeaders, data: string[]): Promise<MockResponse<any>> => {
|
||||||
|
const request = streamifyArray(data) as HttpRequest;
|
||||||
|
request.url = requestUrl.pathname;
|
||||||
|
request.method = method;
|
||||||
|
request.headers = headers;
|
||||||
|
request.headers.host = requestUrl.host;
|
||||||
|
const response: MockResponse<any> = createResponse({ eventEmitter: EventEmitter });
|
||||||
|
|
||||||
|
const endPromise = new Promise((resolve): void => {
|
||||||
|
response.on('end', (): void => {
|
||||||
|
expect(response._isEndCalled()).toBeTruthy();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await handler.handleSafe({ request, response });
|
||||||
|
await endPromise;
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user