refactor(client): Remove the password strength checker. (#4903) r=vladikoff

Not attached to any PR.
This commit is contained in:
Shane Tomlinson 2017-04-06 14:06:46 -07:00 коммит произвёл Vlad Filippov
Родитель 9b296fb9f7
Коммит 4786c1fcb1
11 изменённых файлов: 1 добавлений и 548 удалений

Просмотреть файл

@ -40,8 +40,7 @@ require.config({
],
requireOnDemand: [
'fxaClient',
'jwcrypto',
'passwordcheck'
'jwcrypto'
],
// the sriify task will replace sriConfig for production
// DO NOT EDIT BELOW HERE
@ -85,7 +84,6 @@ require.config({
modal: '../bower_components/jquery-modal/jquery.modal',
moment: '../bower_components/moment/moment',
mustache: '../bower_components/mustache/mustache',
passwordcheck: '../bower_components/fxa-password-strength-checker/build/fxa-password-strength-checker',
'p-promise': '../bower_components/p/p',
raven: '../bower_components/raven-js/dist/raven',
sinon: '../bower_components/sinon/lib/sinon',

Просмотреть файл

@ -13,7 +13,6 @@ define(function (require, exports, module) {
const Notifier = require('lib/channels/notifier');
const PasswordMixin = require('views/mixins/password-mixin');
const PasswordResetMixin = require('views/mixins/password-reset-mixin');
const PasswordStrengthMixin = require('views/mixins/password-strength-mixin');
const ResendMixin = require('views/mixins/resend-mixin')();
const ServiceMixin = require('views/mixins/service-mixin');
const Template = require('stache!templates/complete_reset_password');
@ -168,7 +167,6 @@ define(function (require, exports, module) {
FlowEventsMixin,
PasswordMixin,
PasswordResetMixin,
PasswordStrengthMixin,
ResendMixin,
ServiceMixin
);

Просмотреть файл

@ -1,103 +0,0 @@
/* 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/. */
/**
* Plugin to display hints for passwords,
* hints change as the user types
*/
define(function (require, exports, module) {
'use strict';
const { t } = require('views/base');
const Tooltip = require('views/tooltip');
// this link is not suitable for L10N and should be available in only `en` locales.
const CHECK_PASSWORD_FIELD_SELECTOR = '.check-password';
const INPUT_HELP_FOCUSED = '.input-help-focused';
const TOOLTIP_MESSAGES = {
FOCUS_PROMPT_MESSAGE: t('8 characters minimum, but longer if you plan to sync passwords.'),
INITIAL_PROMPT_MESSAGE: t('A strong, unique password will keep your Firefox data safe from intruders.'),
WARNING_PROMPT_MESSAGE: t('This is a common password; please consider another one.')
};
const PasswordPromptMixin = {
// only the .check-password fields will be checked
events: {
'blur .check-password': 'onPasswordBlur',
'focus .check-password': 'onInputFocus',
'keyup .check-password': 'onInputKeyUp'
},
afterRender () {
this.updateFormValueChanges();
},
displayPasswordInitialPrompt (inputEl) {
this.$(inputEl).siblings(INPUT_HELP_FOCUSED).html(this.translate(TOOLTIP_MESSAGES.INITIAL_PROMPT_MESSAGE));
this._logPromptExperimentEvent('INITIAL_PROMPT_MESSAGE');
},
displayPasswordFocusPrompt (inputEl) {
this.$(inputEl).siblings(INPUT_HELP_FOCUSED).html(this.translate(TOOLTIP_MESSAGES.FOCUS_PROMPT_MESSAGE));
this._logPromptExperimentEvent('FOCUS_PROMPT_MESSAGE');
},
displayPasswordWarningPrompt () {
const promptContent =
this.translate(TOOLTIP_MESSAGES.WARNING_PROMPT_MESSAGE);
const tooltip = new Tooltip({
dismissible: false,
extraClassNames: 'tooltip-suggest tooltip-warning',
invalidEl: this.$(CHECK_PASSWORD_FIELD_SELECTOR),
message: promptContent
});
tooltip.render();
this._logPromptExperimentEvent('WARNING_PROMPT_MESSAGE');
},
showPasswordPrompt (inputEl) {
const length = this.$(inputEl).val().length;
if (length === 0) {
this.displayPasswordInitialPrompt(inputEl);
} else {
this.displayPasswordFocusPrompt(inputEl);
}
},
onInputFocus (event) {
this.showPasswordPrompt(event.currentTarget);
},
onInputKeyUp (event) {
this.showPasswordPrompt(event.currentTarget);
},
onPasswordBlur () {
const password = this.getElementValue(CHECK_PASSWORD_FIELD_SELECTOR);
this.checkPasswordStrength(password);
},
_logPromptExperimentEvent (eventNameSuffix) {
const eventName = 'experiment.pw_prompt.' + eventNameSuffix.toLowerCase();
this.logEventOnce(eventName);
},
/**
* Determines if user is using an English locale.
* @returns {Boolean}
* @private
*/
_isEnglishLocale () {
return !! (this.lang && this.lang.indexOf('en') === 0);
},
// Constants, exposed for testing
CHECK_PASSWORD_FIELD_SELECTOR: CHECK_PASSWORD_FIELD_SELECTOR,
INPUT_HELP_FOCUSED: INPUT_HELP_FOCUSED,
TOOLTIP_MESSAGES: TOOLTIP_MESSAGES
};
module.exports = PasswordPromptMixin;
});

Просмотреть файл

@ -1,106 +0,0 @@
/* 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/. */
define(function (require, exports, module) {
'use strict';
const _ = require('underscore');
const p = require('lib/promise');
const requireOnDemand = require('lib/require-on-demand');
const Url = require('lib/url');
const PasswordPromptMixin = require('views/mixins/password-prompt-mixin');
var PasswordStrengthMixin = {
initialize (options) {
this._able = options.able;
},
_isPasswordStrengthCheckEnabledValue: undefined,
isPasswordStrengthCheckEnabled () {
if (_.isUndefined(this._isPasswordStrengthCheckEnabledValue)) {
var abData = {
// the window parameter will override any ab testing features
forcePasswordStrengthCheck: Url.searchParam('passwordStrengthCheck', this.window.location.search),
isMetricsEnabledValue: this.metrics.isCollectionEnabled(),
uniqueUserId: this.user.get('uniqueUserId')
};
this._isPasswordStrengthCheckEnabledValue =
this._able.choose('passwordStrengthCheckEnabled', abData);
if (this._isPasswordStrengthCheckEnabledValue) {
this._logStrengthExperimentEvent('enabled');
}
}
return this._isPasswordStrengthCheckEnabledValue;
},
_passwordStrengthCheckerPromise: undefined,
getPasswordStrengthChecker () {
// returns a promise that resolves once the library is loaded.
if (! this._passwordStrengthCheckerPromise) {
this._passwordStrengthCheckerPromise = requireOnDemand('passwordcheck')
// Log any failures loading the script
.fail(this.logError.bind(this))
.then((PasswordCheck) => {
return new PasswordCheck();
});
}
return this._passwordStrengthCheckerPromise;
},
/**
* Check the password strength. Returns a promise that resolves
* when the check is complete. Promise resolves to `DISABLED` if
* password strength checker is disabled.
*
* Usage:
*
* view.checkPasswordStrength(password)
* .then(function (status) {
* // do something with the status
* });
*
* @method checkPasswordStrength
* @param {String} password
*
* @returns {Promise}
*/
checkPasswordStrength (password) {
if (! this.isPasswordStrengthCheckEnabled()) {
return p('DISABLED');
}
return this.getPasswordStrengthChecker()
.then((passwordStrengthChecker) => {
var deferred = p.defer();
passwordStrengthChecker(password, (passwordCheckStatus) => {
passwordCheckStatus = passwordCheckStatus || 'UNKNOWN';
if (passwordCheckStatus === 'BLOOMFILTER_HIT' ||
passwordCheckStatus === 'BLOOMFILTER_MISS') {
this._logStrengthExperimentEvent('bloomfilter_used');
}
if (passwordCheckStatus === 'BLOOMFILTER_HIT' ||
passwordCheckStatus === 'ALL_LETTERS_OR_NUMBERS') {
// display the warning prompt only if the password is ALL_LETTERS_OR_NUMBERS
// or password is found in list of common passwords
this.displayPasswordWarningPrompt();
}
this._logStrengthExperimentEvent(passwordCheckStatus);
deferred.resolve(passwordCheckStatus);
});
return deferred.promise;
});
},
_logStrengthExperimentEvent (eventNameSuffix) {
var eventName = 'experiment.pw_strength.' + eventNameSuffix.toLowerCase();
this.logViewEvent(eventName);
}
};
module.exports = _.extend(PasswordStrengthMixin, PasswordPromptMixin);
});

Просмотреть файл

@ -13,7 +13,6 @@ define(function (require, exports, module) {
const FormView = require('views/form');
const ExperimentMixin = require('views/mixins/experiment-mixin');
const PasswordMixin = require('views/mixins/password-mixin');
const PasswordStrengthMixin = require('views/mixins/password-strength-mixin');
const ServiceMixin = require('views/mixins/service-mixin');
const SettingsPanelMixin = require('views/mixins/settings-panel-mixin');
const Template = require('stache!templates/settings/change_password');
@ -71,7 +70,6 @@ define(function (require, exports, module) {
View,
ExperimentMixin,
PasswordMixin,
PasswordStrengthMixin,
FloatingPlaceholderMixin,
SettingsPanelMixin,
ServiceMixin,

Просмотреть файл

@ -18,7 +18,6 @@ define(function (require, exports, module) {
const MigrationMixin = require('views/mixins/migration-mixin');
const p = require('lib/promise');
const PasswordMixin = require('views/mixins/password-mixin');
const PasswordStrengthMixin = require('views/mixins/password-strength-mixin');
const ResumeTokenMixin = require('views/mixins/resume-token-mixin');
const ServiceMixin = require('views/mixins/service-mixin');
const SignedInNotificationMixin = require('views/mixins/signed-in-notification-mixin');
@ -120,12 +119,6 @@ define(function (require, exports, module) {
}.bind(this));
}
if (this.isPasswordStrengthCheckEnabled()) {
// load the password strength checker early so the user does
// not need to wait once they fill out the password.
this.getPasswordStrengthChecker();
}
return FormView.prototype.afterVisible.call(this);
},
@ -399,7 +392,6 @@ define(function (require, exports, module) {
FlowBeginMixin,
MigrationMixin,
PasswordMixin,
PasswordStrengthMixin,
ResumeTokenMixin,
ServiceMixin,
SignInMixin,

Просмотреть файл

@ -1,141 +0,0 @@
/* 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/. */
define(function (require, exports, module) {
'use strict';
const $ = require('jquery');
const { assert } = require('chai');
const Cocktail = require('cocktail');
const Notifier = require('lib/channels/notifier');
const p = require('lib/promise');
const PasswordPromptMixin = require('views/mixins/password-prompt-mixin');
const PasswordStrengthMixin = require('views/mixins/password-strength-mixin');
const sinon = require('sinon');
const FormView = require('views/form');
const Template = require('stache!templates/test_template');
const Translator = require('lib/translator');
const TestView = FormView.extend({
template: Template
});
const viewOpts = {
notifier: new Notifier(),
translator: new Translator({forceEnglish: true})
};
Cocktail.mixin(
TestView,
PasswordPromptMixin,
PasswordStrengthMixin
);
const TOOLTIP_SELECTOR = '.tooltip-warning';
describe('views/mixins/password-prompt-mixin', function () {
var view;
describe('showPasswordPrompt displays different prompts', function () {
beforeEach(function () {
view = new TestView(viewOpts);
return view.render();
});
it('displays the initial password prompt when password field is empty', function () {
var password = '';
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).val(password);
view.showPasswordPrompt(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR);
assert.equal(
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).siblings(PasswordPromptMixin.INPUT_HELP_FOCUSED).html(),
PasswordPromptMixin.TOOLTIP_MESSAGES.INITIAL_PROMPT_MESSAGE
);
});
it('displays the focused password prompt when password field is not empty', function () {
var password = 's';
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).val(password);
view.showPasswordPrompt(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR);
assert.equal(
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).siblings(PasswordPromptMixin.INPUT_HELP_FOCUSED).html(),
PasswordPromptMixin.TOOLTIP_MESSAGES.FOCUS_PROMPT_MESSAGE
);
});
});
describe('event triggers call the correct methods', function () {
beforeEach(function () {
view = new TestView(viewOpts);
sinon.spy(view, 'showPasswordPrompt');
return view.render();
});
it('onInputFocus calls showPasswordPrompt with the right password field', function () {
var event = new $.Event('focus');
event.currentTarget = PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR;
view.onInputFocus(event);
assert.isTrue(view.showPasswordPrompt.calledWith(
PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR));
});
it('onInputKeyUp calls showPasswordPrompt with the right password field', function () {
var event = new $.Event('keyup');
event.currentTarget = PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR;
view.onInputKeyUp(event);
assert.isTrue(view.showPasswordPrompt.calledWith(
PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR));
});
});
describe('checks password strength and displays tooltip if required', function () {
beforeEach(function () {
view = new TestView(viewOpts);
return view.render();
});
it('calls checkPasswordStrength with the right password', function () {
var password = 'charlie2';
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).val(password);
sinon.stub(view, 'checkPasswordStrength', function (password) {
// do nothing
});
view.onPasswordBlur();
assert.isTrue(view.checkPasswordStrength.calledWith('charlie2'));
});
it('displays tooltip when password is weak', function () {
var password = 'charlie2';
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).val(password);
sinon.stub(view, 'checkPasswordStrength', function (password) {
view.displayPasswordWarningPrompt();
});
view.lang = 'en-rr';
view.onPasswordBlur();
return p()
// wait for tooltip
.delay(50)
.then(() => {
assert.equal(view.$(TOOLTIP_SELECTOR).length, 1);
});
});
it('does not display tooltip when password is strong', function () {
var password = 'imstronglol';
view.$(PasswordPromptMixin.CHECK_PASSWORD_FIELD_SELECTOR).val(password);
sinon.stub(view, 'checkPasswordStrength', function (password) {
// do nothing
});
view.onPasswordBlur();
return p()
// wait for tooltip
.delay(50)
.then(() => {
assert.equal(view.$(TOOLTIP_SELECTOR).length, 0);
});
});
});
});
});

Просмотреть файл

@ -1,149 +0,0 @@
/* 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/. */
define(function (require, exports, module) {
'use strict';
const BaseView = require('views/base');
const chai = require('chai');
const Cocktail = require('cocktail');
const PasswordStrengthMixin = require('views/mixins/password-strength-mixin');
const sinon = require('sinon');
var assert = chai.assert;
describe('views/mixins/password-strength-mixin', function () {
// has a dot at the end
var EVENT_NAME_PREFIX = 'experiment.pw_strength.';
var view;
var View = BaseView.extend({
// nothing to extend
});
Cocktail.mixin(
View,
PasswordStrengthMixin
);
describe('isPasswordStrengthCheckEnabled', function () {
it('calls able to make the choice', function () {
var ableMock = {
choose: sinon.spy(function () {
return true;
})
};
view = new View({
able: ableMock,
metrics: {
isCollectionEnabled () {
return true;
},
logViewEvent: sinon.spy()
},
user: {
get () {
return 'userid';
}
},
window: {
location: {
search: '?passwordStrengthCheck=true'
}
}
});
sinon.spy(view, 'logViewEvent');
assert.isTrue(view.isPasswordStrengthCheckEnabled());
assert.isTrue(
ableMock.choose.calledWith('passwordStrengthCheckEnabled', {
forcePasswordStrengthCheck: 'true',
isMetricsEnabledValue: true,
uniqueUserId: 'userid'
})
);
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'enabled'));
});
});
describe('disabled', function () {
it('does not attempt to check password', function () {
view = new View();
sinon.spy(view, 'logViewEvent');
sinon.stub(view, 'isPasswordStrengthCheckEnabled', function () {
return false;
});
sinon.spy(view, 'getPasswordStrengthChecker');
return view.checkPasswordStrength('password')
.then(function (status) {
assert.equal(status, 'DISABLED');
assert.isFalse(view.getPasswordStrengthChecker.called);
});
});
});
describe('enabled', function () {
beforeEach(function () {
view = new View();
sinon.spy(view, 'logViewEvent');
sinon.stub(view, 'isPasswordStrengthCheckEnabled', function () {
return true;
});
});
it('logs `too_short` when password is short', function () {
return view.checkPasswordStrength('hello')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'too_short'));
});
});
it('logs `missing_password` when no password is passed', function () {
return view.checkPasswordStrength('')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'missing_password'));
});
});
it('logs `all_letters_or_numbers` when password is all numbers', function () {
return view.checkPasswordStrength('123456789')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'all_letters_or_numbers'));
});
});
it('logs `all_letters_or_numbers` when password is all letters', function () {
return view.checkPasswordStrength('dragondrag')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'all_letters_or_numbers'));
});
});
it('logs `long_enough` when password is >= 12 chars', function () {
return view.checkPasswordStrength('imsuperlongandstrong')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'long_enough'));
});
});
it('logs `bloomfilter_used` and `bloomfilter_hit` when password is in Bloom filter', function () {
return view.checkPasswordStrength('charlie2')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'bloomfilter_used'));
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'bloomfilter_hit'));
});
});
it('logs `bloomfilter_used` and `bloomfilter_miss` when checked against Bloom filter but not present', function () {
return view.checkPasswordStrength('pO09kskAs')
.then(function () {
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'bloomfilter_used'));
assert.isTrue(view.logViewEvent.calledWith(EVENT_NAME_PREFIX + 'bloomfilter_miss'));
});
});
});
});
});

Просмотреть файл

@ -372,24 +372,6 @@ define(function (require, exports, module) {
});
}, done);
});
it('loads the password strength checker if enabled', function () {
sinon.stub(view, 'isPasswordStrengthCheckEnabled', function () {
return true;
});
sinon.stub(view, 'getPasswordStrengthChecker', function () {
return true;
});
return view.render()
.then(function () {
return view.afterVisible();
})
.then(function () {
assert.isTrue(view.getPasswordStrengthChecker.called);
});
});
});
describe('isValid', function () {
@ -1238,19 +1220,6 @@ define(function (require, exports, module) {
});
});
describe('onPasswordBlur', function () {
beforeEach(function () {
sinon.spy(view, 'checkPasswordStrength');
});
it('calls checkPasswordStrength with provided password', function () {
var password = 'somerandomvalue';
view.$('.password').val(password);
view.onPasswordBlur();
assert.isTrue(view.checkPasswordStrength.calledWith(password));
});
});
describe('onAmoSignIn', function () {
beforeEach(function () {
relier.set('email', email);

Просмотреть файл

@ -139,9 +139,7 @@ function (Translator, Session) {
'../tests/spec/views/mixins/modal-settings-panel-mixin',
'../tests/spec/views/mixins/open-webmail-mixin',
'../tests/spec/views/mixins/password-mixin',
'../tests/spec/views/mixins/password-prompt-mixin',
'../tests/spec/views/mixins/password-reset-mixin',
'../tests/spec/views/mixins/password-strength-mixin',
'../tests/spec/views/mixins/pulse-graphic-mixin',
'../tests/spec/views/mixins/resend-mixin',
'../tests/spec/views/mixins/resume-token-mixin',

Просмотреть файл

@ -10,7 +10,6 @@
"easteregg": "https://github.com/mozilla/fxa-easter-egg.git#ab20cd517cf8ae9feee115e48745189d28e13bc3",
"fxa-checkbox": "mozilla/fxa-checkbox#7f856afffd394a144f718e28e6fb79092d6ccddd",
"fxa-js-client": "https://github.com/mozilla/fxa-js-client.git#0.1.54",
"fxa-password-strength-checker": "https://github.com/mozilla/fxa-password-strength-checker.git#d73b3ade374607e2749ee375301b0a168008b16f",
"html5shiv": "3.7.2",
"JavaScript-MD5": "1.1.0",
"jquery": "3.1.0",