Design class architecture and HTTP api

This commit is contained in:
Tankred Hase 2016-05-26 13:45:32 +02:00
parent e23b65ac0f
commit 82fcb819e9
5 changed files with 201 additions and 42 deletions

View File

@ -17,6 +17,16 @@
'use strict'; 'use strict';
/**
* Database documents have the format:
* {
* _id: "02C134D079701934", // the 16 byte key id
* email: "jon@example.com", // the primary and verified email address
* publicKeyArmored: "-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----"
* }
*/
const DB_TYPE = 'publickey';
/** /**
* A controller that handlers PGP public keys queries to the database * A controller that handlers PGP public keys queries to the database
*/ */
@ -30,6 +40,38 @@ class PublicKey {
this._mongo = mongo; this._mongo = mongo;
} }
//
// Create/Update
//
put(options) {
}
verify(options) {
}
//
// Read
//
get(options) {
}
//
// Delete
//
remove(options) {
}
verifyRemove(options) {
}
} }
module.exports = PublicKey; module.exports = PublicKey;

29
src/dao/email.js Normal file
View File

@ -0,0 +1,29 @@
/**
* Mailvelope - secure email with OpenPGP encryption for Webmail
* Copyright (C) 2016 Mailvelope GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
/**
* A simple wrapper around Nodemailer to send verification emails
*/
class Email {
send(options) {
}
}

View File

@ -31,30 +31,35 @@ class HKP {
this._publicKey = publicKey; this._publicKey = publicKey;
} }
/**
* Public key lookup via http GET
* @param {Object} ctx The koa request/response context
*/
*lookup(ctx) {
var params = this.parseQueryString(ctx);
if (!params) {
return; // invalid request
}
this.setHeaders(ctx);
if (params.mr) {
this.setGetMRHEaders(ctx);
}
ctx.body = yield Promise.resolve('----- BEGIN PUBLIC PGP KEY -----');
}
/** /**
* Public key upload via http POST * Public key upload via http POST
* @param {Object} ctx The koa request/response context * @param {Object} ctx The koa request/response context
*/ */
*add(ctx) { *add(ctx) {
ctx.throw(501, 'Not implemented!'); ctx.throw(501, 'Not implemented!');
return yield Promise.resolve(); yield;
}
/**
* Public key lookup via http GET
* @param {Object} ctx The koa request/response context
*/
*lookup(ctx) {
let params = this.parseQueryString(ctx);
if (!params) {
return; // invalid request
}
let key = yield this._publicKey.get(params);
if (key) {
ctx.body = key.publicKeyArmored;
if (params.mr) {
this.setGetMRHEaders(ctx);
}
} else {
ctx.status = 404;
ctx.body = 'Not found!';
}
} }
/** /**
@ -64,20 +69,22 @@ class HKP {
* @return {Object} The query parameters or undefined for an invalid request * @return {Object} The query parameters or undefined for an invalid request
*/ */
parseQueryString(ctx) { parseQueryString(ctx) {
let q = ctx.query;
let params = { let params = {
op: q.op, // operation ... only 'get' is supported op: ctx.query.op, // operation ... only 'get' is supported
mr: q.options === 'mr', // machine readable mr: ctx.query.options === 'mr' // machine readable
keyid: this.checkId(q.search) ? q.search.replace('0x', '') : null,
email: this.checkEmail(q.search) ? q.search : null,
}; };
if (this.checkId(ctx.query.search)) {
params._id = ctx.query.search.replace(/^0x/, '');
} else if(this.checkEmail(ctx.query.search)) {
params.email = ctx.query.search;
}
if (params.op !== 'get') { if (params.op !== 'get') {
ctx.status = 501; ctx.status = 501;
ctx.body = 'Not implemented!'; ctx.body = 'Not implemented!';
return; return;
} else if (!params.keyid && !params.email) { } else if (!params._id && !params.email) {
ctx.status = 404; ctx.status = 400;
ctx.body = 'Invalid request!'; ctx.body = 'Invalid request!';
return; return;
} }
@ -101,18 +108,7 @@ class HKP {
* @return {Boolean} If the key id is valid * @return {Boolean} If the key id is valid
*/ */
checkId(keyid) { checkId(keyid) {
return /^0x[a-fA-F0-9]{8,40}/.test(keyid); return /^0x[a-fA-F0-9]{8,40}$/.test(keyid);
}
/**
* Set HTTP headers for the HKP requests.
* @param {Object} ctx The koa request/response context
*/
setHeaders(ctx) {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Cache-Control', 'no-cache');
ctx.set('Pragma', 'no-cache');
ctx.set('Connection', 'keep-alive');
} }
/** /**

56
src/routes/rest.js Normal file
View File

@ -0,0 +1,56 @@
/**
* Mailvelope - secure email with OpenPGP encryption for Webmail
* Copyright (C) 2016 Mailvelope GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
/**
* The REST api to provide additional functionality on top of HKP
*/
class REST {
constructor(publicKey) {
this._publicKey = publicKey;
}
*create(ctx) {
ctx.throw(501, 'Not implemented!');
yield;
}
*verify(ctx) {
ctx.throw(501, 'Not implemented!');
yield;
}
*read(ctx) {
ctx.throw(501, 'Not implemented!');
yield;
}
*remove(ctx) {
ctx.throw(501, 'Not implemented!');
yield;
}
*verifyRemove(ctx) {
ctx.throw(501, 'Not implemented!');
yield;
}
}
module.exports = REST;

View File

@ -25,18 +25,53 @@ const router = require('koa-router')();
const Mongo = require('./dao/mongo'); const Mongo = require('./dao/mongo');
const PublicKey = require('./ctrl/public-key'); const PublicKey = require('./ctrl/public-key');
const HKP = require('./routes/hkp'); const HKP = require('./routes/hkp');
const REST = require('./routes/rest');
let mongo, publicKey, hkp; let mongo, publicKey, hkp, rest;
// //
// Configure koa router // Configure koa HTTP server
// //
router.get('/pks/lookup', function *() { // HKP routes
router.post('/pks/add', function *() { // no query params
yield hkp.add(this);
});
router.get('/pks/lookup', function *() { // ?op=get&search=0x1234567890123456
yield hkp.lookup(this); yield hkp.lookup(this);
}); });
router.post('/pks/add', function *() {
yield hkp.add(this); // REST api routes
router.post('/api/key', function *() { // no query params
yield rest.create(this);
});
router.get('/api/key', function *() { // ?id=keyid OR ?email=email
yield rest.read(this);
});
router.del('/api/key', function *() { // ?id=keyid OR ?email=email
yield rest.remove(this);
});
// links for verification and sharing
router.get('/api/verify', function *() { // ?id=keyid&nonce=nonce
yield rest.verify(this);
});
router.get('/api/verifyRemove', function *() { // ?id=keyid&nonce=nonce
yield rest.verifyRemove(this);
});
router.get('/:email', function *() { // shorthand link for sharing
yield rest.read(this);
});
// Set HTTP response headers
app.use(function *(next) {
this.set('Access-Control-Allow-Origin', '*');
this.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
this.set('Access-Control-Allow-Headers', 'Content-Type');
this.set('Cache-Control', 'no-cache');
this.set('Pragma', 'no-cache');
this.set('Connection', 'keep-alive');
yield next;
}); });
app.use(router.routes()); app.use(router.routes());
@ -56,6 +91,7 @@ function injectDependencies() {
}); });
publicKey = new PublicKey(mongo); publicKey = new PublicKey(mongo);
hkp = new HKP(publicKey); hkp = new HKP(publicKey);
rest = new REST(publicKey);
} }
function readCredentials() { function readCredentials() {