diff --git a/core/server/services/settings/settings-bread-service.js b/core/server/services/settings/settings-bread-service.js index f0f1cb2ba00b..e2e138d16935 100644 --- a/core/server/services/settings/settings-bread-service.js +++ b/core/server/services/settings/settings-bread-service.js @@ -297,7 +297,7 @@ class SettingsBREADService { if (EMAIL_KEYS.includes(setting.key)) { const email = setting.value; const key = setting.key; - const hasChanged = getSetting(setting) !== email; + const hasChanged = getSetting(setting).value !== email; if (await this.requiresEmailVerification({email, hasChanged})) { emailsToVerify.push({email, key}); diff --git a/test/e2e-api/admin/__snapshots__/settings.test.js.snap b/test/e2e-api/admin/__snapshots__/settings.test.js.snap index 7f57a0feab43..7727c5620c1c 100644 --- a/test/e2e-api/admin/__snapshots__/settings.test.js.snap +++ b/test/e2e-api/admin/__snapshots__/settings.test.js.snap @@ -916,6 +916,295 @@ Object { } `; +exports[`Settings API Edit does not trigger email verification flow if members_support_address remains the same 1: [body] 1`] = ` +Object { + "meta": Object {}, + "settings": Array [ + Object { + "key": "title", + "value": "[]", + }, + Object { + "key": "description", + "value": "Thoughts, stories and ideas", + }, + Object { + "key": "logo", + "value": "", + }, + Object { + "key": "cover_image", + "value": "https://static.ghost.org/v4.0.0/images/publication-cover.jpg", + }, + Object { + "key": "icon", + "value": "http://127.0.0.1:2369/content/images/size/w256h256/2019/07/icon.png", + }, + Object { + "key": "accent_color", + "value": "#FF1A75", + }, + Object { + "key": "locale", + "value": "ua", + }, + Object { + "key": "timezone", + "value": "Pacific/Auckland", + }, + Object { + "key": "codeinjection_head", + "value": null, + }, + Object { + "key": "codeinjection_foot", + "value": "", + }, + Object { + "key": "facebook", + "value": "ghost", + }, + Object { + "key": "twitter", + "value": "@ghost", + }, + Object { + "key": "navigation", + "value": "[{\\"label\\":\\"label1\\"}]", + }, + Object { + "key": "secondary_navigation", + "value": "[{\\"label\\":\\"Data & privacy\\",\\"url\\":\\"/privacy/\\"},{\\"label\\":\\"Contact\\",\\"url\\":\\"/contact/\\"},{\\"label\\":\\"Contribute →\\",\\"url\\":\\"/contribute/\\"}]", + }, + Object { + "key": "meta_title", + "value": "SEO title", + }, + Object { + "key": "meta_description", + "value": "SEO description", + }, + Object { + "key": "og_image", + "value": "http://127.0.0.1:2369/content/images/2019/07/facebook.png", + }, + Object { + "key": "og_title", + "value": "facebook title", + }, + Object { + "key": "og_description", + "value": "facebook description", + }, + Object { + "key": "twitter_image", + "value": "http://127.0.0.1:2369/content/images/2019/07/twitter.png", + }, + Object { + "key": "twitter_title", + "value": "twitter title", + }, + Object { + "key": "twitter_description", + "value": "twitter description", + }, + Object { + "key": "active_theme", + "value": "casper", + }, + Object { + "key": "is_private", + "value": false, + }, + Object { + "key": "password", + "value": "", + }, + Object { + "key": "public_hash", + "value": StringMatching /\\[a-z0-9\\]\\{30\\}/, + }, + Object { + "key": "default_content_visibility", + "value": "public", + }, + Object { + "key": "default_content_visibility_tiers", + "value": "[]", + }, + Object { + "key": "members_signup_access", + "value": "all", + }, + Object { + "key": "members_support_address", + "value": "support@example.com", + }, + Object { + "key": "stripe_secret_key", + "value": null, + }, + Object { + "key": "stripe_publishable_key", + "value": null, + }, + Object { + "key": "stripe_plans", + "value": "[]", + }, + Object { + "key": "stripe_connect_publishable_key", + "value": "pk_test_for_stripe", + }, + Object { + "key": "stripe_connect_secret_key", + "value": "••••••••", + }, + Object { + "key": "stripe_connect_livemode", + "value": null, + }, + Object { + "key": "stripe_connect_display_name", + "value": null, + }, + Object { + "key": "stripe_connect_account_id", + "value": null, + }, + Object { + "key": "members_monthly_price_id", + "value": null, + }, + Object { + "key": "members_yearly_price_id", + "value": null, + }, + Object { + "key": "portal_name", + "value": true, + }, + Object { + "key": "portal_button", + "value": true, + }, + Object { + "key": "portal_plans", + "value": "[\\"free\\"]", + }, + Object { + "key": "portal_products", + "value": "[]", + }, + Object { + "key": "portal_button_style", + "value": "icon-and-text", + }, + Object { + "key": "portal_button_icon", + "value": null, + }, + Object { + "key": "portal_button_signup_text", + "value": "Subscribe", + }, + Object { + "key": "mailgun_domain", + "value": null, + }, + Object { + "key": "mailgun_api_key", + "value": null, + }, + Object { + "key": "mailgun_base_url", + "value": null, + }, + Object { + "key": "email_track_opens", + "value": true, + }, + Object { + "key": "email_verification_required", + "value": false, + }, + Object { + "key": "amp", + "value": false, + }, + Object { + "key": "amp_gtag_id", + "value": null, + }, + Object { + "key": "firstpromoter", + "value": false, + }, + Object { + "key": "firstpromoter_id", + "value": null, + }, + Object { + "key": "labs", + "value": "{\\"members\\":true}", + }, + Object { + "key": "slack_url", + "value": "", + }, + Object { + "key": "slack_username", + "value": "New Slack Username", + }, + Object { + "key": "unsplash", + "value": false, + }, + Object { + "key": "shared_views", + "value": "[]", + }, + Object { + "key": "editor_default_email_recipients", + "value": "visibility", + }, + Object { + "key": "editor_default_email_recipients_filter", + "value": "all", + }, + Object { + "key": "members_enabled", + "value": true, + }, + Object { + "key": "members_invite_only", + "value": false, + }, + Object { + "key": "paid_members_enabled", + "value": true, + }, + Object { + "key": "firstpromoter_account", + "value": null, + }, + ], +} +`; + +exports[`Settings API Edit does not trigger email verification flow if members_support_address remains the same 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "3376", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Origin, Accept-Encoding", + "x-cache-invalidate": "/*", + "x-powered-by": "Express", +} +`; + exports[`Settings API Edit editing members_support_address triggers email verification flow 1: [body] 1`] = ` Object { "meta": Object { diff --git a/test/e2e-api/admin/settings.test.js b/test/e2e-api/admin/settings.test.js index 8036ce8422ef..cf263d4cb329 100644 --- a/test/e2e-api/admin/settings.test.js +++ b/test/e2e-api/admin/settings.test.js @@ -167,6 +167,8 @@ describe('Settings API', function () { .matchHeaderSnapshot({ etag: anyEtag }); + + mockManager.assert.sentEmailCount(0); }); it('removes image size prefixes when setting the icon', async function () { @@ -197,6 +199,8 @@ describe('Settings API', function () { // Check if not changed (also check internal ones) const afterValue = settingsCache.get('icon'); assert.equal(afterValue, 'http://127.0.0.1:2369/content/images/2019/07/icon.png'); + + mockManager.assert.sentEmailCount(0); }); it('cannot edit uneditable settings', async function () { @@ -215,6 +219,7 @@ describe('Settings API', function () { const emailVerificationRequired = body.settings.find(setting => setting.key === 'email_verification_required'); assert.strictEqual(emailVerificationRequired.value, false); }); + mockManager.assert.sentEmailCount(0); }); it('editing members_support_address triggers email verification flow', async function () { @@ -237,12 +242,40 @@ describe('Settings API', function () { sent_email_verification: ['members_support_address'] }); }); - + + mockManager.assert.sentEmailCount(1); mockManager.assert.sentEmail({ subject: 'Verify email address', to: 'support@example.com' }); }); + + it('does not trigger email verification flow if members_support_address remains the same', async function () { + await models.Settings.edit({ + key: 'members_support_address', + value: 'support@example.com' + }); + + await agent.put('settings/') + .body({ + settings: [{key: 'members_support_address', value: 'support@example.com'}] + }) + .expectStatus(200) + .matchBodySnapshot({ + settings: matchSettingsArray(CURRENT_SETTINGS_COUNT) + }) + .matchHeaderSnapshot({ + etag: anyEtag + }) + .expect(({body}) => { + const membersSupportAddress = body.settings.find(setting => setting.key === 'members_support_address'); + assert.strictEqual(membersSupportAddress.value, 'support@example.com'); + + assert.deepEqual(body.meta, {}); + }); + + mockManager.assert.sentEmailCount(0); + }); }); describe('verify key update', function () { @@ -263,6 +296,7 @@ describe('Settings API', function () { const membersSupportAddress = body.settings.find(setting => setting.key === 'members_support_address'); assert.strictEqual(membersSupportAddress.value, 'support@example.com'); }); + mockManager.assert.sentEmailCount(0); }); it('cannot update invalid keys via token', async function () {