From 7bcb5c4e156242aa6658ea1c4a31a4d990ca40e8 Mon Sep 17 00:00:00 2001 From: michael-volynets Date: Mon, 9 Dec 2019 17:51:13 +0200 Subject: [PATCH 1/2] Refactored pgp_pubkey.ts file (not ready) --- extension/chrome/elements/pgp_pubkey.ts | 182 +++++++++++++----------- 1 file changed, 97 insertions(+), 85 deletions(-) diff --git a/extension/chrome/elements/pgp_pubkey.ts b/extension/chrome/elements/pgp_pubkey.ts index bb163e98e3e..9c8d1e8a373 100644 --- a/extension/chrome/elements/pgp_pubkey.ts +++ b/extension/chrome/elements/pgp_pubkey.ts @@ -12,68 +12,60 @@ import { BrowserMsg } from '../../js/common/browser/browser-msg.js'; import { Assert } from '../../js/common/assert.js'; import { Xss } from '../../js/common/platform/xss.js'; import { Url } from '../../js/common/core/common.js'; +import { View } from '../../js/common/view.js'; declare const openpgp: typeof OpenPGP; -Catch.try(async () => { - - // todo - this should use KeyImportUI for consistency. Needs general refactoring, hard to follow. - - Ui.event.protect(); - - // minimized means I have to click to see details. Compact means the details take up very little space. - const uncheckedUrlParams = Url.parse(['acctEmail', 'armoredPubkey', 'parentTabId', 'minimized', 'compact', 'frameId']); - const acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); - const parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId'); - const armoredPubkey = Pgp.armor.normalize(Assert.urlParamRequire.string(uncheckedUrlParams, 'armoredPubkey'), 'publicKey'); - const frameId = Assert.urlParamRequire.string(uncheckedUrlParams, 'frameId'); - const compact = uncheckedUrlParams.compact === true; - const minimized = uncheckedUrlParams.minimized === true; - - const { keys: pubs } = await openpgp.key.readArmored(armoredPubkey); - const isUsableButExpired = await Pgp.key.usableButExpired(pubs[0]); - const isExpired = await Pgp.key.expired(pubs[0]); - - const sendResizeMsg = () => { - const desiredHeight = $('#pgp_block').height()! + (compact ? 10 : 30); // #pgp_block is defined in template - BrowserMsg.send.setCss(parentTabId, { selector: `iframe#${frameId}`, css: { height: `${desiredHeight}px` } }); - }; - - const setBtnText = async () => { - if (pubs.length > 1) { - $('.action_add_contact').text('import ' + pubs.length + ' public keys'); - } else { - const [contact] = await Store.dbContactGet(undefined, [String($('.input_email').val())]); - $('.action_add_contact') - .text(contact?.has_pgp ? 'update key' : `import ${isExpired ? 'expired ' : ''}key`) - .css('background-color', isExpired ? '#989898' : ''); - } - }; - - const render = async () => { - $('.pubkey').text(armoredPubkey); - if (compact) { +// todo - this should use KeyImportUI for consistency. +View.run(class PgpPubkeyView extends View { + private readonly acctEmail: string; + private readonly parentTabId: string; + private readonly armoredPubkey: string; + private readonly frameId: string; + private readonly compact: boolean; // means the details take up very little space. + private readonly minimized: boolean; // means I have to click to see details. + private publicKeys: OpenPGP.key.Key[] | undefined; + private isExpired: boolean | undefined; + + constructor() { + super(); + const uncheckedUrlParams = Url.parse(['acctEmail', 'armoredPubkey', 'parentTabId', 'minimized', 'compact', 'frameId']); + this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); + this.parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId'); + this.armoredPubkey = Pgp.armor.normalize(Assert.urlParamRequire.string(uncheckedUrlParams, 'armoredPubkey'), 'publicKey'); + this.frameId = Assert.urlParamRequire.string(uncheckedUrlParams, 'frameId'); + this.compact = uncheckedUrlParams.compact === true; + this.minimized = uncheckedUrlParams.minimized === true; + } + + async render() { + Ui.event.protect(); + this.publicKeys = (await openpgp.key.readArmored(this.armoredPubkey)).keys; + this.isExpired = await Pgp.key.expired(this.publicKeys[0]); + $('.pubkey').text(this.armoredPubkey); + if (this.compact) { $('.hide_if_compact').remove(); $('body').css({ border: 'none', padding: 0 }); $('.line').removeClass('line'); } - $('.line.fingerprints, .line.add_contact').css('display', minimized ? 'none' : 'block'); - if (pubs.length === 1) { - $('.line.fingerprints .fingerprint').text(await Pgp.key.fingerprint(pubs[0], 'spaced') || '(fingerprint error)'); - $('.line.fingerprints .keywords').text(mnemonic(await Pgp.key.longid(pubs[0]) || '') || '(mnemonic error)'); + $('.line.fingerprints, .line.add_contact').css('display', this.minimized ? 'none' : 'block'); + if (this.publicKeys.length === 1) { + $('.line.fingerprints .fingerprint').text(await Pgp.key.fingerprint(this.publicKeys[0], 'spaced') || '(fingerprint error)'); + $('.line.fingerprints .keywords').text(mnemonic(await Pgp.key.longid(this.publicKeys[0]) || '') || '(mnemonic error)'); } else { $('.line.fingerprints').css({ display: 'none' }); } - if (typeof pubs[0] !== 'undefined') { - if (!isUsableButExpired && ! await pubs[0].getEncryptionKey() && ! await pubs[0].getSigningKey()) { - showKeyNotUsableError(); + if (this.publicKeys[0]) { + const isUsableButExpired = await Pgp.key.usableButExpired(this.publicKeys[0]); + if (!isUsableButExpired && ! await this.publicKeys[0].getEncryptionKey() && ! await this.publicKeys[0].getSigningKey()) { + this.showKeyNotUsableError(); } else { - if (compact) { + if (this.compact) { $('.hide_if_compact_and_not_error').remove(); } let emailText = ''; - if (pubs.length === 1) { - const email = pubs[0].users[0].userId ? Str.parseEmail(pubs[0].users[0].userId ? pubs[0].users[0].userId!.userid : '').email : undefined; + if (this.publicKeys.length === 1) { + const email = Str.parseEmail(this.publicKeys[0].users[0].userId?.userid || '').email; if (email) { emailText = email; $('.input_email').val(email); // checked above @@ -82,30 +74,63 @@ Catch.try(async () => { emailText = 'more than one person'; $('.input_email').css({ display: 'none' }); const pubToEmail = (pubkey: OpenPGP.key.Key) => Str.parseEmail(pubkey.users[0].userId ? pubkey.users[0].userId!.userid : '').email; - Xss.sanitizeAppend('.add_contact', Xss.escape(' for ' + pubs.map(pubToEmail).filter(e => !!e).join(', '))); + Xss.sanitizeAppend('.add_contact', Xss.escape(' for ' + this.publicKeys.map(pubToEmail).filter(e => !!e).join(', '))); } Xss.sanitizePrepend('#pgp_block.pgp_pubkey .result', `This message includes a Public Key for .`); $('.pubkey').addClass('good'); - setBtnText().catch(Catch.reportErr); + this.setBtnText().catch(Catch.reportErr); } } else { - let fixed = armoredPubkey; + let fixed = this.armoredPubkey; while (/\n> |\n>\n/.test(fixed)) { fixed = fixed.replace(/\n> /g, '\n').replace(/\n>\n/g, '\n\n'); } - if (fixed !== armoredPubkey) { // try to re-render it after un-quoting, (minimized because it is probably their own pubkey quoted by the other guy) - window.location.href = Url.create('pgp_pubkey.htm', { armoredPubkey: fixed, minimized: true, acctEmail, parentTabId, frameId }); + if (fixed !== this.armoredPubkey) { // try to re-render it after un-quoting, (minimized because it is probably their own pubkey quoted by the other guy) + window.location.href = Url.create('pgp_pubkey.htm', { + armoredPubkey: fixed, minimized: true, + acctEmail: this.acctEmail, parentTabId: this.parentTabId, frameId: this.frameId + }); } else { - showKeyNotUsableError(); + this.showKeyNotUsableError(); } } - }; + this.sendResizeMsg(); + } + + setHandlers() { + $('.action_add_contact').click(this.setHandler(btn => this.addContactHandler(btn))); + $('.input_email').keyup(this.setHandler(() => this.setBtnText())); + $('.action_show_full').click(this.setHandler(btn => this.showFullKeyHandler(btn))); + } + + private sendResizeMsg() { + const desiredHeight = $('#pgp_block').height()! + (this.compact ? 10 : 30); // #pgp_block is defined in template + BrowserMsg.send.setCss(this.parentTabId, { selector: `iframe#${this.frameId}`, css: { height: `${desiredHeight}px` } }); + } + + private async setBtnText() { + if (this.publicKeys!.length > 1) { + $('.action_add_contact').text('import ' + this.publicKeys!.length + ' public keys'); + } else { + const [contact] = await Store.dbContactGet(undefined, [String($('.input_email').val())]); + $('.action_add_contact') + .text(contact?.has_pgp ? 'update key' : `import ${this.isExpired ? 'expired ' : ''}key`) + .css('background-color', this.isExpired ? '#989898' : ''); + } + } - $('.action_add_contact').click(Ui.event.handle(async target => { - if (pubs.length > 1) { + private showKeyNotUsableError() { + $('.fingerprints, .add_contact').remove(); + $('#pgp_block.pgp_pubkey .result') + .prepend('This OpenPGP key is not usable.'); // xss-direct + $('.pubkey').addClass('bad'); + } + + private async addContactHandler(addContactBtn: HTMLElement) { + if (this.publicKeys!.length > 1) { const contacts: Contact[] = []; - for (const pubkey of pubs) { - const email = Str.parseEmail(pubkey.users[0].userId ? pubkey.users[0].userId!.userid : '').email; + for (const pubkey of this.publicKeys!) { + const email = Str.parseEmail(pubkey.users[0].userId?.userid || '').email; if (email) { contacts.push(await Store.dbContactObj({ email, client: 'pgp', pubkey: pubkey.armor(), lastUse: Date.now(), lastSig: await Pgp.key.lastSig(pubkey), @@ -114,42 +139,29 @@ Catch.try(async () => { } } await Store.dbContactSave(undefined, contacts); - Xss.sanitizeReplace(target, 'added public keys'); - BrowserMsg.send.addToContacts(parentTabId); + Xss.sanitizeReplace(addContactBtn, 'added public keys'); + BrowserMsg.send.addToContacts(this.parentTabId); $('.input_email').remove(); - } else if (pubs.length) { + } else if (this.publicKeys!.length) { if (Str.isEmailValid(String($('.input_email').val()))) { const contact = await Store.dbContactObj({ - email: String($('.input_email').val()), client: 'pgp', pubkey: pubs[0].armor(), lastUse: Date.now(), - lastSig: await Pgp.key.lastSig(pubs[0]), expiresOn: await Pgp.key.dateBeforeExpiration(pubs[0]) + email: String($('.input_email').val()), client: 'pgp', pubkey: this.publicKeys![0].armor(), lastUse: Date.now(), + lastSig: await Pgp.key.lastSig(this.publicKeys![0]), expiresOn: await Pgp.key.dateBeforeExpiration(this.publicKeys![0]) }); await Store.dbContactSave(undefined, contact); - BrowserMsg.send.addToContacts(parentTabId); - Xss.sanitizeReplace(target, `${Xss.escape(String($('.input_email').val()))} added`); + BrowserMsg.send.addToContacts(this.parentTabId); + Xss.sanitizeReplace(addContactBtn, `${Xss.escape(String($('.input_email').val()))} added`); $('.input_email').remove(); } else { await Ui.modal.error('This email is invalid, please check for typos. Not added.'); $('.input_email').focus(); } } - })); - - const showKeyNotUsableError = () => { - $('.fingerprints, .add_contact').remove(); - $('#pgp_block.pgp_pubkey .result') - .prepend('This OpenPGP key is not usable.'); // xss-direct - $('.pubkey').addClass('bad'); - }; + } - $('.input_email').keyup(() => setBtnText()); - - $('.action_show_full').click(Ui.event.handle(target => { - $(target).css('display', 'none'); + private showFullKeyHandler(showFullBtn: HTMLElement) { + $(showFullBtn).css('display', 'none'); $('pre.pubkey, .line.fingerprints, .line.add_contact').css('display', 'block'); - sendResizeMsg(); - })); - - await render(); - sendResizeMsg(); - -})(); + this.sendResizeMsg(); + } +}); From c959a83b5ce085e32ff51ce909fc6a0186a2f2df Mon Sep 17 00:00:00 2001 From: michael-volynets Date: Mon, 9 Dec 2019 21:05:23 +0200 Subject: [PATCH 2/2] Issue #2239, Save first key in the variable --- extension/chrome/elements/pgp_pubkey.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/extension/chrome/elements/pgp_pubkey.ts b/extension/chrome/elements/pgp_pubkey.ts index 9c8d1e8a373..a919103f6e5 100644 --- a/extension/chrome/elements/pgp_pubkey.ts +++ b/extension/chrome/elements/pgp_pubkey.ts @@ -25,6 +25,7 @@ View.run(class PgpPubkeyView extends View { private readonly compact: boolean; // means the details take up very little space. private readonly minimized: boolean; // means I have to click to see details. private publicKeys: OpenPGP.key.Key[] | undefined; + private primaryPubKey: OpenPGP.key.Key | undefined; private isExpired: boolean | undefined; constructor() { @@ -41,7 +42,8 @@ View.run(class PgpPubkeyView extends View { async render() { Ui.event.protect(); this.publicKeys = (await openpgp.key.readArmored(this.armoredPubkey)).keys; - this.isExpired = await Pgp.key.expired(this.publicKeys[0]); + this.primaryPubKey = this.publicKeys[0]; + this.isExpired = await Pgp.key.expired(this.primaryPubKey); $('.pubkey').text(this.armoredPubkey); if (this.compact) { $('.hide_if_compact').remove(); @@ -50,14 +52,14 @@ View.run(class PgpPubkeyView extends View { } $('.line.fingerprints, .line.add_contact').css('display', this.minimized ? 'none' : 'block'); if (this.publicKeys.length === 1) { - $('.line.fingerprints .fingerprint').text(await Pgp.key.fingerprint(this.publicKeys[0], 'spaced') || '(fingerprint error)'); - $('.line.fingerprints .keywords').text(mnemonic(await Pgp.key.longid(this.publicKeys[0]) || '') || '(mnemonic error)'); + $('.line.fingerprints .fingerprint').text(await Pgp.key.fingerprint(this.primaryPubKey, 'spaced') || '(fingerprint error)'); + $('.line.fingerprints .keywords').text(mnemonic(await Pgp.key.longid(this.primaryPubKey) || '') || '(mnemonic error)'); } else { $('.line.fingerprints').css({ display: 'none' }); } - if (this.publicKeys[0]) { - const isUsableButExpired = await Pgp.key.usableButExpired(this.publicKeys[0]); - if (!isUsableButExpired && ! await this.publicKeys[0].getEncryptionKey() && ! await this.publicKeys[0].getSigningKey()) { + if (this.primaryPubKey) { + const isUsableButExpired = await Pgp.key.usableButExpired(this.primaryPubKey); + if (!isUsableButExpired && ! await this.primaryPubKey.getEncryptionKey() && ! await this.primaryPubKey.getSigningKey()) { this.showKeyNotUsableError(); } else { if (this.compact) { @@ -65,7 +67,7 @@ View.run(class PgpPubkeyView extends View { } let emailText = ''; if (this.publicKeys.length === 1) { - const email = Str.parseEmail(this.publicKeys[0].users[0].userId?.userid || '').email; + const email = Str.parseEmail(this.primaryPubKey.users[0].userId?.userid || '').email; if (email) { emailText = email; $('.input_email').val(email); // checked above