From 970741cf5def06c0314480432da07f3df8373a46 Mon Sep 17 00:00:00 2001 From: Sag Date: Mon, 20 Jan 2025 22:12:55 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20Blocked=20spammy=20email=20domai?= =?UTF-8?q?ns=20in=20member=20signups=20(#22027)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/ghost/issue/ONC-721 ref https://app.incident.io/ghost/incidents/132 - added a blocklist at the email domain level for free member signups - for example, if `blocked-domain.com` is blocked, `thomas@blocked-domain.com` cannot sign up as free member - the blocklist is configurable: `"spam.blocked_email_domains": ["blocked-domain.com"]` --- apps/portal/src/utils/errors.js | 1 + .../core/core/server/services/members/api.js | 4 +- .../newsletters/NewslettersService.js | 4 +- .../services/settings/SettingsBREADService.js | 4 +- ghost/core/core/shared/config/defaults.json | 3 +- .../send-magic-link.test.js.snap | 18 +++++++ .../e2e-api/members/send-magic-link.test.js | 20 ++++++++ ghost/i18n/locales/af/portal.json | 1 + ghost/i18n/locales/ar/portal.json | 1 + ghost/i18n/locales/bg/portal.json | 1 + ghost/i18n/locales/bn/portal.json | 1 + ghost/i18n/locales/bs/portal.json | 1 + ghost/i18n/locales/ca/portal.json | 1 + ghost/i18n/locales/context.json | 1 + ghost/i18n/locales/cs/portal.json | 1 + ghost/i18n/locales/da/portal.json | 1 + ghost/i18n/locales/de-CH/portal.json | 1 + ghost/i18n/locales/de/portal.json | 1 + ghost/i18n/locales/el/portal.json | 1 + ghost/i18n/locales/en/portal.json | 1 + ghost/i18n/locales/eo/portal.json | 1 + ghost/i18n/locales/es/portal.json | 1 + ghost/i18n/locales/et/portal.json | 1 + ghost/i18n/locales/fa/portal.json | 1 + ghost/i18n/locales/fi/portal.json | 1 + ghost/i18n/locales/fr/portal.json | 1 + ghost/i18n/locales/gd/portal.json | 1 + ghost/i18n/locales/he/portal.json | 1 + ghost/i18n/locales/hi/portal.json | 1 + ghost/i18n/locales/hr/portal.json | 1 + ghost/i18n/locales/hu/portal.json | 1 + ghost/i18n/locales/id/portal.json | 1 + ghost/i18n/locales/is/portal.json | 1 + ghost/i18n/locales/it/portal.json | 1 + ghost/i18n/locales/ja/portal.json | 1 + ghost/i18n/locales/ko/portal.json | 1 + ghost/i18n/locales/kz/portal.json | 1 + ghost/i18n/locales/lt/portal.json | 1 + ghost/i18n/locales/lv/portal.json | 1 + ghost/i18n/locales/mk/portal.json | 1 + ghost/i18n/locales/mn/portal.json | 1 + ghost/i18n/locales/ms/portal.json | 1 + ghost/i18n/locales/ne/portal.json | 1 + ghost/i18n/locales/nl/portal.json | 1 + ghost/i18n/locales/nn/portal.json | 1 + ghost/i18n/locales/no/portal.json | 1 + ghost/i18n/locales/pl/portal.json | 1 + ghost/i18n/locales/pt-BR/portal.json | 1 + ghost/i18n/locales/pt/portal.json | 1 + ghost/i18n/locales/ro/portal.json | 1 + ghost/i18n/locales/ru/portal.json | 1 + ghost/i18n/locales/si/portal.json | 1 + ghost/i18n/locales/sk/portal.json | 1 + ghost/i18n/locales/sl/portal.json | 1 + ghost/i18n/locales/sq/portal.json | 1 + ghost/i18n/locales/sr-Cyrl/portal.json | 1 + ghost/i18n/locales/sr/portal.json | 1 + ghost/i18n/locales/sv/portal.json | 1 + ghost/i18n/locales/sw/portal.json | 1 + ghost/i18n/locales/ta/portal.json | 1 + ghost/i18n/locales/th/portal.json | 1 + ghost/i18n/locales/tr/portal.json | 1 + ghost/i18n/locales/uk/portal.json | 1 + ghost/i18n/locales/ur/portal.json | 1 + ghost/i18n/locales/uz/portal.json | 1 + ghost/i18n/locales/vi/portal.json | 1 + ghost/i18n/locales/zh-Hant/portal.json | 1 + ghost/i18n/locales/zh/portal.json | 1 + ghost/magic-link/lib/MagicLink.js | 32 +++++++++++- ghost/magic-link/test/index.test.js | 51 +++++++++++++++++++ ghost/members-api/lib/members-api.js | 6 ++- 71 files changed, 196 insertions(+), 8 deletions(-) diff --git a/apps/portal/src/utils/errors.js b/apps/portal/src/utils/errors.js index c48e3716bfc8..93034e1cc981 100644 --- a/apps/portal/src/utils/errors.js +++ b/apps/portal/src/utils/errors.js @@ -59,6 +59,7 @@ export function chooseBestErrorMessage(error, alreadyTranslatedDefaultMessage, t t('Too many different sign-in attempts, try again in {{number}} days'); t('Failed to send magic link email'); t('This site only accepts paid members.'); + t('This email domain is not accepted, try again with a different email address'); } }; diff --git a/ghost/core/core/server/services/members/api.js b/ghost/core/core/server/services/members/api.js index 6b65cf6bd8f1..26f5d46d7caf 100644 --- a/ghost/core/core/server/services/members/api.js +++ b/ghost/core/core/server/services/members/api.js @@ -20,6 +20,7 @@ const memberAttributionService = require('../member-attribution'); const emailSuppressionList = require('../email-suppression-list'); const {t} = require('../i18n'); const sentry = require('../../../shared/sentry'); +const sharedConfig = require('../../../shared/config'); const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000; const MAGIC_LINK_TOKEN_VALIDITY_AFTER_USAGE = 10 * 60 * 1000; @@ -238,7 +239,8 @@ function createApiInstance(config) { emailSuppressionList, settingsCache, sentry, - settingsHelpers + settingsHelpers, + config: sharedConfig }); return membersApiInstance; diff --git a/ghost/core/core/server/services/newsletters/NewslettersService.js b/ghost/core/core/server/services/newsletters/NewslettersService.js index 5247c69c93bb..bf9082e96a7a 100644 --- a/ghost/core/core/server/services/newsletters/NewslettersService.js +++ b/ghost/core/core/server/services/newsletters/NewslettersService.js @@ -6,6 +6,7 @@ const debug = require('@tryghost/debug')('services:newsletters'); const tpl = require('@tryghost/tpl'); const errors = require('@tryghost/errors'); const sentry = require('../../../shared/sentry'); +const config = require('../../../shared/config'); const messages = { nameAlreadyExists: 'A newsletter with the same name already exists', @@ -86,7 +87,8 @@ class NewslettersService { getText, getHTML, getSubject, - sentry + sentry, + config }); } diff --git a/ghost/core/core/server/services/settings/SettingsBREADService.js b/ghost/core/core/server/services/settings/SettingsBREADService.js index 0638920c510a..bd93c0d7d9a4 100644 --- a/ghost/core/core/server/services/settings/SettingsBREADService.js +++ b/ghost/core/core/server/services/settings/SettingsBREADService.js @@ -6,6 +6,7 @@ const logging = require('@tryghost/logging'); const MagicLink = require('@tryghost/magic-link'); const verifyEmailTemplate = require('./emails/verify-email'); const sentry = require('../../../shared/sentry'); +const config = require('../../../shared/config'); const EMAIL_KEYS = ['members_support_address']; const messages = { @@ -82,7 +83,8 @@ class SettingsBREADService { getText, getHTML, getSubject, - sentry + sentry, + config }); } diff --git a/ghost/core/core/shared/config/defaults.json b/ghost/core/core/shared/config/defaults.json index 049665626498..2c1220b8d814 100644 --- a/ghost/core/core/shared/config/defaults.json +++ b/ghost/core/core/shared/config/defaults.json @@ -126,7 +126,8 @@ "maxWait": 360000, "lifetime": 3600, "freeRetries": 10 - } + }, + "blocked_email_domains": [] }, "caching": { "frontend": { diff --git a/ghost/core/test/e2e-api/members/__snapshots__/send-magic-link.test.js.snap b/ghost/core/test/e2e-api/members/__snapshots__/send-magic-link.test.js.snap index e8e0a5499ec3..1305fc1af3d1 100644 --- a/ghost/core/test/e2e-api/members/__snapshots__/send-magic-link.test.js.snap +++ b/ghost/core/test/e2e-api/members/__snapshots__/send-magic-link.test.js.snap @@ -107,3 +107,21 @@ Object { ], } `; + +exports[`sendMagicLink blocks signups from blocked email domains 1: [body] 1`] = ` +Object { + "errors": Array [ + Object { + "code": null, + "context": null, + "details": null, + "ghostErrorCode": null, + "help": null, + "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "message": "This email domain is not accepted, try again with a different email address", + "property": null, + "type": "BadRequestError", + }, + ], +} +`; diff --git a/ghost/core/test/e2e-api/members/send-magic-link.test.js b/ghost/core/test/e2e-api/members/send-magic-link.test.js index f353f37fb133..88eefe3869d0 100644 --- a/ghost/core/test/e2e-api/members/send-magic-link.test.js +++ b/ghost/core/test/e2e-api/members/send-magic-link.test.js @@ -4,6 +4,7 @@ const settingsCache = require('../../../core/shared/settings-cache'); const DomainEvents = require('@tryghost/domain-events'); const {anyErrorId} = matchers; const spamPrevention = require('../../../core/server/web/shared/middleware/api/spam-prevention'); +const configUtils = require('../../utils/configUtils'); let membersAgent, membersService; @@ -29,6 +30,7 @@ describe('sendMagicLink', function () { afterEach(function () { mockManager.restore(); + configUtils.restore(); }); it('Errors when passed multiple emails', async function () { @@ -285,4 +287,22 @@ describe('sendMagicLink', function () { } }); }); + + it('blocks signups from blocked email domains', async function () { + configUtils.set('spam:blocked_email_domains', ['blocked-domain.com']); + + const email = 'this-member-does-not-exist@blocked-domain.com'; + await membersAgent.post('/api/send-magic-link') + .body({ + email, + emailType: 'signup' + }) + .expectStatus(400) + .matchBodySnapshot({ + errors: [{ + id: anyErrorId, + message: 'This email domain is not accepted, try again with a different email address' + }] + }); + }); }); diff --git a/ghost/i18n/locales/af/portal.json b/ghost/i18n/locales/af/portal.json index bacfa41c0daf..90cc19596033 100644 --- a/ghost/i18n/locales/af/portal.json +++ b/ghost/i18n/locales/af/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Hierdie webwerf is slegs op uitnodiging, kontak die eienaar vir toegang.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ar/portal.json b/ghost/i18n/locales/ar/portal.json index 2be918d4fe97..e781b56499e0 100644 --- a/ghost/i18n/locales/ar/portal.json +++ b/ghost/i18n/locales/ar/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": ".حدث خطأ أثناء الاشتراك، يرجى المحاولة مرة أخرى", "There was an error processing your payment. Please try again.": ".حدث خطأ أثناء معالجة دفعك، يرجى المحاولة مرة أخرى", "There was an error sending the email, please try again": ".حدث خطأ أثناء إرسال البريد الاكتروني، يرجى المحاولة مرة أخرى", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "هذا الموقع للمشتركين فقط، تواصل مع ادارة الموقع للحصول على اشتراك.", "This site is not accepting payments at the moment.": "هذا الموقع لا يقبل المدفوعات في الوقت الحالي", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/bg/portal.json b/ghost/i18n/locales/bg/portal.json index 6df78718e14d..3960c1dd0f2a 100644 --- a/ghost/i18n/locales/bg/portal.json +++ b/ghost/i18n/locales/bg/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Възникна грешка при продължаването на абонамента ви, опитайте отново.", "There was an error processing your payment. Please try again.": "Възникна грешка при обработката на вашето плащане. Моля, опитайте отново.", "There was an error sending the email, please try again": "Възникна грешка при изпращане на имейл, опитайте отново", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Сайтът е само с покани. Свържете се със собственика за да получите достъп.", "This site is not accepting payments at the moment.": "В момента сайтът не приема плащания.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/bn/portal.json b/ghost/i18n/locales/bn/portal.json index 4dbf4485a4e7..a89cfc896826 100644 --- a/ghost/i18n/locales/bn/portal.json +++ b/ghost/i18n/locales/bn/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "এই সাইটটি কেবল আমন্ত্রণের মাধ্যমে, প্রবেশাধিকার পেতে মালিকের সাথে যোগাযোগ করুন।", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/bs/portal.json b/ghost/i18n/locales/bs/portal.json index 5d2bfa3bd848..0d2da7661caa 100644 --- a/ghost/i18n/locales/bs/portal.json +++ b/ghost/i18n/locales/bs/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ova je stranica samo na poziv, kontaktiraj vlasnika za pristup.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ca/portal.json b/ghost/i18n/locales/ca/portal.json index d17417d828a9..16f7031facd2 100644 --- a/ghost/i18n/locales/ca/portal.json +++ b/ghost/i18n/locales/ca/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Aquest llog és només per invitació, contacta amb el propietari per obtenir accés.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/context.json b/ghost/i18n/locales/context.json index c9ae0468fde7..9d845830c9cf 100644 --- a/ghost/i18n/locales/context.json +++ b/ghost/i18n/locales/context.json @@ -242,6 +242,7 @@ "This comment has been hidden.": "Text for a comment thas was hidden", "This comment has been removed.": "Text for a comment thas was removed", "This email address will not be used.": "This is in the footer of signup verification emails, and comes right after 'If you did not make this request, you can simply delete this message.'", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "A message on the member login screen indicating that a site is not-open to public signups", "This site is not accepting payments at the moment.": "An error message shown when a tips or donations link is opened but the site has donations disabled", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/cs/portal.json b/ghost/i18n/locales/cs/portal.json index 36f707e7d81b..f4a806b16078 100644 --- a/ghost/i18n/locales/cs/portal.json +++ b/ghost/i18n/locales/cs/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "Při zpracování vaší platby došlo k chybě. Zkuste to prosím znovu.", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Tento web je pouze pro pozvané, kontaktujte provozovatele pro přístup.", "This site is not accepting payments at the moment.": "Tento web momentálně nepřijímá platby.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/da/portal.json b/ghost/i18n/locales/da/portal.json index b8f3f329f89c..03c1eb8c7880 100644 --- a/ghost/i18n/locales/da/portal.json +++ b/ghost/i18n/locales/da/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Der opstod en fejl under forlængelsen af dit abonnement, prøv venligst igen.", "There was an error processing your payment. Please try again.": "Der opstod en fejl under behandlingen af din betaling. Prøv venligst igen.", "There was an error sending the email, please try again": "Der opstod en fejl under afsendelse af e-mailen, prøv venligst igen.", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Denne sider kræver at du skal være inviteret. Kontakt ejeres for at få adgang.", "This site is not accepting payments at the moment.": "Denne side accepterer ikke betalinger i øjeblikket.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/de-CH/portal.json b/ghost/i18n/locales/de-CH/portal.json index 8d3bb9548256..03895b4d89de 100644 --- a/ghost/i18n/locales/de-CH/portal.json +++ b/ghost/i18n/locales/de-CH/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Der Zugang zu diesem Inhalt ist eingeschränkt. Bitte kontaktieren Sie uns, wenn Sie Zugang wünschen.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/de/portal.json b/ghost/i18n/locales/de/portal.json index 83d7591b7d47..7fe5353ff085 100644 --- a/ghost/i18n/locales/de/portal.json +++ b/ghost/i18n/locales/de/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Beim Erneuern deines Abonnements ist ein Fehler aufgetreten. Bitte versuche es erneut.", "There was an error processing your payment. Please try again.": "Bei der Verarbeitung deiner Zahlung gab es einen Fehler. Bitte versuche es noch einmal.", "There was an error sending the email, please try again": "Beim Versand der E-Mail ist ein Fehler aufgetreten. Bitte versuche es erneut.", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Für diese Seite benötigst du eine Einladung. Bitte kontaktiere den Inhaber.", "This site is not accepting payments at the moment.": "Diese Website nimmt zur Zeit keine Zahlungen entgegen.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/el/portal.json b/ghost/i18n/locales/el/portal.json index bc56a279929d..98a6143e0b49 100644 --- a/ghost/i18n/locales/el/portal.json +++ b/ghost/i18n/locales/el/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Αυτός ο ιστότοπος είναι μόνο με πρόσκληση, επικοινωνήστε με τον ιδιοκτήτη για πρόσβαση.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/en/portal.json b/ghost/i18n/locales/en/portal.json index 54638995cf52..508f16c34662 100644 --- a/ghost/i18n/locales/en/portal.json +++ b/ghost/i18n/locales/en/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/eo/portal.json b/ghost/i18n/locales/eo/portal.json index 760b2ebe312b..7f5724bdf9a5 100644 --- a/ghost/i18n/locales/eo/portal.json +++ b/ghost/i18n/locales/eo/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ĉi tiu retejo estas nur por invitiĝuloj, kontaktu la proprietulo por alireblo.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/es/portal.json b/ghost/i18n/locales/es/portal.json index 5e50595327c1..0dd42ceae6f9 100644 --- a/ghost/i18n/locales/es/portal.json +++ b/ghost/i18n/locales/es/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Hubo un error en continuar la suscripción, inténtalo de nuevo por favor.", "There was an error processing your payment. Please try again.": "Hubo un error procesando tu pago. Intentalo de nuevvo por favor.", "There was an error sending the email, please try again": "Hubo un error enviando el correo electrónico, intentalo de nuevo por favor.", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Este sitio es solo por invitación, contacta al propietario para obtener acceso.", "This site is not accepting payments at the moment.": "Este sitio no acepta pagos en este momento.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/et/portal.json b/ghost/i18n/locales/et/portal.json index 7eb3a3485d19..5d70c09a7db1 100644 --- a/ghost/i18n/locales/et/portal.json +++ b/ghost/i18n/locales/et/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "See sait on ainult kutsetega, juurdepääsu saamiseks võtke ühendust omanikuga.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/fa/portal.json b/ghost/i18n/locales/fa/portal.json index c73468136c5f..c8d227e1b70b 100644 --- a/ghost/i18n/locales/fa/portal.json +++ b/ghost/i18n/locales/fa/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "دسترسی به این وب\u200cسایت نیازمند دعوت\u200cنامه است، با مالک آن برای دریافت دسترسی تماس بگیرید.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/fi/portal.json b/ghost/i18n/locales/fi/portal.json index 62ec5e3b4ed2..a7714935494d 100644 --- a/ghost/i18n/locales/fi/portal.json +++ b/ghost/i18n/locales/fi/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Tämä sivu on vain kutsutuille, ota yhteyttä omistajaan saadaksesi pääsyoikeuden.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/fr/portal.json b/ghost/i18n/locales/fr/portal.json index 22f7eb2a2287..21e6f2b607e5 100644 --- a/ghost/i18n/locales/fr/portal.json +++ b/ghost/i18n/locales/fr/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Une erreur s'est produite lors de la prolongation de votre abonnement, veuillez réessayer.", "There was an error processing your payment. Please try again.": "Une erreur s'est produite lors du traitement de votre paiement. Veuillez réessayer.", "There was an error sending the email, please try again": "Une erreur s'est produite lors de l'envoi de l'e-mail, veuillez réessayer.", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ce site est réservé aux invités. Veuillez écrire au propriétaire pour en demander l'accès.", "This site is not accepting payments at the moment.": "Ce site n'accepte pas les paiements pour le moment.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/gd/portal.json b/ghost/i18n/locales/gd/portal.json index 90092761438b..007908c4babd 100644 --- a/ghost/i18n/locales/gd/portal.json +++ b/ghost/i18n/locales/gd/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "Thachair mearachd fhad 's a bhathar a' làimhseachadh a' phàighidh agad. Feuch a-rithist an ceann greis.", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Feumar cuireadh airson an làrach-lìn seo, leig fios dhan rianaire ma tha thu ag iarraidh cothrom-inntrigidh.", "This site is not accepting payments at the moment.": "Chan eil an làrach seo a' gabhail ri phàighidhean an-dràsta.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/he/portal.json b/ghost/i18n/locales/he/portal.json index 1241bbd50932..19c9163cf736 100644 --- a/ghost/i18n/locales/he/portal.json +++ b/ghost/i18n/locales/he/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "שגיאה בהמשך המנוי שלכם, נסו שוב.", "There was an error processing your payment. Please try again.": "שגיאה בעיבוד התשלום שלכם. נסו שוב.", "There was an error sending the email, please try again": "שגיאה בשליחת המייל, נסו שוב", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "אתר זה פתוח למוזמנים בלבד, פנו לבעל האתר לגישה.", "This site is not accepting payments at the moment.": "אתר זה לא מקבל תשלומים כרגע.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/hi/portal.json b/ghost/i18n/locales/hi/portal.json index e5c6927dcabf..c3cb361240eb 100644 --- a/ghost/i18n/locales/hi/portal.json +++ b/ghost/i18n/locales/hi/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "यह साइट केवल निमंत्रण द्वारा है, पहुँच के लिए मालिक से संपर्क करें।", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/hr/portal.json b/ghost/i18n/locales/hr/portal.json index 69135f7de322..937fb337dd95 100644 --- a/ghost/i18n/locales/hr/portal.json +++ b/ghost/i18n/locales/hr/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ove stranice su samo za članove, kontaktirajte vlasnika kako biste dobili pristup.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/hu/portal.json b/ghost/i18n/locales/hu/portal.json index de9f7d87bd3e..f304651bcbbe 100644 --- a/ghost/i18n/locales/hu/portal.json +++ b/ghost/i18n/locales/hu/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "A website csak meghívóval látogatható. Meghívóért lépjen kapcsolatba az oldal tulajdonosával!", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/id/portal.json b/ghost/i18n/locales/id/portal.json index bd9c652e0832..5de578cae554 100644 --- a/ghost/i18n/locales/id/portal.json +++ b/ghost/i18n/locales/id/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Terjadi kesalahan saat melanjutkan langganan Anda, harap coba lagi.", "There was an error processing your payment. Please try again.": "Terjadi kesalahan saat memproses pembayaran Anda. Harap coba lagi.", "There was an error sending the email, please try again": "Terjadi kesalahan saat mengirim email, harap coba lagi", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Situs ini hanya untuk yang diundang, hubungi pemiliknya untuk mendapatkan akses.", "This site is not accepting payments at the moment.": "Situs ini tidak menerima pembayaran saat ini.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/is/portal.json b/ghost/i18n/locales/is/portal.json index ef6598309e01..9b5b2a17b8bf 100644 --- a/ghost/i18n/locales/is/portal.json +++ b/ghost/i18n/locales/is/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Aðgangur krefst boðsmiða, hafið samband við eiganda síðunnar til að fá aðgang.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/it/portal.json b/ghost/i18n/locales/it/portal.json index c99667b53316..2e5738f4051a 100644 --- a/ghost/i18n/locales/it/portal.json +++ b/ghost/i18n/locales/it/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "C'è stato un errore nella continuazione del tuo abbonamento, riprova per favore.", "There was an error processing your payment. Please try again.": "C'è stato un errore durante l’elaborazione del tuo pagamento. Riprova per favore.", "There was an error sending the email, please try again": "C'è stato un errore nell'invio dell'e-mail, per favore riprova", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Questo sito è accessibile solo su invito, contatta il proprietario per poter accedere.", "This site is not accepting payments at the moment.": "Questo sito non accetta pagamenti al momento.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ja/portal.json b/ghost/i18n/locales/ja/portal.json index 4e9b53d0f182..9b3182cca723 100644 --- a/ghost/i18n/locales/ja/portal.json +++ b/ghost/i18n/locales/ja/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "このサイトは招待制です。アクセスするにはオーナーに連絡してください。", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ko/portal.json b/ghost/i18n/locales/ko/portal.json index e71b36ac98ef..2fd55603b09c 100644 --- a/ghost/i18n/locales/ko/portal.json +++ b/ghost/i18n/locales/ko/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "구독 계속하기 중 오류가 발생했어요. 다시 시도해 주세요.", "There was an error processing your payment. Please try again.": "결제 처리 중 오류가 발생했어요. 다시 시도해 주세요.", "There was an error sending the email, please try again": "이메일 전송 중 오류가 발생했어요. 다시 시도해 주세요", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "위 사이트는 초대된 사용자만 사용이 가능해요. 접근을 위해서는 관리자에게 연락해 주세요.", "This site is not accepting payments at the moment.": "현재 이 사이트는 결제를 받지 않고 있어요.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/kz/portal.json b/ghost/i18n/locales/kz/portal.json index 8414afa9f024..ce82ba45e3db 100644 --- a/ghost/i18n/locales/kz/portal.json +++ b/ghost/i18n/locales/kz/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Бұл сайтқа тек шақырту бойынша кіруге болады, рұқсат алу үшін иесіне хабарласыңыз.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/lt/portal.json b/ghost/i18n/locales/lt/portal.json index d09d5df89d0e..e05538188dd1 100644 --- a/ghost/i18n/locales/lt/portal.json +++ b/ghost/i18n/locales/lt/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ši svetainė pasiekiama tik su pakvietimu, susisiekite su savininku dėl prieigos. ", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/lv/portal.json b/ghost/i18n/locales/lv/portal.json index a0b1eff46da5..190adb36072e 100644 --- a/ghost/i18n/locales/lv/portal.json +++ b/ghost/i18n/locales/lv/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Turpinot abonementu, radās kļūda. Lūdzu, mēģiniet vēlreiz.", "There was an error processing your payment. Please try again.": "Apstrādājot jūsu maksājumu, radās kļūda. Lūdzu, mēģiniet vēlreiz.", "There was an error sending the email, please try again": "Nosūtot e-pasta ziņojumu, radās kļūda. Lūdzu, mēģiniet vēlreiz", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Šī vietne ir paredzēta tikai ielūgumam. Lai iegūtu piekļuvi, sazinieties ar īpašnieku.", "This site is not accepting payments at the moment.": "Šī vietne pašlaik nepieņem maksājumus.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/mk/portal.json b/ghost/i18n/locales/mk/portal.json index 0c9831b3116d..3451e7300d6e 100644 --- a/ghost/i18n/locales/mk/portal.json +++ b/ghost/i18n/locales/mk/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Оваа страница е достапна само со покана. За пристап контактирајте го сопственикот.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/mn/portal.json b/ghost/i18n/locales/mn/portal.json index e949b2027436..97d23717539d 100644 --- a/ghost/i18n/locales/mn/portal.json +++ b/ghost/i18n/locales/mn/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Энэхүү сайт руу зөвхөн урилгаар нэвтрэх боломжтой тул та админд нь хандана уу.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ms/portal.json b/ghost/i18n/locales/ms/portal.json index a9c86031367e..bd2111dea79d 100644 --- a/ghost/i18n/locales/ms/portal.json +++ b/ghost/i18n/locales/ms/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Laman web ini hanya untuk jemputan, hubungi pemilik untuk akses.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ne/portal.json b/ghost/i18n/locales/ne/portal.json index 54638995cf52..508f16c34662 100644 --- a/ghost/i18n/locales/ne/portal.json +++ b/ghost/i18n/locales/ne/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/nl/portal.json b/ghost/i18n/locales/nl/portal.json index 20843b69737b..e16b198b6218 100644 --- a/ghost/i18n/locales/nl/portal.json +++ b/ghost/i18n/locales/nl/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Er was een fout bij het voortzetten van je abonnement, probeer het opnieuw.", "There was an error processing your payment. Please try again.": "Er was een fout bij het verwerken van je betaling, probeer het opnieuw.", "There was an error sending the email, please try again": "Er was een fout bij het verzenden van de e-mail, probeer het opnieuw", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Deze site is alleen toegankelijk op uitnodiging, neem contact op met de eigenaar.", "This site is not accepting payments at the moment.": "Deze site accepteert momenteel geen betalingen.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/nn/portal.json b/ghost/i18n/locales/nn/portal.json index 84f77247b85d..d3c06a17261d 100644 --- a/ghost/i18n/locales/nn/portal.json +++ b/ghost/i18n/locales/nn/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Denne sida er kun for inviterte, ta kontakt med eigaren for tilgang.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/no/portal.json b/ghost/i18n/locales/no/portal.json index 283b19c68da8..55f85c8d6460 100644 --- a/ghost/i18n/locales/no/portal.json +++ b/ghost/i18n/locales/no/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "En feil oppstod ved fornyelse av abonnementet, vennligst prøv igjen.", "There was an error processing your payment. Please try again.": "Det oppsto en feil under behandling av betalingen din. Vennligst prøv igjen.", "There was an error sending the email, please try again": "En feil oppstod ved sending av e-posten, vennligst prøv igjen", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Denne nettsiden er kun for inviterte. Kontakt eieren for invitasjon.", "This site is not accepting payments at the moment.": "Denne nettsiden godtar ikke betalinger for øyeblikket.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/pl/portal.json b/ghost/i18n/locales/pl/portal.json index 8b28b867e664..518af111ec0f 100644 --- a/ghost/i18n/locales/pl/portal.json +++ b/ghost/i18n/locales/pl/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ta strona posiada zamknięty dostęp. Skontaktuj się z właścicielem, aby uzyskać dostęp.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/pt-BR/portal.json b/ghost/i18n/locales/pt-BR/portal.json index f631eead816f..94cbdeb8c25e 100644 --- a/ghost/i18n/locales/pt-BR/portal.json +++ b/ghost/i18n/locales/pt-BR/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Houve um erro ao continuar sua assinatura, por favor, tente novamente.", "There was an error processing your payment. Please try again.": "Houve um erro ao processar seu pagamento. Por favor, tente novamente.", "There was an error sending the email, please try again": "Houve um erro ao enviar o e-mail, por favor, tente novamente.", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Este site é apenas para convidados. Contate o proprietário para obter acesso.", "This site is not accepting payments at the moment.": "Este site não está aceitando pagamentos no momento.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/pt/portal.json b/ghost/i18n/locales/pt/portal.json index 62d46bca91e8..8d7766791702 100644 --- a/ghost/i18n/locales/pt/portal.json +++ b/ghost/i18n/locales/pt/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "Houve um problema ao processar o seu pagamento. Tente novamente por favor.", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "O acesso a este site é feito apenas por convite. Entre em contacto com o proprietário para obter acesso.", "This site is not accepting payments at the moment.": "Este site não está a aceitar pagamentos de momento", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ro/portal.json b/ghost/i18n/locales/ro/portal.json index 8788c457616c..dce5d968ca71 100644 --- a/ghost/i18n/locales/ro/portal.json +++ b/ghost/i18n/locales/ro/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Acest site este disponibil doar pe bază de invitație, contactează proprietarul pentru acces.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ru/portal.json b/ghost/i18n/locales/ru/portal.json index 4b0d228a2371..c9525ab915e5 100644 --- a/ghost/i18n/locales/ru/portal.json +++ b/ghost/i18n/locales/ru/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "Произошла ошибка при обработке вашего платежа. Попробуйте ещё раз.", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Доступ к материалам этого сайта возможен только по приглашению. Для получения доступа свяжитесь с владельцем сайта.", "This site is not accepting payments at the moment.": "В данный момент сайт не принимает платежи.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/si/portal.json b/ghost/i18n/locales/si/portal.json index a5d2505aabc3..4ccedfa27b9a 100644 --- a/ghost/i18n/locales/si/portal.json +++ b/ghost/i18n/locales/si/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "මෙම වෙබ් අඩවිය ආරාධිතයන් සඳහා පමණි, ප්\u200dරවේශ වීම සඳහා හිමිකරු අමතන්න.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sk/portal.json b/ghost/i18n/locales/sk/portal.json index 79c1cf5192c8..36486b2eb3bb 100644 --- a/ghost/i18n/locales/sk/portal.json +++ b/ghost/i18n/locales/sk/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Táto stránka je iba pre pozvaných úžívateľov, kontaktujte vlastníka stránky.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sl/portal.json b/ghost/i18n/locales/sl/portal.json index 4fc4285f4f3d..9121d3779904 100644 --- a/ghost/i18n/locales/sl/portal.json +++ b/ghost/i18n/locales/sl/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "To spletno mesto je dostopno samo s povabilom, obrnite se na lastnika.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sq/portal.json b/ghost/i18n/locales/sq/portal.json index ab63e66bd55d..e42367d14468 100644 --- a/ghost/i18n/locales/sq/portal.json +++ b/ghost/i18n/locales/sq/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Kjo faqe eshte vetem me ftesa, kontaktoni zoteruesin per akses.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sr-Cyrl/portal.json b/ghost/i18n/locales/sr-Cyrl/portal.json index 7e969ce11a77..858400556f2f 100644 --- a/ghost/i18n/locales/sr-Cyrl/portal.json +++ b/ghost/i18n/locales/sr-Cyrl/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "Дошло је до грешке при обради ваше уплате. Молимо вас покушајте поново.", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Овај сајт је само на позив, контактирајте власника ради приступа.", "This site is not accepting payments at the moment.": "Овај сајт тренутно не прихвата уплате.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sr/portal.json b/ghost/i18n/locales/sr/portal.json index e300ae10a44c..586908dcbfa1 100644 --- a/ghost/i18n/locales/sr/portal.json +++ b/ghost/i18n/locales/sr/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Ovaj sajt je samo za članove, kontaktirajte vlasnika kako bi dobili pristup.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sv/portal.json b/ghost/i18n/locales/sv/portal.json index d56cf1651f8b..55aeecc8e41a 100644 --- a/ghost/i18n/locales/sv/portal.json +++ b/ghost/i18n/locales/sv/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Det blev fel när din prenumeration skulle fortsättas, vänligen försök igen", "There was an error processing your payment. Please try again.": "Det blev fel när din betalning skulle behandlas, vänligen försök igen", "There was an error sending the email, please try again": "Det blev ett fel när e-posten skulle skickas, vänligen försök igen", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Den här sidan är endast för inbjudna, kontakta ägaren för åtkomst.", "This site is not accepting payments at the moment.": "Den här webbsidan accepterar inte betalningar för tillfället", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/sw/portal.json b/ghost/i18n/locales/sw/portal.json index 046013638c22..218f70a3df21 100644 --- a/ghost/i18n/locales/sw/portal.json +++ b/ghost/i18n/locales/sw/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Tovuti hii ni ya mialiko pekee, wasiliana na mmiliki kupata ufikiaji.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ta/portal.json b/ghost/i18n/locales/ta/portal.json index 4b5c92473f49..b7289ef5487a 100644 --- a/ghost/i18n/locales/ta/portal.json +++ b/ghost/i18n/locales/ta/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "உங்கள் சந்தாவைத் தொடர்வதில் பிழை ஏற்பட்டது, மீண்டும் முயற்சிக்கவும்.", "There was an error processing your payment. Please try again.": "உங்கள் கட்டணத்தை செயலாக்குவதில் பிழை ஏற்பட்டது. மீண்டும் முயற்சிக்கவும்.", "There was an error sending the email, please try again": "மின்னஞ்சலை அனுப்புவதில் பிழை ஏற்பட்டது, மீண்டும் முயற்சிக்கவும்", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "இந்த தளம் அழைப்பு மட்டுமே, அணுகலுக்கு உரிமையாளரைத் தொடர்பு கொள்ளவும்.", "This site is not accepting payments at the moment.": "இந்த தளம் தற்போது கட்டணங்களை ஏற்கவில்லை.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/th/portal.json b/ghost/i18n/locales/th/portal.json index 13a3b082bff9..92f4be14b6f6 100644 --- a/ghost/i18n/locales/th/portal.json +++ b/ghost/i18n/locales/th/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "เว็บไซต์นี้สำหรับผู้ได้รับเชิญเท่านั้น โปรดติดต่อเจ้าของเพื่อเข้าถึง", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/tr/portal.json b/ghost/i18n/locales/tr/portal.json index 20047bab6603..9d3059588bd7 100644 --- a/ghost/i18n/locales/tr/portal.json +++ b/ghost/i18n/locales/tr/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Aboneliğinizi devam ettirirken bir hata oluştu, lütfen tekrar deneyin.", "There was an error processing your payment. Please try again.": "Ödemeniz işlenirken bir hata oluştu. Lütfen tekrar deneyiniz.", "There was an error sending the email, please try again": "E-posta gönderilirken bir hata oluştu, lütfen tekrar deneyin.", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Bu site sadece davetiyesi olanlar içindir, erişim için site sahibiyle iletişime geç.", "This site is not accepting payments at the moment.": "Bu site şu anda ödeme kabul etmemektedir.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/uk/portal.json b/ghost/i18n/locales/uk/portal.json index 7eae5dd5038f..4dc2eb04d087 100644 --- a/ghost/i18n/locales/uk/portal.json +++ b/ghost/i18n/locales/uk/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Під час продовження підписки сталася помилка. Спробуйте ще раз.", "There was an error processing your payment. Please try again.": "Під час обробки вашого платежу сталася помилка. Спробуйте ще раз.", "There was an error sending the email, please try again": "Під час надсилання листа сталася помилка. Повторіть спробу", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Цей сайт доступний тільки за запрошенням, звернись до власника сайта для доступу.", "This site is not accepting payments at the moment.": "Цей сайт на даний момент не приймає платежі.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/ur/portal.json b/ghost/i18n/locales/ur/portal.json index c65c245264b3..041dea35fca6 100644 --- a/ghost/i18n/locales/ur/portal.json +++ b/ghost/i18n/locales/ur/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "یہ سائٹ صرف دعوتی ہے، دستیابی کے لئے مالک سے رابطہ کریں۔", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/uz/portal.json b/ghost/i18n/locales/uz/portal.json index b08485cac70d..6e569adb5503 100644 --- a/ghost/i18n/locales/uz/portal.json +++ b/ghost/i18n/locales/uz/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Bu saytda faqat taklif qilinadi, kirish uchun egasiga murojaat qiling.", "This site is not accepting payments at the moment.": "", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/vi/portal.json b/ghost/i18n/locales/vi/portal.json index ce097a1e3238..4c655fd91517 100644 --- a/ghost/i18n/locales/vi/portal.json +++ b/ghost/i18n/locales/vi/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "Xảy ra lỗi khi tiếp tục gói thành viên, vui lòng thử lại", "There was an error processing your payment. Please try again.": "Xảy ra lỗi khi tiến hành thanh toán. Hãy thử lại sau.", "There was an error sending the email, please try again": "Xảy ra lỗi khi gửi email, vui lòng thử lại", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "Trang web này chỉ dành cho những người được mời, hãy liên hệ với chủ sở hữu để cấp quyền truy cập.", "This site is not accepting payments at the moment.": "Trang web này hiện chưa chấp nhận thanh toán.", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/zh-Hant/portal.json b/ghost/i18n/locales/zh-Hant/portal.json index 38be03f724e0..812f54e0b4d3 100644 --- a/ghost/i18n/locales/zh-Hant/portal.json +++ b/ghost/i18n/locales/zh-Hant/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "續約您的訂閱時發生錯誤,請您再試一次。", "There was an error processing your payment. Please try again.": "處理您的付款時發生錯誤,請您再試一次。", "There was an error sending the email, please try again": "寄送 email 時發生錯誤,請您再試一次。", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "此網站僅限受邀請者觀看,請聯繫網站擁有者取得存取權限。", "This site is not accepting payments at the moment.": "此網站目前無付款方式。", "This site only accepts paid members.": "", diff --git a/ghost/i18n/locales/zh/portal.json b/ghost/i18n/locales/zh/portal.json index 1db54a5c4671..016e303c01cc 100644 --- a/ghost/i18n/locales/zh/portal.json +++ b/ghost/i18n/locales/zh/portal.json @@ -165,6 +165,7 @@ "There was an error continuing your subscription, please try again.": "", "There was an error processing your payment. Please try again.": "您的付款处理失败,请重试。", "There was an error sending the email, please try again": "", + "This email domain is not accepted, try again with a different email address": "", "This site is invite-only, contact the owner for access.": "此网站仅限邀请,联系网站所有者以获取访问", "This site is not accepting payments at the moment.": "本网站目前暂不接受付款。", "This site only accepts paid members.": "", diff --git a/ghost/magic-link/lib/MagicLink.js b/ghost/magic-link/lib/MagicLink.js index b49b25a20b89..f05030a6ee0b 100644 --- a/ghost/magic-link/lib/MagicLink.js +++ b/ghost/magic-link/lib/MagicLink.js @@ -2,7 +2,8 @@ const {IncorrectUsageError, BadRequestError} = require('@tryghost/errors'); const {isEmail} = require('@tryghost/validator'); const tpl = require('@tryghost/tpl'); const messages = { - invalidEmail: 'Email is not valid' + invalidEmail: 'Email is not valid', + unsupportedEmailDomain: 'This email domain is not accepted, try again with a different email address' }; /** @@ -34,6 +35,7 @@ class MagicLink { * @param {typeof defaultGetHTML} [options.getHTML] * @param {typeof defaultGetSubject} [options.getSubject] * @param {object} [options.sentry] + * @param {object} [options.config] */ constructor(options) { if (!options || !options.transporter || !options.tokenProvider || !options.getSigninURL) { @@ -46,6 +48,7 @@ class MagicLink { this.getHTML = options.getHTML || defaultGetHTML; this.getSubject = options.getSubject || defaultGetSubject; this.sentry = options.sentry || undefined; + this.config = options.config || {}; } /** @@ -60,12 +63,19 @@ class MagicLink { */ async sendMagicLink(options) { this.sentry?.captureMessage?.(`[Magic Link] Generating magic link`, {extra: options}); - + if (!isEmail(options.email)) { throw new BadRequestError({ message: tpl(messages.invalidEmail) }); } + + if (this.isEmailDomainBlocked(options.email)) { + throw new BadRequestError({ + message: tpl(messages.unsupportedEmailDomain) + }); + } + const token = await this.tokenProvider.create(options.tokenData); const type = options.type || 'signin'; @@ -108,6 +118,24 @@ class MagicLink { const tokenData = await this.tokenProvider.validate(token); return tokenData; } + + /** + * Check if the email domain is blocked, based on the `spam.blocked_email_domains` config + * + * @param {string} email + * @returns {boolean} + */ + isEmailDomainBlocked(email) { + const emailDomain = email.split('@')[1]?.toLowerCase(); + const blockedDomains = this.config?.get('spam:blocked_email_domains'); + + // Config is not set properly: skip check + if (!blockedDomains || !Array.isArray(blockedDomains)) { + return false; + } + + return blockedDomains.includes(emailDomain); + } } /** diff --git a/ghost/magic-link/test/index.test.js b/ghost/magic-link/test/index.test.js index 22520d2671c2..b7e2de246b0c 100644 --- a/ghost/magic-link/test/index.test.js +++ b/ghost/magic-link/test/index.test.js @@ -21,6 +21,9 @@ describe('MagicLink', function () { getSubject: sandbox.stub().returns('SOMESUBJECT'), transporter: { sendMail: sandbox.stub().resolves() + }, + config: { + get: sandbox.stub().resolves() } }; const service = new MagicLink(options); @@ -53,6 +56,9 @@ describe('MagicLink', function () { getSubject: sandbox.stub().returns('SOMESUBJECT'), transporter: { sendMail: sandbox.stub().resolves() + }, + config: { + get: sandbox.stub().resolves() } }; const service = new MagicLink(options); @@ -85,6 +91,48 @@ describe('MagicLink', function () { assert.equal(options.transporter.sendMail.firstCall.args[0].text, options.getText.firstCall.returnValue); assert.equal(options.transporter.sendMail.firstCall.args[0].html, options.getHTML.firstCall.returnValue); }); + + it('Blocks signups from blocked email domains', async function () { + const options = { + tokenProvider: new MagicLink.JWTTokenProvider(secret), + getSigninURL: sandbox.stub().returns('FAKEURL'), + getText: sandbox.stub().returns('SOMETEXT'), + getHTML: sandbox.stub().returns('SOMEHTML'), + getSubject: sandbox.stub().returns('SOMESUBJECT'), + transporter: { + sendMail: sandbox.stub().resolves() + }, + config: { + get: sandbox.stub().withArgs('spam:blocked_email_domains').returns(['blocked-domain.com']) + } + }; + const service = new MagicLink(options); + + const blockedArgs = { + email: 'test@blocked-domain.com', + tokenData: { + id: '420' + } + }; + + await assert.rejects( + () => service.sendMagicLink(blockedArgs), + { + name: 'BadRequestError', + message: 'This email domain is not accepted, try again with a different email address' + } + ); + + // Verify non-blocked domain is allowed + const allowedArgs = { + email: 'test@allowed-domain.com', + tokenData: { + id: '420' + } + }; + + await assert.doesNotReject(() => service.sendMagicLink(allowedArgs)); + }); }); describe('#getDataFromToken', function () { @@ -96,6 +144,9 @@ describe('MagicLink', function () { getHTML: sandbox.stub().returns('SOMEHTML'), transporter: { sendMail: sandbox.stub().resolves() + }, + config: { + get: sandbox.stub().resolves() } }; const service = new MagicLink(options); diff --git a/ghost/members-api/lib/members-api.js b/ghost/members-api/lib/members-api.js index 6bacee6d92a2..660bc6cdca71 100644 --- a/ghost/members-api/lib/members-api.js +++ b/ghost/members-api/lib/members-api.js @@ -73,7 +73,8 @@ module.exports = function MembersAPI({ emailSuppressionList, settingsCache, sentry, - settingsHelpers + settingsHelpers, + config }) { const tokenService = new TokenService({ privateKey, @@ -158,7 +159,8 @@ module.exports = function MembersAPI({ getText, getHTML, getSubject, - sentry + sentry, + config }); const paymentsService = new PaymentsService({