зеркало из https://github.com/mozilla/fxa.git
Merge pull request #16315 from mozilla/fxa-9065
fix(email): Disable using Relay masks to create an account or secondary
This commit is contained in:
Коммит
2b651ce11b
|
@ -624,6 +624,10 @@ var ERRORS = {
|
|||
errno: 1065,
|
||||
message: t('The image file size is too large to be uploaded.'),
|
||||
},
|
||||
EMAIL_MASK_NEW_ACCOUNT: {
|
||||
errno: 1066,
|
||||
message: t('Email masks can’t be used to create an account.'),
|
||||
},
|
||||
};
|
||||
|
||||
export default _.extend({}, Errors, {
|
||||
|
|
|
@ -18,6 +18,7 @@ Vat.register('channelKey', Vat.string().test(Validate.isBase64Url));
|
|||
Vat.register('codeChallenge', Vat.string().min(43).max(128));
|
||||
Vat.register('codeChallengeMethod', Vat.string().valid('S256'));
|
||||
Vat.register('email', Vat.string().test(Validate.isEmailValid));
|
||||
Vat.register('emailMask', Vat.string().test(Validate.isEmailMask));
|
||||
Vat.register('hex', Vat.string().test(Validate.isHexValid));
|
||||
Vat.register('idToken', Vat.string());
|
||||
Vat.register('keyFetchToken', Vat.string());
|
||||
|
|
|
@ -26,6 +26,7 @@ import checkEmailDomain from '../lib/email-domain-validator';
|
|||
import PocketMigrationMixin from './mixins/pocket-migration-mixin';
|
||||
import BrandMessagingMixin from './mixins/brand-messaging-mixin';
|
||||
import MonitorClientMixin from './mixins/monitor-client-mixin';
|
||||
import { isEmailMask } from 'fxa-shared/email/helpers';
|
||||
|
||||
const EMAIL_SELECTOR = 'input[type=email]';
|
||||
|
||||
|
@ -265,6 +266,15 @@ class IndexView extends FormView {
|
|||
.then(({ exists, hasPassword, hasLinkedAccount }) => {
|
||||
const nextEndpoint = exists ? 'signin' : 'signup';
|
||||
|
||||
// If a Relay email mask is being used for a new account, show an error
|
||||
if (nextEndpoint === 'signup' && isEmailMask(email)) {
|
||||
this.showValidationError(
|
||||
EMAIL_SELECTOR,
|
||||
AuthErrors.toError('EMAIL_MASK_NEW_ACCOUNT')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporary hack for React work that allows us to pass the entered `email` as
|
||||
// a param. When 'signup' and 'signin' flows are both finished and we're ready,
|
||||
// we can convert the index page over and pass this along with router state
|
||||
|
|
|
@ -435,6 +435,33 @@ describe('views/index', () => {
|
|||
assert.equal(account.get('email'), EMAIL);
|
||||
});
|
||||
});
|
||||
|
||||
it('allows signin with Relay mask email (@mozmail.com)', () => {
|
||||
const storedAccount = user.initAccount({});
|
||||
sinon.stub(user, 'checkAccountStatus').callsFake(() =>
|
||||
Promise.resolve({
|
||||
exists: true,
|
||||
hasPassword: true,
|
||||
hasLinkedAccount: false,
|
||||
})
|
||||
);
|
||||
sinon.stub(user, 'getAccountByEmail').callsFake(() => storedAccount);
|
||||
|
||||
return view.checkEmail('aaa@mozmail.com').then(() => {
|
||||
const brokerAccount = broker.beforeSignIn.args[0][0];
|
||||
assert.equal(brokerAccount.get('email'), 'aaa@mozmail.com');
|
||||
|
||||
assert.isTrue(
|
||||
view.navigate.calledOnceWith('signin', {
|
||||
account: storedAccount,
|
||||
})
|
||||
);
|
||||
const { account } = view.navigate.args[0][1];
|
||||
assert.strictEqual(account, storedAccount);
|
||||
// Ensure the email is added to the stored account.
|
||||
assert.equal(account.get('email'), 'aaa@mozmail.com');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('email is not registered', () => {
|
||||
|
@ -457,6 +484,19 @@ describe('views/index', () => {
|
|||
assert.isFalse(view.navigate.called);
|
||||
});
|
||||
});
|
||||
|
||||
it('displays error with Relay mask email (@mozmail.com)', () => {
|
||||
sinon
|
||||
.stub(user, 'checkAccountStatus')
|
||||
.callsFake(() => Promise.resolve({ exists: false }));
|
||||
sinon.stub(view, '_validateEmailDomain').resolves();
|
||||
sinon.spy(view, 'showValidationError');
|
||||
return view.checkEmail('abc@mozmail.com').then(() => {
|
||||
assert.isTrue(view.showValidationError.called);
|
||||
const err = view.showValidationError.args[0][1];
|
||||
assert.isTrue(AuthErrors.is(err, 'EMAIL_MASK_NEW_ACCOUNT'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MX record validation configurations', () => {
|
||||
|
|
|
@ -70,6 +70,26 @@ describe('PageSecondaryEmailAdd', () => {
|
|||
|
||||
expect(screen.getByTestId('save-button')).toHaveAttribute('disabled');
|
||||
});
|
||||
|
||||
it('should display tooltip error when using email mask', async () => {
|
||||
renderWithRouter(
|
||||
<AppContext.Provider value={mockAppContext({ account })}>
|
||||
<PageSecondaryEmailAdd />
|
||||
</AppContext.Provider>
|
||||
);
|
||||
|
||||
const input = screen.getByTestId('input-field');
|
||||
fireEvent.change(input, { target: { value: 'user@mozmail.com' } });
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('save-button'));
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId('tooltip')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('Email masks can’t be used as a secondary email')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSecondaryEmailCode', () => {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { HomePath } from '../../../constants';
|
|||
import InputText from '../../InputText';
|
||||
import FlowContainer from '../FlowContainer';
|
||||
import VerifiedSessionGuard from '../VerifiedSessionGuard';
|
||||
import { isEmailValid } from 'fxa-shared/email/helpers';
|
||||
import { isEmailMask, isEmailValid } from 'fxa-shared/email/helpers';
|
||||
import { useAccount, useAlertBar } from 'fxa-settings/src/models';
|
||||
import {
|
||||
AuthUiErrorNos,
|
||||
|
@ -66,8 +66,18 @@ export const PageSecondaryEmailAdd = (_: RouteComponentProps) => {
|
|||
setSaveBtnDisabled(!isValid);
|
||||
setEmail(inputRef.current?.value);
|
||||
setErrorText('');
|
||||
|
||||
if (isEmailMask(email)) {
|
||||
const errorText = l10n.getString(
|
||||
'add-secondary-email-mask',
|
||||
null,
|
||||
'Email masks can’t be used as a secondary email'
|
||||
);
|
||||
setErrorText(errorText);
|
||||
setSaveBtnDisabled(true);
|
||||
}
|
||||
},
|
||||
[setSaveBtnDisabled]
|
||||
[setSaveBtnDisabled, setErrorText, l10n]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -622,6 +622,10 @@ const ERRORS = {
|
|||
errno: 1065,
|
||||
message: 'The image file size is too large to be uploaded.',
|
||||
},
|
||||
EMAIL_MASK_NEW_ACCOUNT: {
|
||||
errno: 1066,
|
||||
message: 'Email masks can’t be used to create an account.',
|
||||
},
|
||||
};
|
||||
|
||||
type ErrorKey = keyof typeof ERRORS;
|
||||
|
|
|
@ -359,6 +359,23 @@ describe('Signup page', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('fails for Relay email masks', () => {
|
||||
['a@relay.firefox.com', 'b@mozmail.com', 'c@sub.mozmail.com'].forEach(
|
||||
(mask) => {
|
||||
it(`fails for mask ${mask}`, async () => {
|
||||
renderWithLocalizationProvider(
|
||||
<Subject queryParams={{ email: mask }} />
|
||||
);
|
||||
await fillOutForm();
|
||||
submit();
|
||||
await screen.findByText(
|
||||
'Email masks can’t be used to create an account.'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('passes newsletter subscription options to the next screen', async () => {
|
||||
const mockBeginSignupHandler = jest
|
||||
.fn()
|
||||
|
|
|
@ -47,6 +47,11 @@ import { MozServices } from '../../lib/types';
|
|||
import LoadingSpinner from 'fxa-react/components/LoadingSpinner';
|
||||
import firefox from '../../lib/channels/firefox';
|
||||
import ThirdPartyAuth from '../../components/ThirdPartyAuth';
|
||||
import {
|
||||
AuthUiErrors,
|
||||
composeAuthUiErrorTranslationId,
|
||||
} from '../../lib/auth-errors/auth-errors';
|
||||
import { isEmailMask } from 'fxa-shared/email/helpers';
|
||||
|
||||
export const viewName = 'signup';
|
||||
|
||||
|
@ -194,6 +199,17 @@ export const Signup = ({
|
|||
navigate('/cannot_create_account');
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable creating accounts with email masks
|
||||
if (isEmailMask(email)) {
|
||||
const message = 'Email masks can’t be used to create an account.';
|
||||
const ftlId = composeAuthUiErrorTranslationId({
|
||||
errno: AuthUiErrors.EMAIL_MASK_NEW_ACCOUNT.errno,
|
||||
});
|
||||
setBannerErrorText(ftlMsgResolver.getMsg(ftlId, message));
|
||||
return;
|
||||
}
|
||||
|
||||
setBeginSignupLoading(true);
|
||||
|
||||
const atLeast18AtReg = Number(age) >= 18 ? true : null;
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Mozmail.com is the Firefox Relay email mask, we prohibit creating accounts with it
|
||||
const emailMaskRegex = /@([a-zA-Z0-9.-]+\.)?(mozmail|relay\.firefox)\.(com)$/i;
|
||||
|
||||
export function normalizeEmail(originalEmail: string): string {
|
||||
return originalEmail.toLowerCase();
|
||||
}
|
||||
|
@ -32,7 +39,8 @@ export function emailsMatch(firstEmail: string, secondEmail: string): boolean {
|
|||
// * http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
|
||||
// '/' in the character class is (redundantly) backslash-escaped to produce
|
||||
// the same minimized form in node 4.x and node 0.10.
|
||||
const emailRegex = /^[\w.!#$%&'*+\/=?^`{|}~-]{1,64}@[a-z\d](?:[a-z\d-]{0,253}[a-z\d])?(?:\.[a-z\d](?:[a-z\d-]{0,253}[a-z\d])?)+$/i;
|
||||
const emailRegex =
|
||||
/^[\w.!#$%&'*+\/=?^`{|}~-]{1,64}@[a-z\d](?:[a-z\d-]{0,253}[a-z\d])?(?:\.[a-z\d](?:[a-z\d-]{0,253}[a-z\d])?)+$/i;
|
||||
|
||||
/**
|
||||
* Check if an email address is valid
|
||||
|
@ -55,3 +63,14 @@ export function isEmailValid(email: string): boolean {
|
|||
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an email address is an email mask. Currently, the only email mask
|
||||
* we check is mozmail.com, relay.firefox.com and *.mozmail.com from Firefox Relay.
|
||||
*
|
||||
* @param {String} email
|
||||
* @return {Boolean} true if email is mask, false otw.
|
||||
*/
|
||||
export function isEmailMask(email: string): boolean {
|
||||
return emailMaskRegex.test(email);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче