Skip to content
Snippets Groups Projects

Clean up display_password_requirements feature flag

Feature has been deemed stable and the feature flag can
be removed

Changelog: added
EE: true
parent f6bb5eaa
No related branches found
No related tags found
1 merge request!181446Clean up display_password_requirements feature flag
......@@ -66,15 +66,6 @@ export default {
isEmptyPasswordLegal() {
return this.password.trim() === '' && this.allowNoPassword;
},
displayPasswordRequirements() {
// this will be true with display_password_requirements ff removal
return this.ruleTypes.includes(COMMON) || this.ruleTypes.includes(USER_INFO);
},
boxClasses() {
return {
'gl-text-subtle': this.displayPasswordRequirements,
};
},
},
watch: {
password() {
......@@ -94,25 +85,25 @@ export default {
this.password = this.passwordInputElement.value;
});
if (this.firstName && this.displayPasswordRequirements) {
if (this.firstName) {
this.firstName.addEventListener('input', () => {
this.checkValidity(this.findRule(USER_INFO));
});
}
if (this.lastName && this.displayPasswordRequirements) {
if (this.lastName) {
this.lastName.addEventListener('input', () => {
this.checkValidity(this.findRule(USER_INFO));
});
}
if (this.username && this.displayPasswordRequirements) {
if (this.username) {
this.username.addEventListener('input', () => {
this.checkValidity(this.findRule(USER_INFO));
});
}
if (this.email && this.displayPasswordRequirements) {
if (this.email) {
this.email.addEventListener('input', () => {
this.checkValidity(this.findRule(USER_INFO));
});
......@@ -202,8 +193,7 @@ export default {
return { name: 'status_created_borderless', size: 12 };
},
ruleText(rule) {
// make text capitalization permanent with display_password_requirements ff removal
return this.ruleTypes.includes(COMMON) ? capitalizeFirstCharacter(rule.text) : rule.text;
return capitalizeFirstCharacter(rule.text);
},
passwordComplexityParams() {
return {
......@@ -221,7 +211,11 @@ export default {
</script>
<template>
<div v-show="!isEmptyPasswordLegal" data-testid="password-requirement-list" :class="boxClasses">
<div
v-show="!isEmptyPasswordLegal"
class="gl-text-subtle"
data-testid="password-requirement-list"
>
<div
v-for="(rule, index) in ruleList"
:key="rule.text"
......
......@@ -46,7 +46,7 @@ def form_based_providers
def password_rule_list(basic)
if ::License.feature_available?(:password_complexity)
rules = []
rules.concat([:length, :common, :user_info]) if basic && display_password_requirements?
rules.concat([:length, :common, :user_info]) if basic
rules << :number if ::Gitlab::CurrentSettings.password_number_required?
rules << :lowercase if ::Gitlab::CurrentSettings.password_lowercase_required?
rules << :uppercase if ::Gitlab::CurrentSettings.password_uppercase_required?
......
......@@ -40,11 +40,6 @@ def unconfirmed_email_text
)
end
def display_password_requirements?
::License.feature_available?(:password_complexity) &&
::Feature.enabled?(:display_password_requirements, :instance, type: :gitlab_com_derisk)
end
private
def registration_objective_options
......
- if display_password_requirements?
- if ::License.feature_available?(:password_complexity)
= form.label :password, _('Password')
%input.form-control.gl-form-input.js-password{ data: { id: "#{form_resource_name}_password",
testid: 'new-user-password-field',
......
---
name: display_password_requirements
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/461828
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158789
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/509916
milestone: '17.3'
group: group::activation
type: gitlab_com_derisk
default_enabled: false
......@@ -12,6 +12,7 @@ import {
INVALID_INPUT_CLASS,
INVALID_FORM_CLASS,
I18N,
LENGTH,
COMMON,
USER_INFO,
} from 'ee/password/constants';
......@@ -35,13 +36,11 @@ describe('Password requirement list component', () => {
mockAxios.restore();
});
const FIRST_NAME_INPUT_ID = 'new_user_first_name';
const PASSWORD_INPUT_CLASS = 'js-password-complexity-validation';
const findStatusIcon = (ruleType) => wrapper.findByTestId(`password-${ruleType}-status-icon`);
const findGlIcon = (statusIcon) => findStatusIcon(statusIcon).findComponent(GlIcon);
const findRuleTextsByClass = (colorClassName) =>
wrapper.findAllByTestId('password-rule-text').filter((c) => c.classes(colorClassName));
const findFirstNameInputElement = () => document.querySelector(`#${FIRST_NAME_INPUT_ID}`);
const findPasswordInputElement = () => document.querySelector(`.${PASSWORD_INPUT_CLASS}`);
const findForm = () => findPasswordInputElement().form;
const findSubmitButton = () => findForm().querySelector('[type="submit"]');
......@@ -64,7 +63,7 @@ describe('Password requirement list component', () => {
beforeEach(() => {
setHTMLFixture(`
<form>
<input id="${FIRST_NAME_INPUT_ID}" name="new_user[first_name]" type="text">
<input id="new_user_first_name" name="new_user[first_name]" type="text">
<input autocomplete="new-password" class="form-control gl-form-input ${PASSWORD_INPUT_CLASS}" type="password" name="new_user[password]" id="new_user_password">
<input type="submit" name="commit" value="Submit">
</form>
......@@ -75,17 +74,6 @@ describe('Password requirement list component', () => {
resetHTMLFixture();
});
it('does not throw errors on name change', () => {
createComponent();
const firstNameInputElement = findFirstNameInputElement();
expect(() => {
firstNameInputElement.value = 'Name';
firstNameInputElement.dispatchEvent(new Event('input'));
}).not.toThrow();
});
describe('when empty password is not allowed', () => {
beforeEach(() => {
createComponent();
......@@ -205,10 +193,8 @@ describe('Password requirement list component', () => {
);
describe('password complexity rules', () => {
const password = '11111111';
beforeEach(() => {
createComponent({ props: { ruleTypes: [COMMON, USER_INFO] } });
createComponent({ props: { ruleTypes: [LENGTH, COMMON, USER_INFO] } });
});
it('shows the list as secondary text', () => {
......@@ -217,18 +203,9 @@ describe('Password requirement list component', () => {
).toBe(true);
});
it('does not throw errors on name change', () => {
createComponent();
const firstNameInputElement = findFirstNameInputElement();
expect(() => {
firstNameInputElement.value = 'Name';
firstNameInputElement.dispatchEvent(new Event('input'));
}).not.toThrow();
});
describe('when there are errors', () => {
const password = '1111111';
beforeEach(() => {
mockAxios
.onPost(PASSWORD_COMPLEXITY_PATH, { user: { password, first_name: '' } })
......@@ -250,9 +227,10 @@ describe('Password requirement list component', () => {
const errorRules = findRuleTextsByClass(RED_TEXT_CLASS);
expect(errorRules.length).toBe(2);
expect(errorRules.at(0).text()).toBe('Cannot use common phrases (e.g. "password")');
expect(errorRules.at(1).text()).toBe('Cannot include your name, username, or email');
expect(errorRules.length).toBe(3);
expect(errorRules.at(0).text()).toBe('Must be between 8-128 characters');
expect(errorRules.at(1).text()).toBe('Cannot use common phrases (e.g. "password")');
expect(errorRules.at(2).text()).toBe('Cannot include your name, username, or email');
expect(findRuleTextsByClass(GREEN_TEXT_CLASS).length).toBe(0);
expect(findForm().classList.contains(INVALID_FORM_CLASS)).toBe(true);
expect(passwordInputElement.classList.contains(INVALID_INPUT_CLASS)).toBe(true);
......@@ -260,6 +238,8 @@ describe('Password requirement list component', () => {
});
describe('when there are no errors', () => {
const password = '11111111';
beforeEach(() => {
mockAxios
.onPost(PASSWORD_COMPLEXITY_PATH, { user: { password, first_name: '' } })
......@@ -277,15 +257,18 @@ describe('Password requirement list component', () => {
const validRules = findRuleTextsByClass(GREEN_TEXT_CLASS);
expect(validRules.length).toBe(2);
expect(validRules.at(0).text()).toBe('Cannot use common phrases (e.g. "password")');
expect(validRules.at(1).text()).toBe('Cannot include your name, username, or email');
expect(validRules.length).toBe(3);
expect(validRules.at(0).text()).toBe('Must be between 8-128 characters');
expect(validRules.at(1).text()).toBe('Cannot use common phrases (e.g. "password")');
expect(validRules.at(2).text()).toBe('Cannot include your name, username, or email');
expect(findRuleTextsByClass(RED_TEXT_CLASS).length).toBe(0);
expect(passwordInputElement.classList.contains(INVALID_INPUT_CLASS)).toBe(false);
});
});
describe('when password complexity validation failed', () => {
const password = '11111111';
beforeEach(() => {
mockAxios.onPost(PASSWORD_COMPLEXITY_PATH).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
});
......
......@@ -187,16 +187,6 @@
it 'returns basic list' do
expect(password_rule_list(true)).to match_array([:length, :common, :user_info])
end
context 'when display_password_requirements is disabled' do
before do
stub_feature_flags(display_password_requirements: false)
end
it 'returns an empty array' do
expect(password_rule_list(true)).to be_empty
end
end
end
context 'with all rules' do
......
......@@ -62,38 +62,4 @@
end
end
end
describe '#display_password_requirements?' do
subject(:display_password_requirements?) { helper.display_password_requirements? }
context 'when password complexity feature is not available' do
it { is_expected.to be(false) }
end
context 'when password complexity feature is available' do
before do
stub_licensed_features(password_complexity: true)
end
it { is_expected.to be(true) }
end
context 'when display_password_requirements is disabled' do
before do
stub_feature_flags(display_password_requirements: false)
end
context 'when password complexity feature is not available' do
it { is_expected.to be(false) }
end
context 'when password complexity feature is available' do
before do
stub_licensed_features(password_complexity: true)
end
it { is_expected.to be(false) }
end
end
end
end
......@@ -3,7 +3,6 @@
require 'spec_helper'
RSpec.describe 'devise/registrations/_password_input', feature_category: :system_access do
let(:display_password_requirements) { false }
let(:minimum_password_length) { 8 }
let(:password_length_hint) { "Minimum length is #{minimum_password_length} characters." }
......@@ -11,10 +10,10 @@
allow(view).to receive_messages(
form: instance_double(Gitlab::FormBuilders::GitlabUiFormBuilder, label: '_label_'),
form_resource_name: 'new_user',
preregistration_tracking_label: '',
display_password_requirements?: display_password_requirements
preregistration_tracking_label: ''
)
stub_licensed_features(password_complexity: false)
assign(:minimum_password_length, minimum_password_length)
end
......@@ -25,7 +24,9 @@
end
describe 'when displaying password requirements' do
let(:display_password_requirements) { true }
before do
stub_licensed_features(password_complexity: true)
end
it 'does not render the password length hint' do
render
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment