Merge pull request #20 from andris9/gpg

Gpg
This commit is contained in:
Andris Reinman 2016-04-16 10:51:52 -07:00
commit 4d56287583
16 changed files with 131 additions and 6 deletions

View file

@ -3,6 +3,7 @@
let log = require('npmlog');
let nodemailer = require('nodemailer');
let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt;
let settings = require('./models/settings');
let Handlebars = require('handlebars');
let fs = require('fs');
@ -82,7 +83,7 @@ function getTemplate(template, callback) {
}
function createMailer(callback) {
settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned'], (err, configItems) => {
settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase'], (err, configItems) => {
if (err) {
return callback(err);
}
@ -108,6 +109,10 @@ function createMailer(callback) {
rejectUnauthorized: !configItems.smtpSelfSigned
}
});
module.exports.transport.use('stream', openpgpEncrypt({
signingKey: configItems.pgpPrivateKey,
passphrase: configItems.pgpPassphrase
}));
return callback(null, module.exports.transport);
});

View file

@ -12,6 +12,8 @@ module.exports.grouped = ['radio', 'checkbox', 'dropdown'];
module.exports.types = {
text: 'Text',
website: 'Website',
longtext: 'Multi-line text',
gpg: 'GPG Public Key',
number: 'Number',
radio: 'Radio Buttons',
checkbox: 'Checkboxes',
@ -26,6 +28,8 @@ module.exports.types = {
module.exports.genericTypes = {
text: 'string',
website: 'string',
longtext: 'textarea',
gpg: 'textarea',
number: 'number',
'date-us': 'date',
'date-eur': 'date',
@ -264,7 +268,7 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba
let column = null;
let key = slugify('merge ' + name, '_').toUpperCase();
let allowedTypes = ['text', 'number', 'radio', 'checkbox', 'dropdown', 'date-us', 'date-eur', 'birthday-us', 'birthday-eur', 'website', 'option'];
let allowedTypes = ['text', 'number', 'radio', 'checkbox', 'dropdown', 'date-us', 'date-eur', 'birthday-us', 'birthday-eur', 'website', 'option', 'gpg', 'longtext'];
if (allowedTypes.indexOf(type) < 0) {
return callback(new Error('Unknown column type ' + type));
@ -308,7 +312,11 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba
switch (type) {
case 'text':
case 'website':
query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` VARCHAR(255) DEFAULT NULL';
query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` VARCHAR(255) DEFAULT NULL, ADD INDEX (' + column + ')';
break;
case 'gpg':
case 'longtext':
query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` TEXT DEFAULT NULL';
break;
case 'number':
query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` INT(11) DEFAULT NULL, ADD INDEX (' + column + ')';
@ -358,6 +366,8 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => {
switch (field.type) {
case 'text':
case 'website':
case 'gpg':
case 'longtext':
{
let item = {
type: field.type,

View file

@ -99,6 +99,9 @@ module.exports.get = (id, callback) => {
segment.columns = [].concat(module.exports.defaultColumns);
fieldList.forEach(field => {
if (fields.genericTypes[field.type] === 'textarea') {
return;
}
if (field.column) {
segment.columns.push({
column: field.column,

View file

@ -22,7 +22,7 @@
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-contrib-nodeunit": "^1.0.0",
"grunt-eslint": "^18.0.0"
"grunt-eslint": "^18.1.0"
},
"dependencies": {
"bcrypt-nodejs": "0.0.3",
@ -47,7 +47,9 @@
"multer": "^1.1.0",
"mysql": "^2.10.2",
"nodemailer": "^2.3.2",
"nodemailer-openpgp": "^1.0.2",
"npmlog": "^2.0.3",
"openpgp": "^2.2.1",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"request": "^2.71.0",

View file

@ -31,3 +31,7 @@ div.jumbotron{
.code-editor {
height: 400px;
}
.gpg-text {
font-family: monospace;
}

View file

@ -1,5 +1,6 @@
'use strict';
let openpgp = require('openpgp');
let passport = require('../lib/passport');
let express = require('express');
let router = new express.Router();
@ -156,6 +157,26 @@ router.post('/ajax/:id', (req, res) => {
].concat(fields.getRow(fieldList, row).map(cRow => {
if (cRow.type === 'number') {
return htmlescape(cRow.value && humanize.numberFormat(cRow.value, 0) || '');
} else if (cRow.type === 'longtext') {
let value = (cRow.value || '');
if (value.length > 50) {
value = value.substr(0, 47).trim() + '…';
}
return htmlescape(value);
} else if (cRow.type === 'gpg') {
let value = (cRow.value || '').trim();
try {
value = openpgp.key.readArmored(value);
if (value) {
value = value.keys && value.keys[0] && value.keys[0].primaryKey.fingerprint;
if (value) {
value = '0x' + value.substr(-16).toUpperCase();
}
}
} catch (E) {
value = 'parse error';
}
return htmlescape(value || '');
} else {
return htmlescape(cRow.value || '');
}

View file

@ -235,6 +235,8 @@ router.get('/:list/rules/:segment/create', passport.csrfProtection, (req, res) =
}
segment.csrfToken = req.csrfToken();
console.log(segment);
segment.list = list;
res.render('lists/segments/rule-create', segment);

View file

@ -11,7 +11,7 @@ let url = require('url');
let settings = require('../lib/models/settings');
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg'];
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg', 'pgp_private_key', 'pgp_passphrase'];
router.all('/*', (req, res, next) => {
if (!req.user) {

View file

@ -130,10 +130,16 @@ function formatMessage(message, callback) {
FULL_NAME: [].concat(message.subscription.firstName || []).concat(message.subscription.lastName || []).join(' ')
};
let encryptionKeys = [];
fields.getRow(fieldList, message.subscription, true, true).forEach(field => {
if (field.mergeTag) {
message.subscription.mergeTags[field.mergeTag] = field.mergeValue || '';
}
if (field.type === 'gpg' && field.value) {
encryptionKeys.push(field.value.trim());
}
if (field.options) {
field.options.forEach(subField => {
if (subField.mergeTag) {
@ -206,7 +212,8 @@ function formatMessage(message, callback) {
html: tools.formatMessage(configItems.serviceUrl, campaign, list, message.subscription, html),
text: tools.formatMessage(configItems.serviceUrl, campaign, list, message.subscription, campaign.text),
attachments
attachments,
encryptionKeys
});
});
});

View file

@ -26,6 +26,8 @@
<option value="text" {{#if selectedText}} selected {{/if}}>Text</option>
<option value="number" {{#if selectedNumber}} selected {{/if}}>Number</option>
<option value="website" {{#if selectedWebsite}} selected {{/if}}>Website</option>
<option value="gpg" {{#if selectedGpg}} selected {{/if}}>GPG Public Key</option>
<option value="longtext" {{#if selectedLongtext}} selected {{/if}}>Multi-line text</option>
<optgroup label="Date">
<option value="date-us" {{#if selectedDateUs}} selected {{/if}}>Date (MM/DD/YYYY)</option>
<option value="date-eur" {{#if selectedDateEur}} selected {{/if}}>Date (DD/MM/YYYY)</option>

View file

@ -33,6 +33,8 @@
<option value="text" {{#if selectedText}} selected {{/if}}>Text</option>
<option value="number" {{#if selectedNumber}} selected {{/if}}>Number</option>
<option value="website" {{#if selectedWebsite}} selected {{/if}}>Website</option>
<option value="gpg" {{#if selectedGpg}} selected {{/if}}>GPG Public Key</option>
<option value="longtext" {{#if selectedLongtext}} selected {{/if}}>Multi-line text</option>
<optgroup label="Date">
<option value="date-us" {{#if selectedDateUs}} selected {{/if}}>Date (MM/DD/YYYY)</option>
<option value="date-eur" {{#if selectedDateEur}} selected {{/if}}>Date (DD/MM/YYYY)</option>

View file

@ -50,6 +50,15 @@
<input type="url" class="form-control" name="{{column}}" value="{{value}}">
{{/if}}
{{#if typeLongtext}}
<textarea class="form-control" rows="3" name="{{column}}">{{value}}</textarea>
{{/if}}
{{#if typeGpg}}
<textarea class="form-control gpg-text" rows="3" name="{{column}}">{{value}}</textarea>
<span class="help-block">Insert a GPG public key that will be used to encrypt messages sent this subscriber</span>
{{/if}}
{{#if typeDateUs}}
<div class="input-group date fm-date-us">
<input type="text" class="form-control" name="{{column}}" placeholder="MM/DD/YYYY" value="{{value}}"><span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>

View file

@ -63,6 +63,15 @@
<input type="url" class="form-control" name="{{column}}" value="{{value}}">
{{/if}}
{{#if typeLongtext}}
<textarea class="form-control" rows="3" name="{{column}}">{{value}}</textarea>
{{/if}}
{{#if typeGpg}}
<textarea class="form-control gpg-text" rows="3" name="{{column}}">{{value}}</textarea>
<span class="help-block">Insert a GPG public key that will be used to encrypt messages sent this subscriber</span>
{{/if}}
{{#if typeDateUs}}
<div class="input-group date fm-date-us">
<input type="text" class="form-control" name="{{column}}" placeholder="MM/DD/YYYY" value="{{value}}"><span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>

View file

@ -251,6 +251,37 @@
{{/if}}
</fieldset>
<fieldset>
<legend>
PGP Signing
</legend>
<p>
Only messages that are encrypted can be signed. Subsribers who have not set up a PGP public key in their profile receive normal email messages. Users with PGP key set receive encrypted messages and if you have signing key also set, the messages are signed
with this key.
</p>
<p class="text-warning">
Do not use sensitive keys here. The private key and passphrase are not encrypted in the database.
</p>
<div class="form-group">
<label for="pgp-passphrase" class="col-sm-2 control-label">Private Key Passphrase</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="pgp-passphrase" id="pgp-passphrase" placeholder="Passprase" value="{{pgpPassphrase}}">
<span class="help-block">Only fill this if your private key is encrypted with a passphrase</span>
</div>
</div>
<div class="form-group">
<label for="pgp-private-key" class="col-sm-2 control-label">PGP Private Key</label>
<div class="col-sm-10">
<textarea class="form-control gpg-text" rows="3" id="pgp-private-key" name="pgp-private-key">{{pgpPrivateKey}}</textarea>
<span class="help-block">This value is optional. if you do not provide a private key, then PGP encrypted messages are sent without signing.</span>
</div>
</div>
</fieldset>
<hr />
<div class="form-group">

View file

@ -36,6 +36,15 @@
<input type="url" class="form-control" name="{{column}}" value="{{value}}">
{{/if}}
{{#if typeLongtext}}
<textarea class="form-control" rows="3" name="{{column}}">{{value}}</textarea>
{{/if}}
{{#if typeGpg}}
<textarea class="form-control gpg-text" rows="3" name="{{column}}">{{value}}</textarea>
<span class="help-block">Insert your GPG public key here to encrypt messages sent to your address</span>
{{/if}}
{{#if typeDateUs}}
<div class="input-group date fm-date-us">
<input type="text" class="form-control" name="{{column}}" placeholder="MM/DD/YYYY" value="{{value}}"><span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>

View file

@ -33,6 +33,15 @@
<input type="url" class="form-control" name="{{column}}" value="{{value}}">
{{/if}}
{{#if typeLongtext}}
<textarea class="form-control" rows="3" name="{{column}}">{{value}}</textarea>
{{/if}}
{{#if typeGpg}}
<textarea class="form-control gpg-text" rows="3" name="{{column}}">{{value}}</textarea>
<span class="help-block">Insert your GPG public key here to encrypt messages sent to your address</span>
{{/if}}
{{#if typeDateUs}}
<div class="input-group date fm-date-us">
<input type="text" class="form-control" name="{{column}}" placeholder="MM/DD/YYYY" value="{{value}}"><span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>