Commit 3d3fa624 authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets Committed by Ezekiel Kigbo

Require namespace path to be 2 chars long

This will affect Group URL and username (in URL).

This will allow to keep one letter routes for service needs.
Also it should improve autocomplete quality. Also most of 1
and 2 character length usernames are usually taken by bots.
Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parent 530ddc69
......@@ -114,7 +114,7 @@ export default class GlFieldError {
this.state.empty = currentValue === '';
this.state.submitted = true;
this.renderValidity();
this.form.focusOnFirstInvalid.apply(this.form);
this.form.focusInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup
this.inputElement
......
......@@ -52,10 +52,23 @@ export default class GlFieldErrors {
});
}
focusOnFirstInvalid() {
const firstInvalid = this.state.inputs.filter(
input => !input.inputDomElement.validity.valid,
)[0];
firstInvalid.inputElement.focus();
get invalidInputs() {
return this.state.inputs.filter(
({
inputDomElement: {
validity: { valid },
},
}) => !valid,
);
}
get focusedInvalidInput() {
return this.invalidInputs.find(({ inputElement }) => inputElement.is(':focus'));
}
focusInvalid() {
if (this.focusedInvalidInput) return;
this.invalidInputs[0].inputElement.focus();
}
}
......@@ -21,11 +21,24 @@ export default class LengthValidator extends InputValidator {
);
const { value } = this.inputDomElement;
const { maxLengthMessage, maxLength } = this.inputDomElement.dataset;
this.errorMessage = maxLengthMessage;
this.invalidInput = value.length > parseInt(maxLength, 10);
const {
minLength,
minLengthMessage,
maxLengthMessage,
maxLength,
} = this.inputDomElement.dataset;
this.invalidInput = false;
if (value.length > parseInt(maxLength, 10)) {
this.invalidInput = true;
this.errorMessage = maxLengthMessage;
}
if (value.length < parseInt(minLength, 10)) {
this.invalidInput = true;
this.errorMessage = minLengthMessage;
}
this.setValidationStateAndMessage();
}
......
......@@ -39,7 +39,7 @@ export default class UsernameValidator extends InputValidator {
static validateUsernameInput(inputDomElement) {
const username = inputDomElement.value;
if (inputDomElement.checkValidity() && username.length > 0) {
if (inputDomElement.checkValidity() && username.length > 1) {
UsernameValidator.setMessageVisibility(inputDomElement, pendingMessageSelector);
UsernameValidator.fetchUsernameAvailability(username)
.then(usernameTaken => {
......
......@@ -48,6 +48,13 @@ class Namespace < ApplicationRecord
length: { maximum: 255 },
namespace_path: true
# Introduce minimal path length of 2 characters.
# Allow change of other attributes without forcing users to
# rename their user or group. At the same time prevent changing
# the path without complying with new 2 chars requirement.
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/225214
validates :path, length: { minimum: 2 }, if: :path_changed?
validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
validate :nesting_level_allowed
......
- max_first_name_length = max_last_name_length = 127
- max_username_length = 255
- min_username_length = 2
.signup-box.p-3.mb-2
.signup-body
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
......@@ -16,7 +17,7 @@
= f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last Name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_lastname_field' }, required: true, title: _("This field is required.")
.username.form-group
= f.label :username, class: 'label-bold'
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => _("Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :min_length => min_username_length, :min_length_message => s_("SignUp|Username is too short (minimum is %{min_length} characters).") % { min_length: min_username_length }, :max_length => max_username_length, :max_length_message => _("Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
%p.validation-error.gl-field-error-ignore.field-validation.mt-1.hide.cred= _('Username is already taken.')
%p.validation-success.gl-field-error-ignore.field-validation.mt-1.hide.cgreen= _('Username is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.mt-1.hide= _('Checking username availability...')
......
- max_name_length = 255
- max_username_length = 255
- min_username_length = 2
#register-pane.tab-pane.login-box{ role: 'tabpanel' }
.login-body
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
......@@ -12,7 +13,7 @@
= f.text_field :name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length }, :qa_selector => 'new_user_name_field' }, required: true, title: _("This field is required.")
.username.form-group
= f.label :username, class: 'label-bold'
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :min_length => min_username_length, :min_length_message => s_("SignUp|Username is too short (minimum is %{min_length} characters).") % { min_length: min_username_length }, :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
%p.validation-error.gl-field-error-ignore.field-validation.hide= _('Username is already taken.')
%p.validation-success.gl-field-error-ignore.field-validation.hide= _('Username is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking username availability...')
......
---
title: Require namespace path (and username) to be at least 2 chars long
merge_request: 35649
author:
type: changed
......@@ -999,8 +999,8 @@ RSpec.describe User do
describe '#managed_free_namespaces' do
let_it_be(:user) { create(:user) }
let_it_be(:licensed_group) { create(:group, gitlab_subscription: create(:gitlab_subscription, :bronze)) }
let_it_be(:free_group_z) { create(:group, name: 'Z', gitlab_subscription: create(:gitlab_subscription, :free)) }
let_it_be(:free_group_a) { create(:group, name: 'A', gitlab_subscription: create(:gitlab_subscription, :free)) }
let_it_be(:free_group_z) { create(:group, name: 'AZ', gitlab_subscription: create(:gitlab_subscription, :free)) }
let_it_be(:free_group_a) { create(:group, name: 'AA', gitlab_subscription: create(:gitlab_subscription, :free)) }
subject { user.managed_free_namespaces }
......
......@@ -21013,6 +21013,9 @@ msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "SignUp|Username is too short (minimum is %{min_length} characters)."
msgstr ""
msgid "Signed in"
msgstr ""
......
......@@ -70,6 +70,13 @@ RSpec.shared_examples 'Signup' do
expect(page).to have_content("Username is too long (maximum is 255 characters).")
end
it 'shows an error message if the username is less than 2 characters' do
fill_in 'new_user_username', with: 'u'
wait_for_requests
expect(page).to have_content("Username is too short (minimum is 2 characters).")
end
it 'shows an error message on submit if the username contains special characters' do
fill_in 'new_user_username', with: 'new$user!username'
wait_for_requests
......
......@@ -65,6 +65,36 @@ RSpec.describe Namespace do
it { expect(group).to be_valid }
end
end
describe '1 char path length' do
it 'does not allow to create one' do
namespace = build(:namespace, path: 'j')
expect(namespace).not_to be_valid
expect(namespace.errors[:path].first).to eq('is too short (minimum is 2 characters)')
end
it 'does not allow to update one' do
namespace = create(:namespace)
namespace.update(path: 'j')
expect(namespace).not_to be_valid
expect(namespace.errors[:path].first).to eq('is too short (minimum is 2 characters)')
end
it 'allows updating other attributes for existing record' do
namespace = build(:namespace, path: 'j')
namespace.save(validate: false)
namespace.reload
expect(namespace.path).to eq('j')
namespace.update(name: 'something new')
expect(namespace).to be_valid
expect(namespace.name).to eq('something new')
end
end
end
describe 'delegate' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment