Skip to content

Commit

Permalink
make sure tenant gate is used in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
finn-block committed Nov 17, 2023
1 parent aa6ae54 commit 1a4a612
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 99 deletions.
4 changes: 2 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ export const config = {
process.env.DWN_STORAGE_EVENTS || process.env.DWN_STORAGE || 'level://data',

// require POW-based registration for new tenants
powRegistration: process.env.DWN_REGISTRATION_POW == 'true',
registrationRequirementPow: process.env.DWN_REGISTRATION_POW == 'true',
tenantRegistrationStore:
process.env.DWN_STORAGE_REGISTRATION ||
process.env.DWN_STORAGE ||
'sqlite://data/dwn.db',

tos: process.env.DWN_TOS_FILE,
registrationRequirementTos: process.env.DWN_REGISTRATION_TOS,

// log level - trace/debug/info/warn/error
logLevel: process.env.DWN_SERVER_LOG_LEVEL || 'INFO',
Expand Down
8 changes: 4 additions & 4 deletions src/dwn-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ export class DwnServer {
new URL(this.config.tenantRegistrationStore),
);
const tos =
this.config.tos !== undefined
? readFileSync(this.config.tos).toString()
this.config.registrationRequirementTos !== undefined
? readFileSync(this.config.registrationRequirementTos).toString()
: null;
tenantGate = new TenantGate(
tenantGateDB,
this.config.powRegistration,
this.config.tos !== undefined,
this.config.registrationRequirementPow,
this.config.registrationRequirementTos !== undefined,
tos,
);

Expand Down
33 changes: 11 additions & 22 deletions src/http-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ const packagejson = process.env.npm_package_json
export class HttpApi {
#api: Express;
#server: http.Server;
pow?: TenantGate;
tenantGate: TenantGate;
dwn: Dwn;

constructor(dwn: Dwn, pow?: TenantGate) {
constructor(dwn: Dwn, tenantGate: TenantGate) {
this.#api = express();
this.#server = http.createServer(this.#api);
this.dwn = dwn;
this.pow = pow;
this.tenantGate = tenantGate;

this.#setupMiddleware();
this.#setupRoutes();
Expand Down Expand Up @@ -91,10 +91,6 @@ export class HttpApi {
});

this.#api.get('/:did/records/:id', async (req, res) => {
if (this.pow && !(await this.pow.isTenant(req.params.did))) {
return res.status(403).json('did not authorized on this server');
}

const record = await RecordsRead.create({
filter: { recordId: req.params.id },
});
Expand Down Expand Up @@ -156,16 +152,6 @@ export class HttpApi {
return res.status(400).json(reply);
}

if (this.pow && !(await this.pow.isTenant(dwnRpcRequest.params.target))) {
const reply = createJsonRpcErrorResponse(
dwnRpcRequest.id || uuidv4(),
JsonRpcErrorCodes.Forbidden,
'tenant not authorized, please register first',
);

return res.status(403).json(reply);
}

// Check whether data was provided in the request body
const contentLength = req.headers['content-length'];
const transferEncoding = req.headers['transfer-encoding'];
Expand Down Expand Up @@ -206,16 +192,19 @@ export class HttpApi {
}
});

if (this.pow) {
this.pow.setupRoutes(this.#api);
if (this.tenantGate) {
this.tenantGate.setupRoutes(this.#api);
}

this.#api.get('/info.json', (req, res) => {
res.setHeader('content-type', 'application/json');
const registrationRequirements: string[] = [];
if (this.pow) {
if (config.registrationRequirementPow) {
registrationRequirements.push('proof-of-work-sha256-v0');
}
if (config.registrationRequirementTos) {
registrationRequirements.push('terms-of-service');
}

res.json({
server: process.env.npm_package_name,
Expand All @@ -232,8 +221,8 @@ export class HttpApi {
}

async start(port: number, callback?: () => void): Promise<http.Server> {
if (this.pow) {
await this.pow.initialize();
if (this.tenantGate) {
await this.tenantGate.initialize();
}
this.#listen(port, callback);
return this.#server;
Expand Down
4 changes: 4 additions & 0 deletions src/tenant-gate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ export class TenantGate {
}

async isTenant(tenant: string): Promise<boolean> {
if (!this.#powRequired && !this.#tosRequired) {
return true;
}

const result = await this.#db
.selectFrom('authorizedTenants')
.select('powTime')
Expand Down
16 changes: 7 additions & 9 deletions tests/cors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { executablePath } from 'puppeteer';

import { config as defaultConfig } from '../src/config.js';
import { DwnServer } from '../src/dwn-server.js';
import { clear as clearDwn, dwn } from './test-dwn.js';
import { getTestDwn } from './test-dwn.js';

let noBrowser;
try {
Expand All @@ -24,19 +24,18 @@ class CorsProxySetup {
proxyPort = 9875;

public async start(): Promise<void> {
const testdwn = await getTestDwn();
const dwnServer = new DwnServer({
dwn: dwn,
dwn: testdwn.dwn,
config: {
...defaultConfig,
port: 0, // UNSPEC to obtain test specific free port
registrationRequirementPow: false,
},
});
const dwnPort = await new Promise((resolve) => {
dwnServer.start(() => {
const port = (dwnServer.httpServer.address() as AddressInfo).port;
resolve(port);
});
});
await dwnServer.start();
const dwnPort = (dwnServer.httpServer.address() as AddressInfo).port;

// setup proxy server
const proxy = httpProxy.createProxyServer({});
const server = http.createServer((req, res) => {
Expand Down Expand Up @@ -77,7 +76,6 @@ class CorsProxySetup {
await new Promise((resolve) => {
dwnServer.stop(resolve);
});
await clearDwn();
}
}

Expand Down
8 changes: 3 additions & 5 deletions tests/dwn-process-message.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ import { v4 as uuidv4 } from 'uuid';
import { handleDwnProcessMessage } from '../src/json-rpc-handlers/dwn/process-message.js';
import type { RequestContext } from '../src/lib/json-rpc-router.js';
import { createJsonRpcRequest } from '../src/lib/json-rpc.js';
import { clear as clearDwn, dwn } from './test-dwn.js';
import { getTestDwn } from './test-dwn.js';
import { createProfile, createRecordsWriteMessage } from './utils.js';

describe('handleDwnProcessMessage', function () {
afterEach(async function () {
await clearDwn();
});

it('returns a JSON RPC Success Response when DWN returns a 2XX status code', async function () {
const alice = await createProfile();

Expand All @@ -23,6 +19,7 @@ describe('handleDwnProcessMessage', function () {
target: alice.did,
});

const dwn = (await getTestDwn()).dwn;
const context: RequestContext = { dwn, transport: 'http', dataStream };

const { jsonRpcResponse } = await handleDwnProcessMessage(
Expand All @@ -47,6 +44,7 @@ describe('handleDwnProcessMessage', function () {
target: 'did:key:abc1234',
});

const dwn = (await getTestDwn()).dwn;
const context: RequestContext = { dwn, transport: 'http' };

const { jsonRpcResponse } = await handleDwnProcessMessage(
Expand Down
12 changes: 4 additions & 8 deletions tests/dwn-server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@ import { expect } from 'chai';

import { config } from '../src/config.js';
import { DwnServer } from '../src/dwn-server.js';
import { clear, dwn } from './test-dwn.js';
import { getTestDwn } from './test-dwn.js';

describe('DwnServer', function () {
let dwnServer: DwnServer;
const options = {
dwn: dwn,
config: config,
};
before(function () {
dwnServer = new DwnServer(options);
before(async function () {
const testdwn = await getTestDwn();
dwnServer = new DwnServer({ dwn: testdwn.dwn, config: config });
});
after(async function () {
dwnServer.stop(() => console.log('server stop'));
await clear();
});
it('should create an instance of DwnServer', function () {
expect(dwnServer).to.be.an.instanceOf(DwnServer);
Expand Down
18 changes: 8 additions & 10 deletions tests/http-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
JsonRpcErrorCodes,
} from '../src/lib/json-rpc.js';
import type { TenantGate } from '../src/tenant-gate.js';
import { clear as clearDwn, getDwn as getTestDwn } from './test-dwn.js';
import { getTestDwn } from './test-dwn.js';
import type { Profile } from './utils.js';
import {
createProfile,
Expand All @@ -49,8 +49,8 @@ describe('http api', function () {
let dwn: Dwn;

before(async function () {
config.powRegistration = true;
const testdwn = await getTestDwn();
config.registrationRequirementPow = true;
const testdwn = await getTestDwn(true);
dwn = testdwn.dwn;
tenantGate = testdwn.tenantGate;

Expand All @@ -65,7 +65,6 @@ describe('http api', function () {
afterEach(async function () {
server.close();
server.closeAllConnections();
await clearDwn();
});

describe('/register/pow', function () {
Expand Down Expand Up @@ -233,9 +232,7 @@ describe('http api', function () {
it('rejects unauthorized tenants', async function () {
const unauthorized = await createProfile();
const recordsQuery = await RecordsQuery.create({
filter: {
schema: 'woosa',
},
filter: { schema: 'woosa' },
signer: unauthorized.signer,
});

Expand All @@ -250,8 +247,9 @@ describe('http api', function () {
.set('dwn-request', JSON.stringify(dwnRequest))
.send();

expect(response.statusCode).to.equal(403);
expect(response.statusCode).to.equal(200);
expect(response.body.id).to.equal(requestId);
expect(response.body.result.reply.status.code).to.equal(401);
});
});

Expand Down Expand Up @@ -679,14 +677,14 @@ describe('http api', function () {
expect(response.status).to.equal(404);
});

it('returns a 403 for invalid or unauthorized did', async function () {
it('returns a 404 for invalid or unauthorized did', async function () {
const unauthorized = await createProfile();
const { recordsWrite } = await createRecordsWriteMessage(unauthorized);

const response = await fetch(
`http://localhost:3000/${unauthorized.did}/records/${recordsWrite.message.recordId}`,
);
expect(response.status).to.equal(403);
expect(response.status).to.equal(404);
});

it('returns a 404 for invalid record id', async function () {
Expand Down
12 changes: 3 additions & 9 deletions tests/process-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,22 @@ import sinon from 'sinon';

import { config } from '../src/config.js';
import { DwnServer } from '../src/dwn-server.js';
import { clear, dwn } from './test-dwn.js';
import { getTestDwn } from './test-dwn.js';

describe('Process Handlers', function () {
let dwnServer: DwnServer;
const options = {
dwn: dwn,
config: config,
};
let processExitStub: sinon.SinonStub;

before(async function () {
dwnServer = new DwnServer(options);
});
beforeEach(async function () {
const testdwn = await getTestDwn();
dwnServer = new DwnServer({ dwn: testdwn.dwn, config: config });
await dwnServer.start();
processExitStub = sinon.stub(process, 'exit');
});
afterEach(async function () {
process.removeAllListeners('SIGINT');
process.removeAllListeners('SIGTERM');
process.removeAllListeners('uncaughtException');
await clear();
processExitStub.restore();
});
it('should stop when SIGINT is emitted', async function () {
Expand Down
44 changes: 20 additions & 24 deletions tests/test-dwn.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
Dwn,
DataStoreLevel,
EventLogLevel,
MessageStoreLevel,
} from '@tbd54566975/dwn-sdk-js';
import { Dwn } from '@tbd54566975/dwn-sdk-js';
import {
DataStoreSql,
EventLogSql,
Expand All @@ -13,29 +8,30 @@ import {
import { getDialectFromURI } from '../src/storage.js';
import { TenantGate } from '../src/tenant-gate.js';

const dataStore = new DataStoreLevel({ blockstoreLocation: 'data/DATASTORE' });
const eventLog = new EventLogLevel({ location: 'data/EVENTLOG' });
const messageStore = new MessageStoreLevel({
blockstoreLocation: 'data/MESSAGESTORE',
indexLocation: 'data/INDEX',
});

export const dwn = await Dwn.create({ eventLog, dataStore, messageStore });

export async function clear(): Promise<void> {
await dataStore.clear();
await eventLog.clear();
await messageStore.clear();
}

export async function getDwn(): Promise<{ dwn: Dwn; tenantGate: TenantGate }> {
export async function getTestDwn(
powRequired?: boolean,
tosRequired?: boolean,
): Promise<{
dwn: Dwn;
tenantGate: TenantGate;
}> {
const db = getDialectFromURI(new URL('sqlite://'));
const dataStore = new DataStoreSql(db);
const eventLog = new EventLogSql(db);
const messageStore = new MessageStoreSql(db);
const tenantGate = new TenantGate(db, true, false);
const tenantGate = new TenantGate(db, powRequired, tosRequired);

const dwn = await Dwn.create({ eventLog, dataStore, messageStore });
let dwn: Dwn;
try {
dwn = await Dwn.create({
eventLog,
dataStore,
messageStore,
tenantGate,
});
} catch (e) {
throw e;
}

return { dwn, tenantGate };
}
Loading

0 comments on commit 1a4a612

Please sign in to comment.