2015-02-01 23:44:44 +08:00
|
|
|
(function () {
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2015-02-03 07:58:58 +08:00
|
|
|
var
|
2015-02-22 06:00:51 +08:00
|
|
|
_ = require('_'),
|
|
|
|
ko = require('ko'),
|
2015-06-23 05:33:27 +08:00
|
|
|
$ = require('$'),
|
|
|
|
kn = require('Knoin/Knoin'),
|
|
|
|
|
|
|
|
Translator = require('Common/Translator'),
|
2015-02-22 06:00:51 +08:00
|
|
|
|
|
|
|
Utils = require('Common/Utils')
|
2015-02-03 07:58:58 +08:00
|
|
|
;
|
2015-02-01 23:44:44 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function PgpUserStore()
|
|
|
|
{
|
2015-02-03 07:58:58 +08:00
|
|
|
this.capaOpenPGP = ko.observable(false);
|
2015-02-17 21:30:10 +08:00
|
|
|
|
2015-02-01 23:44:44 +08:00
|
|
|
this.openpgp = null;
|
|
|
|
|
2015-02-03 07:58:58 +08:00
|
|
|
this.openpgpkeys = ko.observableArray([]);
|
|
|
|
this.openpgpKeyring = null;
|
2015-02-01 23:44:44 +08:00
|
|
|
|
2015-02-03 07:58:58 +08:00
|
|
|
this.openpgpkeysPublic = this.openpgpkeys.filter(function (oItem) {
|
|
|
|
return !!(oItem && !oItem.isPrivate);
|
|
|
|
});
|
2015-02-01 23:44:44 +08:00
|
|
|
|
2015-02-03 07:58:58 +08:00
|
|
|
this.openpgpkeysPrivate = this.openpgpkeys.filter(function (oItem) {
|
|
|
|
return !!(oItem && oItem.isPrivate);
|
|
|
|
});
|
|
|
|
}
|
2015-02-01 23:44:44 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
PgpUserStore.prototype.isSupported = function ()
|
|
|
|
{
|
|
|
|
return !!this.openpgp;
|
|
|
|
};
|
|
|
|
|
2015-02-22 06:00:51 +08:00
|
|
|
PgpUserStore.prototype.findPublicKeyByHex = function (sHash)
|
|
|
|
{
|
|
|
|
return _.find(this.openpgpkeysPublic(), function (oItem) {
|
2015-06-23 05:33:27 +08:00
|
|
|
return sHash && oItem && sHash === oItem.id;
|
2015-02-22 06:00:51 +08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-06-23 05:33:27 +08:00
|
|
|
PgpUserStore.prototype.findPrivateKeyByHex = function (sHash)
|
2015-02-22 06:00:51 +08:00
|
|
|
{
|
2015-06-23 05:33:27 +08:00
|
|
|
return _.find(this.openpgpkeysPrivate(), function (oItem) {
|
|
|
|
return sHash && oItem && sHash === oItem.id;
|
|
|
|
});
|
|
|
|
};
|
2015-02-22 06:00:51 +08:00
|
|
|
|
2015-06-23 05:33:27 +08:00
|
|
|
PgpUserStore.prototype.findPublicKeysByEmail = function (sEmail)
|
|
|
|
{
|
|
|
|
return _.compact(_.flatten(_.map(this.openpgpkeysPublic(), function (oItem) {
|
2016-04-07 02:26:29 +08:00
|
|
|
var oKey = oItem && -1 !== oItem.emails.indexOf(sEmail) ? oItem : null;
|
2015-06-23 05:33:27 +08:00
|
|
|
return oKey ? oKey.getNativeKeys() : [null];
|
|
|
|
}), true));
|
|
|
|
};
|
2015-02-22 06:00:51 +08:00
|
|
|
|
2015-06-23 05:33:27 +08:00
|
|
|
PgpUserStore.prototype.findPublicKeysBySigningKeyIds = function (aSigningKeyIds)
|
|
|
|
{
|
|
|
|
var self = this;
|
|
|
|
return _.compact(_.flatten(_.map(aSigningKeyIds, function (oId) {
|
|
|
|
var oKey = oId && oId.toHex ? self.findPublicKeyByHex(oId.toHex()) : null;
|
|
|
|
return oKey ? oKey.getNativeKeys() : [null];
|
|
|
|
}), true));
|
|
|
|
};
|
2015-02-22 06:00:51 +08:00
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
PgpUserStore.prototype.findPrivateKeysByEncryptionKeyIds = function (aEncryptionKeyIds, aRecipients, bReturnWrapKeys)
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
var self = this, aResult = [];
|
|
|
|
aResult = Utils.isArray(aEncryptionKeyIds) ? _.compact(_.flatten(_.map(aEncryptionKeyIds, function (oId) {
|
2015-06-23 05:33:27 +08:00
|
|
|
var oKey = oId && oId.toHex ? self.findPrivateKeyByHex(oId.toHex()) : null;
|
|
|
|
return oKey ? (bReturnWrapKeys ? [oKey] : oKey.getNativeKeys()) : [null];
|
2015-09-18 03:48:52 +08:00
|
|
|
}), true)) : [];
|
|
|
|
|
|
|
|
if (0 === aResult.length && Utils.isNonEmptyArray(aRecipients))
|
|
|
|
{
|
|
|
|
aResult = _.compact(_.flatten(_.map(aRecipients, function (sEmail) {
|
|
|
|
var oKey = sEmail ? self.findPrivateKeyByEmailNotNative(sEmail) : null;
|
|
|
|
return oKey ? (bReturnWrapKeys ? [oKey] : oKey.getNativeKeys()) : [null];
|
|
|
|
}), true));
|
|
|
|
}
|
|
|
|
|
|
|
|
return aResult;
|
2015-02-22 06:00:51 +08:00
|
|
|
};
|
|
|
|
|
2015-07-30 01:21:24 +08:00
|
|
|
/**
|
|
|
|
* @param {string} sEmail
|
|
|
|
* @return {?}
|
|
|
|
*/
|
|
|
|
PgpUserStore.prototype.findPublicKeyByEmailNotNative = function (sEmail)
|
|
|
|
{
|
|
|
|
return _.find(this.openpgpkeysPublic(), function (oItem) {
|
2016-04-07 02:26:29 +08:00
|
|
|
return oItem && -1 !== oItem.emails.indexOf(sEmail);
|
2015-07-30 01:21:24 +08:00
|
|
|
}) || null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} sEmail
|
|
|
|
* @return {?}
|
|
|
|
*/
|
|
|
|
PgpUserStore.prototype.findPrivateKeyByEmailNotNative = function (sEmail)
|
|
|
|
{
|
|
|
|
return _.find(this.openpgpkeysPrivate(), function (oItem) {
|
2016-04-07 02:26:29 +08:00
|
|
|
return oItem && -1 !== oItem.emails.indexOf(sEmail);
|
2015-07-30 01:21:24 +08:00
|
|
|
}) || null;
|
|
|
|
};
|
|
|
|
|
2015-02-22 06:00:51 +08:00
|
|
|
/**
|
|
|
|
* @param {string} sEmail
|
|
|
|
* @param {string=} sPassword
|
2015-04-14 02:45:09 +08:00
|
|
|
* @return {?}
|
2015-02-22 06:00:51 +08:00
|
|
|
*/
|
|
|
|
PgpUserStore.prototype.findPrivateKeyByEmail = function (sEmail, sPassword)
|
|
|
|
{
|
|
|
|
var
|
2015-06-23 05:33:27 +08:00
|
|
|
oPrivateKeys = [],
|
2015-02-22 06:00:51 +08:00
|
|
|
oPrivateKey = null,
|
|
|
|
oKey = _.find(this.openpgpkeysPrivate(), function (oItem) {
|
2016-04-07 02:26:29 +08:00
|
|
|
return oItem && -1 !== oItem.emails.indexOf(sEmail);
|
2015-02-22 06:00:51 +08:00
|
|
|
})
|
|
|
|
;
|
|
|
|
|
|
|
|
if (oKey)
|
|
|
|
{
|
2015-06-23 05:33:27 +08:00
|
|
|
oPrivateKeys = oKey.getNativeKeys();
|
|
|
|
oPrivateKey = oPrivateKeys[0] || null;
|
|
|
|
|
2015-02-22 06:00:51 +08:00
|
|
|
try
|
|
|
|
{
|
2015-06-23 05:33:27 +08:00
|
|
|
if (oPrivateKey)
|
2015-02-22 06:00:51 +08:00
|
|
|
{
|
|
|
|
oPrivateKey.decrypt(Utils.pString(sPassword));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
oPrivateKey = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return oPrivateKey;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string=} sPassword
|
2015-04-14 02:45:09 +08:00
|
|
|
* @return {?}
|
2015-02-22 06:00:51 +08:00
|
|
|
*/
|
|
|
|
PgpUserStore.prototype.findSelfPrivateKey = function (sPassword)
|
|
|
|
{
|
|
|
|
return this.findPrivateKeyByEmail(require('Stores/User/Account').email(), sPassword);
|
|
|
|
};
|
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
PgpUserStore.prototype.decryptMessage = function (oMessage, aRecipients, fCallback)
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
var self = this, aPrivateKeys = [];
|
|
|
|
if (oMessage && oMessage.getEncryptionKeyIds)
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
aPrivateKeys = this.findPrivateKeysByEncryptionKeyIds(oMessage.getEncryptionKeyIds(), aRecipients, true);
|
|
|
|
if (aPrivateKeys && 0 < aPrivateKeys.length)
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
kn.showScreenPopup(require('View/Popup/MessageOpenPgp'), [function (oDecriptedKey) {
|
2015-06-23 05:33:27 +08:00
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
if (oDecriptedKey)
|
|
|
|
{
|
|
|
|
var oPrivateKey = null, oDecryptedMessage = null;
|
|
|
|
try
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
oDecryptedMessage = oMessage.decrypt(oDecriptedKey);
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
oDecryptedMessage = null;
|
|
|
|
}
|
2015-06-23 05:33:27 +08:00
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
if (oDecryptedMessage)
|
|
|
|
{
|
|
|
|
oPrivateKey = self.findPrivateKeyByHex(oDecriptedKey.primaryKey.keyid.toHex());
|
|
|
|
if (oPrivateKey)
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
self.verifyMessage(oDecryptedMessage, function (oValidKey, aSigningKeyIds) {
|
|
|
|
fCallback(oPrivateKey, oDecryptedMessage, oValidKey || null, aSigningKeyIds || null);
|
|
|
|
});
|
2015-06-23 05:33:27 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fCallback(oPrivateKey, oDecryptedMessage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
fCallback(oPrivateKey, oDecryptedMessage);
|
2015-06-23 05:33:27 +08:00
|
|
|
}
|
2015-09-18 03:48:52 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fCallback(null, null);
|
|
|
|
}
|
2015-06-23 05:33:27 +08:00
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
}, aPrivateKeys]);
|
2015-06-23 05:33:27 +08:00
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
return false;
|
2015-06-23 05:33:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fCallback(null, null);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
PgpUserStore.prototype.verifyMessage = function (oMessage, fCallback)
|
|
|
|
{
|
|
|
|
var oValid = null, aResult = [], aPublicKeys = [], aSigningKeyIds = [];
|
|
|
|
if (oMessage && oMessage.getSigningKeyIds)
|
|
|
|
{
|
|
|
|
aSigningKeyIds = oMessage.getSigningKeyIds();
|
|
|
|
if (aSigningKeyIds && 0 < aSigningKeyIds.length)
|
|
|
|
{
|
|
|
|
aPublicKeys = this.findPublicKeysBySigningKeyIds(aSigningKeyIds);
|
|
|
|
if (aPublicKeys && 0 < aPublicKeys.length)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
aResult = oMessage.verify(aPublicKeys);
|
|
|
|
oValid = _.find(_.isArray(aResult) ? aResult : [], function (oItem) {
|
|
|
|
return oItem && oItem.valid && oItem.keyid;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (oValid && oValid.keyid && oValid.keyid && oValid.keyid.toHex)
|
|
|
|
{
|
|
|
|
fCallback(this.findPublicKeyByHex(oValid.keyid.toHex()));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-10-14 00:36:43 +08:00
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
Utils.log(e);
|
|
|
|
}
|
2015-06-23 05:33:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fCallback(null, aSigningKeyIds);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fCallback(null);
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*} mDom
|
|
|
|
*/
|
|
|
|
PgpUserStore.prototype.controlsHelper = function (mDom, oVerControl, bSuccess, sTitle, sText)
|
|
|
|
{
|
|
|
|
if (bSuccess)
|
|
|
|
{
|
|
|
|
mDom.removeClass('error').addClass('success').attr('title', sTitle);
|
|
|
|
oVerControl.removeClass('error').addClass('success').attr('title', sTitle);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mDom.removeClass('success').addClass('error').attr('title', sTitle);
|
|
|
|
oVerControl.removeClass('success').addClass('error').attr('title', sTitle);
|
|
|
|
}
|
|
|
|
|
2015-09-18 03:48:52 +08:00
|
|
|
if (!Utils.isUnd(sText))
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
|
|
|
mDom.text(Utils.trim(sText.replace(/(\u200C|\u0002)/g, '')));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*} mDom
|
2015-09-18 03:48:52 +08:00
|
|
|
* @param {MessageModel} oRainLoopMessage
|
2015-06-23 05:33:27 +08:00
|
|
|
*/
|
2015-09-18 03:48:52 +08:00
|
|
|
PgpUserStore.prototype.initMessageBodyControls = function (mDom, oRainLoopMessage)
|
2015-06-23 05:33:27 +08:00
|
|
|
{
|
|
|
|
if (mDom && !mDom.hasClass('inited'))
|
|
|
|
{
|
|
|
|
mDom.addClass('inited');
|
|
|
|
|
|
|
|
var
|
|
|
|
self = this,
|
|
|
|
bEncrypted = mDom.hasClass('encrypted'),
|
|
|
|
bSigned = mDom.hasClass('signed'),
|
|
|
|
oVerControl = null,
|
2016-04-07 20:07:19 +08:00
|
|
|
aRecipients = oRainLoopMessage ? oRainLoopMessage.getEmails(['from', 'to', 'cc']) : [],
|
2015-06-23 05:33:27 +08:00
|
|
|
sData = ''
|
|
|
|
;
|
|
|
|
|
|
|
|
if (bEncrypted || bSigned)
|
|
|
|
{
|
|
|
|
sData = mDom.text();
|
|
|
|
mDom.data('openpgp-original', sData);
|
|
|
|
|
|
|
|
if (bEncrypted)
|
|
|
|
{
|
|
|
|
oVerControl = $('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
|
|
|
|
.attr('title', Translator.i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC'));
|
|
|
|
|
|
|
|
oVerControl.on('click', function () {
|
|
|
|
if ($(this).hasClass('success'))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-27 02:04:50 +08:00
|
|
|
var oMessage = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
oMessage = self.openpgp.message.readArmored(sData);
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
Utils.log(e);
|
2015-09-01 04:13:59 +08:00
|
|
|
}
|
2015-08-27 02:04:50 +08:00
|
|
|
|
2015-06-23 05:33:27 +08:00
|
|
|
if (oMessage && oMessage.getText && oMessage.verify && oMessage.decrypt)
|
|
|
|
{
|
2015-09-18 03:48:52 +08:00
|
|
|
self.decryptMessage(oMessage, aRecipients, function (oValidPrivateKey, oDecriptedMessage, oValidPublicKey, aSigningKeyIds) {
|
2015-06-23 05:33:27 +08:00
|
|
|
|
|
|
|
if (oDecriptedMessage)
|
|
|
|
{
|
|
|
|
if (oValidPublicKey)
|
|
|
|
{
|
|
|
|
self.controlsHelper(mDom, oVerControl, true, Translator.i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
|
|
|
'USER': oValidPublicKey.user + ' (' + oValidPublicKey.id + ')'
|
|
|
|
}), oDecriptedMessage.getText());
|
|
|
|
}
|
|
|
|
else if (oValidPrivateKey)
|
|
|
|
{
|
|
|
|
var
|
|
|
|
aKeyIds = Utils.isNonEmptyArray(aSigningKeyIds) ? aSigningKeyIds : null,
|
|
|
|
sAdditional = aKeyIds ? _.compact(_.map(aKeyIds, function (oItem) {
|
|
|
|
return oItem && oItem.toHex ? oItem.toHex() : null;
|
|
|
|
})).join(', ') : ''
|
|
|
|
;
|
|
|
|
|
|
|
|
self.controlsHelper(mDom, oVerControl, false,
|
|
|
|
Translator.i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') +
|
|
|
|
(sAdditional ? ' (' + sAdditional + ')' : ''),
|
|
|
|
oDecriptedMessage.getText());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self.controlsHelper(mDom, oVerControl, false,
|
|
|
|
Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self.controlsHelper(mDom, oVerControl, false,
|
|
|
|
Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.controlsHelper(mDom, oVerControl, false, Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
|
|
|
return false;
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if (bSigned)
|
|
|
|
{
|
|
|
|
oVerControl = $('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
|
|
|
|
.attr('title', Translator.i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC'));
|
|
|
|
|
|
|
|
oVerControl.on('click', function () {
|
|
|
|
|
|
|
|
if ($(this).hasClass('success') || $(this).hasClass('error'))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-27 02:04:50 +08:00
|
|
|
var oMessage = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
oMessage = self.openpgp.cleartext.readArmored(sData);
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
Utils.log(e);
|
2015-09-01 04:13:59 +08:00
|
|
|
}
|
2015-08-27 02:04:50 +08:00
|
|
|
|
2015-06-23 05:33:27 +08:00
|
|
|
if (oMessage && oMessage.getText && oMessage.verify)
|
|
|
|
{
|
|
|
|
self.verifyMessage(oMessage, function (oValidKey, aSigningKeyIds) {
|
|
|
|
if (oValidKey)
|
|
|
|
{
|
|
|
|
self.controlsHelper(mDom, oVerControl, true, Translator.i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
|
|
|
'USER': oValidKey.user + ' (' + oValidKey.id + ')'
|
|
|
|
}), oMessage.getText());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var
|
|
|
|
aKeyIds = Utils.isNonEmptyArray(aSigningKeyIds) ? aSigningKeyIds : null,
|
|
|
|
sAdditional = aKeyIds ? _.compact(_.map(aKeyIds, function (oItem) {
|
|
|
|
return oItem && oItem.toHex ? oItem.toHex() : null;
|
|
|
|
})).join(', ') : ''
|
|
|
|
;
|
|
|
|
|
|
|
|
self.controlsHelper(mDom, oVerControl, false,
|
|
|
|
Translator.i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') +
|
|
|
|
(sAdditional ? ' (' + sAdditional + ')' : ''));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.controlsHelper(mDom, oVerControl, false, Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oVerControl)
|
|
|
|
{
|
|
|
|
mDom.before(oVerControl).before('<div></div>');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-01 23:44:44 +08:00
|
|
|
module.exports = new PgpUserStore();
|
|
|
|
|
|
|
|
}());
|
|
|
|
|