Merge pull request #45 from mailvelope/dev/remove-primaryEmail-param

Dev/remove primary email param
This commit is contained in:
Tankred Hase 2017-08-24 14:13:26 +08:00 committed by GitHub
commit 2b969c0382
5 changed files with 36 additions and 71 deletions

View File

@ -124,13 +124,11 @@ POST /api/v1/key
```json ```json
{ {
"publicKeyArmored": "-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----", "publicKeyArmored": "-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----"
"primaryEmail": "user@example.com"
} }
``` ```
* **publicKeyArmored**: The ascii armored public PGP key to be uploaded * **publicKeyArmored**: The ascii armored public PGP key to be uploaded
* **primaryEmail (optional)**: The ascii armored block is parsed to check for user ids, so this parameter is purely optional. Normally a verification email is sent to every user id found in the pgp key. To prevent this behaviour, user agents can specify the user's primary email address to send out only one email.
### Verify uploaded key (via link in email) ### Verify uploaded key (via link in email)

View File

@ -37,12 +37,12 @@ class REST {
* @param {Object} ctx The koa request/response context * @param {Object} ctx The koa request/response context
*/ */
async create(ctx) { async create(ctx) {
const {publicKeyArmored, primaryEmail} = ctx.request.body; const {publicKeyArmored} = ctx.request.body;
if (!publicKeyArmored || (primaryEmail && !util.isEmail(primaryEmail))) { if (!publicKeyArmored) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
const origin = util.origin(ctx); const origin = util.origin(ctx);
await this._publicKey.put({publicKeyArmored, primaryEmail, origin}); await this._publicKey.put({publicKeyArmored, origin});
ctx.body = 'Upload successful. Check your inbox to verify your email address.'; ctx.body = 'Upload successful. Check your inbox to verify your email address.';
ctx.status = 201; ctx.status = 201;
} }

View File

@ -36,6 +36,7 @@ const tpl = require('../email/templates.json');
* } * }
* ], * ],
* created: Sat Oct 17 2015 12:17:03 GMT+0200 (CEST), // key creation time as JavaScript Date * created: Sat Oct 17 2015 12:17:03 GMT+0200 (CEST), // key creation time as JavaScript Date
* uploaded: Sat Oct 17 2015 12:17:03 GMT+0200 (CEST), // time of key upload as JavaScript Date
* algorithm: 'rsa_encrypt_sign', // primary key alogrithm * algorithm: 'rsa_encrypt_sign', // primary key alogrithm
* keySize: 4096, // key length in bits * keySize: 4096, // key length in bits
* publicKeyArmored: '-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----' * publicKeyArmored: '-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----'
@ -62,11 +63,10 @@ class PublicKey {
/** /**
* Persist a new public key * Persist a new public key
* @param {String} publicKeyArmored The ascii armored pgp key block * @param {String} publicKeyArmored The ascii armored pgp key block
* @param {String} primaryEmail (optional) The key's primary email address
* @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' } * @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' }
* @yield {undefined} * @yield {undefined}
*/ */
async put({publicKeyArmored, primaryEmail, origin}) { async put({publicKeyArmored, origin}) {
// lazily purge old/unverified keys on every key upload // lazily purge old/unverified keys on every key upload
await this._purgeOldUnverified(); await this._purgeOldUnverified();
// parse key block // parse key block
@ -79,7 +79,7 @@ class PublicKey {
// store key in database // store key in database
await this._persisKey(key); await this._persisKey(key);
// send mails to verify user ids (send only one if primary email is provided) // send mails to verify user ids (send only one if primary email is provided)
await this._sendVerifyEmail(key, primaryEmail, origin); await this._sendVerifyEmail(key, origin);
} }
/** /**
@ -121,17 +121,10 @@ class PublicKey {
* Send verification emails to the public keys user ids for verification. * Send verification emails to the public keys user ids for verification.
* If a primary email address is provided only one email will be sent. * If a primary email address is provided only one email will be sent.
* @param {Array} userIds user id documents containg the verification nonces * @param {Array} userIds user id documents containg the verification nonces
* @param {string} primaryEmail the public key's primary email address
* @param {Object} origin the server's origin (required for email links) * @param {Object} origin the server's origin (required for email links)
* @yield {undefined} * @yield {undefined}
*/ */
async _sendVerifyEmail({userIds, keyId, publicKeyArmored}, primaryEmail, origin) { async _sendVerifyEmail({userIds, keyId, publicKeyArmored}, origin) {
// check for primary email (send only one email)
const primaryUserId = userIds.find(uid => uid.email === primaryEmail);
if (primaryUserId) {
userIds = [primaryUserId];
}
// send emails
for (const userId of userIds) { for (const userId of userIds) {
userId.publicKeyArmored = publicKeyArmored; // set key for encryption userId.publicKeyArmored = publicKeyArmored; // set key for encryption
await this._email.send({template: tpl.verifyKey, userId, keyId, origin}); await this._email.send({template: tpl.verifyKey, userId, keyId, origin});

View File

@ -68,26 +68,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
.end(done); .end(done);
}); });
it('should return 400 for an invalid primaryEmail', done => { it('should return 201', done => {
request(app.listen())
.post('/api/v1/key')
.send({publicKeyArmored, primaryEmail: 'foo'})
.expect(400)
.end(done);
});
it('should return 201 with primaryEmail', done => {
request(app.listen())
.post('/api/v1/key')
.send({publicKeyArmored, primaryEmail})
.expect(201)
.end(() => {
expect(emailParams).to.exist;
done();
});
});
it('should return 201 without primaryEmail', done => {
request(app.listen()) request(app.listen())
.post('/api/v1/key') .post('/api/v1/key')
.send({publicKeyArmored}) .send({publicKeyArmored})
@ -103,7 +84,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
beforeEach(done => { beforeEach(done => {
request(app.listen()) request(app.listen())
.post('/api/v1/key') .post('/api/v1/key')
.send({publicKeyArmored, primaryEmail}) .send({publicKeyArmored})
.expect(201) .expect(201)
.end(done); .end(done);
}); });
@ -134,7 +115,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
beforeEach(done => { beforeEach(done => {
request(app.listen()) request(app.listen())
.post('/api/v1/key') .post('/api/v1/key')
.send({publicKeyArmored, primaryEmail}) .send({publicKeyArmored})
.expect(201) .expect(201)
.end(done); .end(done);
}); });
@ -196,7 +177,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
beforeEach(done => { beforeEach(done => {
request(app.listen()) request(app.listen())
.post('/api/v1/key') .post('/api/v1/key')
.send({publicKeyArmored, primaryEmail}) .send({publicKeyArmored})
.expect(201) .expect(201)
.end(done); .end(done);
}); });
@ -234,7 +215,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
beforeEach(done => { beforeEach(done => {
request(app.listen()) request(app.listen())
.post('/api/v1/key') .post('/api/v1/key')
.send({publicKeyArmored, primaryEmail}) .send({publicKeyArmored})
.expect(201) .expect(201)
.end(() => { .end(() => {
request(app.listen()) request(app.listen())

View File

@ -73,30 +73,23 @@ describe('Public Key Integration Tests', function() {
}); });
describe('put', () => { describe('put', () => {
it('should persist key and send verification email with primaryEmail', async () => { it('should persist key and send verification email', async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin});
expect(mailsSent.length).to.equal(1);
expect(mailsSent[0].to).to.equal(primaryEmail);
expect(mailsSent[0].params.keyId).to.exist;
expect(mailsSent[0].params.nonce).to.exist;
});
it('should persist key and send verification email without primaryEmail', async () => {
await publicKey.put({publicKeyArmored, origin}); await publicKey.put({publicKeyArmored, origin});
expect(mailsSent.length).to.equal(4); expect(mailsSent.length).to.equal(4);
}); });
it('should work twice if not yet verified', async () => { it('should work twice if not yet verified', async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
expect(mailsSent.length).to.equal(1); expect(mailsSent.length).to.equal(4);
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
expect(mailsSent.length).to.equal(2); expect(mailsSent.length).to.equal(8);
}); });
it('should throw 304 if key already exists', async () => { it('should throw 304 if key already exists', async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
try { try {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
expect(false).to.be.true; expect(false).to.be.true;
} catch (e) { } catch (e) {
expect(e.status).to.equal(304); expect(e.status).to.equal(304);
@ -147,7 +140,7 @@ describe('Public Key Integration Tests', function() {
describe('verify', () => { describe('verify', () => {
it('should update the document', async () => { it('should update the document', async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
const emailParams = mailsSent[0].params; const emailParams = mailsSent[0].params;
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE); const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE);
@ -158,7 +151,7 @@ describe('Public Key Integration Tests', function() {
}); });
it('should not find the document', async () => { it('should not find the document', async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
const emailParams = mailsSent[0].params; const emailParams = mailsSent[0].params;
try { try {
await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'}); await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'});
@ -174,11 +167,11 @@ describe('Public Key Integration Tests', function() {
}); });
it('should not verify a second key for already verified user id of another key', async () => { it('should not verify a second key for already verified user id of another key', async () => {
await publicKey.put({publicKeyArmored, primaryEmail: primaryEmail2, origin}); await publicKey.put({publicKeyArmored, origin});
expect(mailsSent.length).to.equal(1); expect(mailsSent.length).to.equal(4);
await publicKey.put({publicKeyArmored: publicKeyArmored2, primaryEmail: primaryEmail2, origin}); await publicKey.put({publicKeyArmored: publicKeyArmored2, origin});
expect(mailsSent.length).to.equal(2); expect(mailsSent.length).to.equal(5);
await publicKey.verify(mailsSent[1].params); await publicKey.verify(mailsSent[4].params);
try { try {
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
@ -189,7 +182,7 @@ describe('Public Key Integration Tests', function() {
const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE); const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE);
expect(gotten.userIds[1].email).to.equal(primaryEmail2); expect(gotten.userIds[1].email).to.equal(primaryEmail2);
expect(gotten.userIds[1].verified).to.be.false; expect(gotten.userIds[1].verified).to.be.false;
expect(gotten.userIds[1].nonce).to.equal(mailsSent[0].params.nonce); expect(gotten.userIds[1].nonce).to.equal(mailsSent[1].params.nonce);
}); });
it('should be able to verify multiple user ids', async () => { it('should be able to verify multiple user ids', async () => {
@ -213,7 +206,7 @@ describe('Public Key Integration Tests', function() {
describe('should find a verified key', () => { describe('should find a verified key', () => {
beforeEach(async () => { beforeEach(async () => {
key = pgp.parseKey(publicKeyArmored); key = pgp.parseKey(publicKeyArmored);
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
}); });
@ -281,7 +274,7 @@ describe('Public Key Integration Tests', function() {
let emailParams; let emailParams;
beforeEach(async () => { beforeEach(async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
emailParams = mailsSent[0].params; emailParams = mailsSent[0].params;
}); });
@ -337,24 +330,24 @@ describe('Public Key Integration Tests', function() {
let keyId; let keyId;
beforeEach(async () => { beforeEach(async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
keyId = mailsSent[0].params.keyId; keyId = mailsSent[0].params.keyId;
}); });
it('should work for verified key', async () => { it('should work for verified key', async () => {
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin});
expect(mailsSent.length).to.equal(5); expect(mailsSent.length).to.equal(8);
}); });
it('should work for unverified key', async () => { it('should work for unverified key', async () => {
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin});
expect(mailsSent.length).to.equal(5); expect(mailsSent.length).to.equal(8);
}); });
it('should work by email address', async () => { it('should work by email address', async () => {
await publicKey.requestRemove({email: primaryEmail, origin}); await publicKey.requestRemove({email: primaryEmail, origin});
expect(mailsSent.length).to.equal(2); expect(mailsSent.length).to.equal(5);
}); });
it('should throw 404 for no key', async () => { it('should throw 404 for no key', async () => {
@ -372,13 +365,13 @@ describe('Public Key Integration Tests', function() {
let keyId; let keyId;
beforeEach(async () => { beforeEach(async () => {
await publicKey.put({publicKeyArmored, primaryEmail, origin}); await publicKey.put({publicKeyArmored, origin});
keyId = mailsSent[0].params.keyId; keyId = mailsSent[0].params.keyId;
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin});
}); });
it('should remove key', async () => { it('should remove key', async () => {
await publicKey.verifyRemove(mailsSent[1].params); await publicKey.verifyRemove(mailsSent[4].params);
const key = await mongo.get({keyId}, DB_TYPE); const key = await mongo.get({keyId}, DB_TYPE);
expect(key).to.not.exist; expect(key).to.not.exist;
}); });