summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-09-16 03:13:19 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-16 03:13:19 +0000
commit0e6ff93eba3ed1ba4c7be0ec78a76dde71a47285 (patch)
tree5644055ecab86d9f68a1ac701ca8ac8e9219ce77 /spec
parentd746accf38bc9304f29c2bc4a70dfdc6565170f7 (diff)
downloadgitlab-ce-0e6ff93eba3ed1ba4c7be0ec78a76dde71a47285.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/registrations_controller_spec.rb27
-rw-r--r--spec/finders/groups_finder_spec.rb102
-rw-r--r--spec/initializers/settings_spec.rb36
-rw-r--r--spec/lib/security/weak_passwords_spec.rb112
-rw-r--r--spec/models/user_spec.rb52
5 files changed, 329 insertions, 0 deletions
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index fcf7331423c..2af04918882 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -493,6 +493,33 @@ RSpec.describe RegistrationsController do
end
end
end
+
+ context 'when the password is weak' do
+ render_views
+ let_it_be(:new_user_params) { { new_user: base_user_params.merge({ password: "password" }) } }
+
+ subject { post(:create, params: new_user_params) }
+
+ context 'when block_weak_passwords is enabled (default)' do
+ it 'renders the form with errors' do
+ expect { subject }.not_to change(User, :count)
+
+ expect(controller.current_user).to be_nil
+ expect(response).to render_template(:new)
+ expect(response.body).to include(_('Password must not contain commonly used combinations of words and letters'))
+ end
+ end
+
+ context 'when block_weak_passwords is disabled' do
+ before do
+ stub_feature_flags(block_weak_passwords: false)
+ end
+
+ it 'permits weak passwords' do
+ expect { subject }.to change(User, :count).by(1)
+ end
+ end
+ end
end
describe '#destroy' do
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index a4cbee6a124..123df418f8d 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -261,6 +261,108 @@ RSpec.describe GroupsFinder do
end
end
end
+
+ context 'with include_ancestors' do
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:parent_group) { create(:group, :public) }
+ let_it_be(:public_subgroup) { create(:group, :public, parent: parent_group) }
+ let_it_be(:public_subgroup2) { create(:group, :public, parent: parent_group) }
+ let_it_be(:private_subgroup1) { create(:group, :private, parent: parent_group) }
+ let_it_be(:internal_sub_subgroup) { create(:group, :internal, parent: public_subgroup) }
+ let_it_be(:public_sub_subgroup) { create(:group, :public, parent: public_subgroup) }
+ let_it_be(:private_subgroup2) { create(:group, :private, parent: parent_group) }
+ let_it_be(:private_sub_subgroup) { create(:group, :private, parent: private_subgroup2) }
+ let_it_be(:private_sub_sub_subgroup) { create(:group, :private, parent: private_sub_subgroup) }
+
+ context 'if include_ancestors is true' do
+ let(:params) { { include_ancestors: true } }
+
+ it 'returns ancestors of user groups' do
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_subgroup2,
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+
+ it 'returns subgroup if user is member of project of subgroup' do
+ project = create(:project, :private, namespace: private_sub_subgroup)
+ project.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_subgroup2,
+ private_sub_subgroup
+ )
+ end
+
+ it 'returns only groups related to user groups if all_available is false' do
+ params[:all_available] = false
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ private_subgroup2,
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+ end
+
+ context 'if include_ancestors is false' do
+ let(:params) { { include_ancestors: false } }
+
+ it 'does not return private ancestors of user groups' do
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+
+ it "returns project's parent group if user is member of project" do
+ project = create(:project, :private, namespace: private_sub_subgroup)
+ project.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_sub_subgroup
+ )
+ end
+
+ it 'returns only user groups and their descendants if all_available is false' do
+ params[:all_available] = false
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+ end
+ end
end
describe '#execute' do
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
index 71ea12a41aa..c3200d2fab1 100644
--- a/spec/initializers/settings_spec.rb
+++ b/spec/initializers/settings_spec.rb
@@ -58,4 +58,40 @@ RSpec.describe Settings do
end
end
end
+
+ describe "#weak_passwords_digest_set" do
+ subject { described_class.gitlab.weak_passwords_digest_set }
+
+ it 'is a Set' do
+ expect(subject).to be_kind_of(Set)
+ end
+
+ it 'contains 4500 password digests' do
+ expect(subject.length).to eq(4500)
+ end
+
+ it 'includes 8 char weak password digest' do
+ expect(subject).to include(digest("password"))
+ end
+
+ it 'includes 16 char weak password digest' do
+ expect(subject).to include(digest("progressivehouse"))
+ end
+
+ it 'includes long char weak password digest' do
+ expect(subject).to include(digest("01234567890123456789"))
+ end
+
+ it 'does not include 7 char weak password digest' do
+ expect(subject).not_to include(digest("1234567"))
+ end
+
+ it 'does not include plaintext' do
+ expect(subject).not_to include("password")
+ end
+
+ def digest(plaintext)
+ Digest::SHA256.base64digest(plaintext)
+ end
+ end
end
diff --git a/spec/lib/security/weak_passwords_spec.rb b/spec/lib/security/weak_passwords_spec.rb
new file mode 100644
index 00000000000..9d12c352abf
--- /dev/null
+++ b/spec/lib/security/weak_passwords_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::WeakPasswords do
+ describe "#weak_for_user?" do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:user) do
+ build_stubbed(:user, username: "56d4ab689a_win",
+ name: "Weakést McWeaky-Pass Jr",
+ email: "predictāble.ZZZ+seventeen@examplecorp.com",
+ public_email: "fortunate@acme.com"
+ )
+ end
+
+ where(:password, :too_weak) do
+ # A random password is not too weak
+ "d2262d56" | false
+
+ # The case-insensitive weak password list
+ "password" | true
+ "pAssWord" | true
+ "princeofdarkness" | true
+
+ # Forbidden substrings
+ "A1B2gitlabC3" | true
+ "gitlab123" | true
+ "theonedevopsplatform" | true
+ "A1gitlib" | false
+
+ # Predicatable name substrings
+ "Aweakést" | true
+ "!@mCwEaKy" | true
+ "A1B2pass" | true
+ "A1B2C3jr" | false # jr is too short
+
+ # Predictable username substrings
+ "56d4ab689a" | true
+ "56d4ab689a_win" | true
+ "56d4ab68" | false # it's part of the username, but not a full part
+ "A1B2Cwin" | false # win is too short
+
+ # Predictable user.email substrings
+ "predictāble.ZZZ+seventeen@examplecorp.com" | true
+ "predictable.ZZZ+seventeen@examplecorp.com" | true
+ "predictāble.ZZZ+seventeen" | true
+ "examplecorp.com" | true
+ "!@exAmplecorp" | true
+ "predictāble123" | true
+ "seventeen" | true
+ "predictable" | false # the accent is different
+ "A1B2CZzZ" | false # ZZZ is too short
+ # Other emails are not considered
+ "fortunate@acme.com" | false
+ "A1B2acme" | false
+ "fortunate" | false
+
+ # A short password is not automatically too weak
+ # We rely on User's password length validation, not WeakPasswords.
+ "1" | false
+ "1234567" | false
+ # But a short password with forbidden words or user attributes
+ # is still weak
+ "gitlab" | true
+ "pass" | true
+ end
+
+ with_them do
+ it { expect(subject.weak_for_user?(password, user)).to eq(too_weak) }
+ end
+
+ context 'with a user who has short email parts' do
+ before do
+ user.email = 'sid@1.io'
+ end
+
+ where(:password, :too_weak) do
+ "11111111" | true # This is on the weak password list
+ "1.ioABCD" | true # 1.io is long enough to match
+ "sid@1.io" | true # matches the email in full
+ "sid@1.ioAB" | true
+ # sid, 1, and io on their own are too short
+ "sid1ioAB" | false
+ "sidsidsi" | false
+ "ioioioio" | false
+ end
+
+ with_them do
+ it { expect(subject.weak_for_user?(password, user)).to eq(too_weak) }
+ end
+ end
+
+ context 'with a user who is missing attributes' do
+ before do
+ user.name = nil
+ user.email = nil
+ user.username = nil
+ end
+
+ where(:password, :too_weak) do
+ "d2262d56" | false
+ "password" | true
+ "gitlab123" | true
+ end
+
+ with_them do
+ it { expect(subject.weak_for_user?(password, user)).to eq(too_weak) }
+ end
+ end
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3487bf7b70e..04f2c7f9176 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -334,6 +334,58 @@ RSpec.describe User do
end
end
end
+
+ context 'check_password_weakness' do
+ let(:weak_password) { "qwertyuiop" }
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(block_weak_passwords: false)
+ end
+
+ it 'does not add an error when password is weak' do
+ expect(Security::WeakPasswords).not_to receive(:weak_for_user?)
+
+ user.password = weak_password
+ expect(user).to be_valid
+ end
+ end
+
+ context 'when feature flag is enabled' do
+ before do
+ stub_feature_flags(block_weak_passwords: true)
+ end
+
+ it 'checks for password weakness when password changes' do
+ expect(Security::WeakPasswords).to receive(:weak_for_user?)
+ .with(weak_password, user).and_call_original
+ user.password = weak_password
+ expect(user).not_to be_valid
+ end
+
+ it 'adds an error when password is weak' do
+ user.password = weak_password
+ expect(user).not_to be_valid
+ expect(user.errors).to be_of_kind(:password, 'must not contain commonly used combinations of words and letters')
+ end
+
+ it 'is valid when password is not weak' do
+ user.password = ::User.random_password
+ expect(user).to be_valid
+ end
+
+ it 'is valid when weak password was already set' do
+ user = build(:user, password: weak_password)
+ user.save!(validate: false)
+
+ expect(Security::WeakPasswords).not_to receive(:weak_for_user?)
+
+ # Change an unrelated value
+ user.name = "Example McExampleFace"
+ expect(user).to be_valid
+ end
+ end
+ end
end
describe 'name' do