Merge branch 'esisar-restrictions'
commit
f535a197d3
|
@ -74,7 +74,7 @@ class REST {
|
|||
if (!util.isKeyId(q.keyId) || !util.isString(q.nonce)) {
|
||||
ctx.throw(400, 'Invalid request!');
|
||||
}
|
||||
const {email} = await this._publicKey.verify(q);
|
||||
const {email} = await this._publicKey.verify(q, util.origin(ctx), ctx);
|
||||
// create link for sharing
|
||||
const link = util.url(util.origin(ctx), `/pks/lookup?op=get&search=${email}`);
|
||||
await ctx.render('verify-success', {email, link});
|
||||
|
|
|
@ -69,7 +69,12 @@ class PGP {
|
|||
// check for at least one valid user id
|
||||
const userIds = await this.parseUserIds(key.users, primaryKey, verifyDate);
|
||||
if (!userIds.length) {
|
||||
util.throw(400, 'Invalid PGP key: invalid user IDs');
|
||||
if (status == 1) {
|
||||
util.throw(400, 'Invalid PGP key: no user ID comes from a valid organisation');
|
||||
}
|
||||
else {
|
||||
util.throw(400, 'Invalid PGP key: invalid user IDs');
|
||||
}
|
||||
}
|
||||
|
||||
// get algorithm details from primary key
|
||||
|
@ -118,8 +123,9 @@ class PGP {
|
|||
* Parse an array of user ids and verify signatures
|
||||
* @param {Array} users A list of openpgp.js user objects
|
||||
* @param {Object} primaryKey The primary key packet of the key
|
||||
* @param {Date} verifyDate Verify user IDs at this point in time
|
||||
* @return {Array} An array of user id objects
|
||||
* @param {Date} verifyDate Verify user IDs at this point in time
|
||||
* @return {Array, integer} An array of user id objects and a satus indicator.
|
||||
* Values of status : 0 if no error, 1 if no address comes from a specific organisation.
|
||||
*/
|
||||
async parseUserIds(users, primaryKey, verifyDate = new Date()) {
|
||||
if (!users || !users.length) {
|
||||
|
@ -127,6 +133,7 @@ class PGP {
|
|||
}
|
||||
// at least one user id must be valid, revoked or expired
|
||||
const result = [];
|
||||
var isFromOrganisation = false;
|
||||
for (const user of users) {
|
||||
const userStatus = await user.verify(primaryKey, verifyDate);
|
||||
if (userStatus !== openpgp.enums.keyStatus.invalid && user.userId && user.userId.userid) {
|
||||
|
@ -140,11 +147,18 @@ class PGP {
|
|||
email: util.normalizeEmail(uid.email),
|
||||
verified: false
|
||||
});
|
||||
if(util.isFromOrganisation(util.normalizeEmail(uid.email)))
|
||||
isFromOrganisation = true;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
var status = 0;
|
||||
if(!isFromOrganisation){
|
||||
result.length = 0;
|
||||
status = 1;
|
||||
}
|
||||
return {userIds: result, status: status};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,6 +91,10 @@ class PublicKey {
|
|||
const filteredPublicKeyArmored = await this._pgp.filterKeyByUserIds(key.userIds.filter(({verified}) => verified), key.publicKeyArmored);
|
||||
// update verified key with new key
|
||||
key.publicKeyArmored = await this._pgp.updateKey(verified.publicKeyArmored, filteredPublicKeyArmored);
|
||||
// send mails to verify all user ids
|
||||
await this._sendVerifyEmail(key, origin, ctx);
|
||||
// store key in database
|
||||
await this._persistKey(key);
|
||||
} else {
|
||||
key.userIds = key.userIds.filter(userId => userId.status === KEY_STATUS_VALID);
|
||||
if (!key.userIds.length) {
|
||||
|
@ -99,11 +103,11 @@ class PublicKey {
|
|||
await this._addKeyArmored(key.userIds, key.publicKeyArmored);
|
||||
// new key, set armored to null
|
||||
key.publicKeyArmored = null;
|
||||
// send mails to verify organisation's user ids
|
||||
await this._sendVerifyOrganisationEmail(key, origin, ctx);
|
||||
// store key in database
|
||||
await this._persistKeyOrganisation(key);
|
||||
}
|
||||
// send mails to verify user ids
|
||||
await this._sendVerifyEmail(key, origin, ctx);
|
||||
// store key in database
|
||||
await this._persistKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,6 +179,24 @@ class PublicKey {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send verification emails to the organisation's public keys user ids for verification.
|
||||
* If a primary email address is provided only one email will be sent.
|
||||
* @param {Array} userIds user id documents containg the verification nonces
|
||||
* @param {Object} origin the server's origin (required for email links)
|
||||
* @param {Object} ctx Context
|
||||
* @return {Promise}
|
||||
*/
|
||||
async _sendVerifyOrganisationEmail({userIds, keyId}, origin, ctx) {
|
||||
for (const userId of userIds) {
|
||||
if (userId.notify && userId.notify === true && util.isFromOrganisation(userId.email)) {
|
||||
// generate nonce for verification
|
||||
userId.nonce = util.random();
|
||||
await this._email.send({template: tpl.verifyKey.bind(null, ctx), userId, keyId, origin, publicKeyArmored: userId.publicKeyArmored});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the public key and its user ids in the database.
|
||||
|
@ -184,7 +206,7 @@ class PublicKey {
|
|||
async _persistKey(key) {
|
||||
// delete old/unverified key
|
||||
await this._mongo.remove({keyId: key.keyId}, DB_TYPE);
|
||||
// generate nonces for verification
|
||||
|
||||
for (const userId of key.userIds) {
|
||||
// remove status from user
|
||||
delete userId.status;
|
||||
|
@ -197,26 +219,61 @@ class PublicKey {
|
|||
util.throw(500, 'Failed to persist key');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the public key and its user ids in the database.
|
||||
* Mark all uids as unprocessed, except the ones with the organisation email.
|
||||
* @param {Object} key public key parameters
|
||||
* @return {Promise}
|
||||
*/
|
||||
async _persistKeyOrganisation(key) {
|
||||
// delete old/unverified key
|
||||
await this._mongo.remove({keyId: key.keyId}, DB_TYPE);
|
||||
|
||||
for (const userId of key.userIds) {
|
||||
if(util.isFromOrganisation(userId.email))
|
||||
{
|
||||
// remove status from user
|
||||
delete userId.status;
|
||||
// remove notify flag from user
|
||||
delete userId.notify;
|
||||
}
|
||||
}
|
||||
// persist new key
|
||||
const r = await this._mongo.create(key, DB_TYPE);
|
||||
if (r.insertedCount !== 1) {
|
||||
util.throw(500, 'Failed to persist key');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a user id by proving knowledge of the nonce.
|
||||
* @param {string} keyId Correspronding public key id
|
||||
* @param {string} nonce The verification nonce proving email address ownership
|
||||
* @param {Object} origin the server's origin (required for email links)
|
||||
* @param {Object} ctx Context
|
||||
* @return {Promise} The email that has been verified
|
||||
*/
|
||||
async verify({keyId, nonce}) {
|
||||
async verify({keyId, nonce}, origin, ctx) {
|
||||
// look for verification nonce in database
|
||||
const query = {keyId, 'userIds.nonce': nonce};
|
||||
const key = await this._mongo.get(query, DB_TYPE);
|
||||
if (!key) {
|
||||
util.throw(404, 'User ID not found');
|
||||
}
|
||||
|
||||
// send mails to verify all unnotified user ids
|
||||
await this._sendVerifyEmail(key, origin, ctx);
|
||||
// store key in database
|
||||
await this._persistKey(key);
|
||||
|
||||
await this._removeKeysWithSameEmail(key, nonce);
|
||||
let {publicKeyArmored, email} = key.userIds.find(userId => userId.nonce === nonce);
|
||||
// update armored key
|
||||
if (key.publicKeyArmored) {
|
||||
publicKeyArmored = await this._pgp.updateKey(key.publicKeyArmored, publicKeyArmored);
|
||||
}
|
||||
|
||||
// flag the user id as verified
|
||||
await this._mongo.update(query, {
|
||||
publicKeyArmored,
|
||||
|
|
|
@ -78,6 +78,19 @@ exports.isEmail = function(data) {
|
|||
return re.test(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks for a valid specific organisation email address.
|
||||
* @param {string} data The email address
|
||||
* @return {boolean} Wether the email address comes from organisation
|
||||
*/
|
||||
exports.isFromOrganisation = function(data) {
|
||||
if (!this.isString(data)) {
|
||||
return false;
|
||||
}
|
||||
const re = /^([a-z0-9\-.]+)@([a-z0-9.\-]*)esisar\.grenoble-inp\.fr$/;
|
||||
return re.test(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize email address to lowercase.
|
||||
* @param {string} email The email address
|
||||
|
|
Loading…
Reference in New Issue