diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-29 12:57:02 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-29 12:57:02 +0000 |
commit | e0ab280b774e34fcfd6fd031616247714230ca68 (patch) | |
tree | 472ee2dcef05f242e1b861caa47a0a5179e92f4c /spec | |
parent | 60b56b48afb89ed1890409b6c425f16549c4d28b (diff) | |
download | gitlab-ce-e0ab280b774e34fcfd6fd031616247714230ca68.tar.gz |
Add latest changes from gitlab-org/security/gitlab@14-3-stable-ee
Diffstat (limited to 'spec')
22 files changed, 470 insertions, 225 deletions
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 015c36c9335..e43ba6358f9 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -807,5 +807,20 @@ RSpec.describe Admin::UsersController do expect(response).to have_gitlab_http_status(:not_found) end end + + context 'when impersonating an admin and attempting to impersonate again' do + let(:admin2) { create(:admin) } + + before do + post :impersonate, params: { id: admin2.username } + end + + it 'does not allow double impersonation', :aggregate_failures do + post :impersonate, params: { id: user.username } + + expect(flash[:alert]).to eq(_('You are already impersonating another user')) + expect(warden.user).to eq(admin2) + end + end end end diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb index 3e4b159271a..568712d29cb 100644 --- a/spec/controllers/import/gitea_controller_spec.rb +++ b/spec/controllers/import/gitea_controller_spec.rb @@ -54,6 +54,48 @@ RSpec.describe Import::GiteaController do end end end + + context 'when DNS Rebinding protection is enabled' do + let(:token) { 'gitea token' } + + let(:ip_uri) { 'http://167.99.148.217' } + let(:uri) { 'try.gitea.io' } + let(:https_uri) { "https://#{uri}" } + let(:http_uri) { "http://#{uri}" } + + before do + session[:gitea_access_token] = token + + allow(Gitlab::UrlBlocker).to receive(:validate!).with(https_uri, anything).and_return([Addressable::URI.parse(https_uri), uri]) + allow(Gitlab::UrlBlocker).to receive(:validate!).with(http_uri, anything).and_return([Addressable::URI.parse(ip_uri), uri]) + + allow(Gitlab::LegacyGithubImport::Client).to receive(:new).and_return(double('Gitlab::LegacyGithubImport::Client', repos: [], orgs: [])) + end + + context 'when provided host url is using https' do + let(:host_url) { https_uri } + + it 'uses unchanged host url to send request to Gitea' do + expect(Gitlab::LegacyGithubImport::Client).to receive(:new).with(token, host: https_uri, api_version: 'v1', hostname: 'try.gitea.io') + + get :status, format: :json + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when provided host url is using http' do + let(:host_url) { http_uri } + + it 'uses changed host url to send request to Gitea' do + expect(Gitlab::LegacyGithubImport::Client).to receive(:new).with(token, host: 'http://167.99.148.217', api_version: 'v1', hostname: 'try.gitea.io') + + get :status, format: :json + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end end diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index f21ef324884..5bf3b4c48bf 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -98,6 +98,19 @@ RSpec.describe Oauth::ApplicationsController do end describe 'POST #create' do + let(:oauth_params) do + { + doorkeeper_application: { + name: 'foo', + redirect_uri: redirect_uri, + scopes: scopes + } + } + end + + let(:redirect_uri) { 'http://example.org' } + let(:scopes) { ['api'] } + subject { post :create, params: oauth_params } it 'creates an application' do @@ -116,38 +129,42 @@ RSpec.describe Oauth::ApplicationsController do expect(response).to redirect_to(profile_path) end - context 'redirect_uri' do + context 'when redirect_uri is invalid' do + let(:redirect_uri) { 'javascript://alert()' } + render_views it 'shows an error for a forbidden URI' do - invalid_uri_params = { - doorkeeper_application: { - name: 'foo', - redirect_uri: 'javascript://alert()', - scopes: ['api'] - } - } - - post :create, params: invalid_uri_params + subject expect(response.body).to include 'Redirect URI is forbidden by the server' + expect(response).to render_template('doorkeeper/applications/index') end end context 'when scopes are not present' do + let(:scopes) { [] } + render_views it 'shows an error for blank scopes' do - invalid_uri_params = { - doorkeeper_application: { - name: 'foo', - redirect_uri: 'http://example.org' - } - } - - post :create, params: invalid_uri_params + subject expect(response.body).to include 'Scopes can't be blank' + expect(response).to render_template('doorkeeper/applications/index') + end + end + + context 'when scopes are invalid' do + let(:scopes) { %w(api foo) } + + render_views + + it 'shows an error for invalid scopes' do + subject + + expect(response.body).to include 'Scopes doesn't match configured on the server.' + expect(response).to render_template('doorkeeper/applications/index') end end @@ -185,14 +202,4 @@ RSpec.describe Oauth::ApplicationsController do def disable_user_oauth allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false) end - - def oauth_params - { - doorkeeper_application: { - name: 'foo', - redirect_uri: 'http://example.org', - scopes: ['api'] - } - } - end end diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index c9059395377..893dd2c76e0 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -78,40 +78,80 @@ RSpec.describe 'Profile > Password' do end end - context 'Change passowrd' do + context 'Change password' do + let(:new_password) { '22233344' } + before do sign_in(user) visit(edit_profile_password_path) end - it 'does not change user passowrd without old one' do - page.within '.update-password' do - fill_passwords('22233344', '22233344') + shared_examples 'user enters an incorrect current password' do + subject do + page.within '.update-password' do + fill_in 'user_current_password', with: user_current_password + fill_passwords(new_password, new_password) + end end - page.within '.flash-container' do - expect(page).to have_content 'You must provide a valid current password' - end - end + it 'handles the invalid password attempt, and prompts the user to try again', :aggregate_failures do + expect(Gitlab::AppLogger).to receive(:info) + .with(message: 'Invalid current password when attempting to update user password', username: user.username, ip: user.current_sign_in_ip) + + subject + + user.reload - it 'does not change password with invalid old password' do - page.within '.update-password' do - fill_in 'user_current_password', with: 'invalid' - fill_passwords('password', 'confirmation') + expect(user.failed_attempts).to eq(1) + expect(user.valid_password?(new_password)).to eq(false) + expect(current_path).to eq(edit_profile_password_path) + + page.within '.flash-container' do + expect(page).to have_content('You must provide a valid current password') + end end - page.within '.flash-container' do - expect(page).to have_content 'You must provide a valid current password' + it 'locks the user account when user passes the maximum attempts threshold', :aggregate_failures do + user.update!(failed_attempts: User.maximum_attempts.pred) + + subject + + expect(current_path).to eq(new_user_session_path) + + page.within '.flash-container' do + expect(page).to have_content('Your account is locked.') + end end end - it 'changes user password' do - page.within '.update-password' do - fill_in "user_current_password", with: user.password - fill_passwords('22233344', '22233344') + context 'when current password is blank' do + let(:user_current_password) { nil } + + it_behaves_like 'user enters an incorrect current password' + end + + context 'when current password is incorrect' do + let(:user_current_password) {'invalid' } + + it_behaves_like 'user enters an incorrect current password' + end + + context 'when the password reset is successful' do + subject do + page.within '.update-password' do + fill_in "user_current_password", with: user.password + fill_passwords(new_password, new_password) + end end - expect(current_path).to eq new_user_session_path + it 'changes the password, logs the user out and prompts them to sign in again', :aggregate_failures do + expect { subject }.to change { user.reload.valid_password?(new_password) }.to(true) + expect(current_path).to eq new_user_session_path + + page.within '.flash-container' do + expect(page).to have_content('Password was successfully updated. Please sign in again.') + end + end end end diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json index e3aeace6383..1072e63b20b 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/project.json +++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json @@ -7579,23 +7579,6 @@ } } ], - "triggers": [ - { - "id": 123, - "token": "cdbfasdf44a5958c83654733449e585", - "project_id": 5, - "owner_id": 1, - "created_at": "2017-01-16T15:25:28.637Z", - "updated_at": "2017-01-16T15:25:28.637Z" - }, - { - "id": 456, - "token": "33a66349b5ad01fc00174af87804e40", - "project_id": 5, - "created_at": "2017-01-16T15:25:29.637Z", - "updated_at": "2017-01-16T15:25:29.637Z" - } - ], "pipeline_schedules": [ { "id": 1, diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson index 93619f4fb44..2b5bda687b8 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson +++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson @@ -1,2 +1,2 @@ {"id":123,"token":"cdbfasdf44a5958c83654733449e585","project_id":5,"owner_id":1,"created_at":"2017-01-16T15:25:28.637Z","updated_at":"2017-01-16T15:25:28.637Z"} -{"id":456,"token":"33a66349b5ad01fc00174af87804e40","project_id":5,"created_at":"2017-01-16T15:25:29.637Z","updated_at":"2017-01-16T15:25:29.637Z"} +{"id":456,"token":"33a66349b5ad01fc00174af87804e40","project_id":5,"created_at":"2017-01-16T15:25:29.637Z","updated_at":"2017-01-16T15:25:29.637Z"}
\ No newline at end of file diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js index 211ed064762..94ad7759110 100644 --- a/spec/frontend/gfm_auto_complete_spec.js +++ b/spec/frontend/gfm_auto_complete_spec.js @@ -574,6 +574,15 @@ describe('GfmAutoComplete', () => { }), ).toBe('<li><small>grp/proj#5</small> Some Issue</li>'); }); + + it('escapes title in the template as it is user input', () => { + expect( + GfmAutoComplete.Issues.templateFunction({ + id: 5, + title: '${search}<script>oh no $', // eslint-disable-line no-template-curly-in-string + }), + ).toBe('<li><small>5</small> ${search}<script>oh no $</li>'); + }); }); describe('GfmAutoComplete.Members', () => { @@ -608,16 +617,18 @@ describe('GfmAutoComplete', () => { ).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>'); }); - it('should add escaped title if title is set', () => { + it('escapes title in the template as it is user input', () => { expect( GfmAutoComplete.Members.templateFunction({ avatarTag: 'IMG', username: 'my-group', - title: 'MyGroup+', + title: '${search}<script>oh no $', // eslint-disable-line no-template-curly-in-string icon: '<i class="icon"/>', availabilityStatus: '', }), - ).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>'); + ).toBe( + '<li>IMG my-group <small>${search}<script>oh no $</small> <i class="icon"/></li>', + ); }); it('should add user availability status if availabilityStatus is set', () => { @@ -782,6 +793,15 @@ describe('GfmAutoComplete', () => { ${'/unlabel ~'} | ${assignedLabels} `('$input shows $output.length labels', expectLabels); }); + + it('escapes title in the template as it is user input', () => { + const color = '#123456'; + const title = '${search}<script>oh no $'; // eslint-disable-line no-template-curly-in-string + + expect(GfmAutoComplete.Labels.templateFunction(color, title)).toBe( + '<li><span class="dropdown-label-box" style="background: #123456"></span> ${search}<script>oh no $</li>', + ); + }); }); describe('emoji', () => { @@ -829,4 +849,15 @@ describe('GfmAutoComplete', () => { }); }); }); + + describe('milestones', () => { + it('escapes title in the template as it is user input', () => { + const expired = false; + const title = '${search}<script>oh no $'; // eslint-disable-line no-template-curly-in-string + + expect(GfmAutoComplete.Milestones.templateFunction(title, expired)).toBe( + '<li>${search}<script>oh no $</li>', + ); + }); + }); }); diff --git a/spec/initializers/100_patch_omniauth_oauth2_spec.rb b/spec/initializers/100_patch_omniauth_oauth2_spec.rb new file mode 100644 index 00000000000..0c436e4ef45 --- /dev/null +++ b/spec/initializers/100_patch_omniauth_oauth2_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'OmniAuth::Strategies::OAuth2', type: :strategy do + let(:strategy) { [OmniAuth::Strategies::OAuth2] } + + it 'verifies the gem version' do + current_version = OmniAuth::OAuth2::VERSION + expected_version = '1.7.1' + + expect(current_version).to eq(expected_version), <<~EOF + New version #{current_version} of the `omniauth-oauth2` gem detected! + + Please check if the monkey patches in `config/initializers_before_autoloader/100_patch_omniauth_oauth2.rb` + are still needed, and either update/remove them, or bump the version in this spec. + + EOF + end + + context 'when a custom error message is passed from an OAuth2 provider' do + let(:message) { 'Please go to https://evil.com' } + let(:state) { 'secret' } + let(:callback_path) { '/users/auth/oauth2/callback' } + let(:params) { { state: state, error: 'evil_key', error_description: message } } + let(:error) { last_request.env['omniauth.error'] } + + before do + env('rack.session', { 'omniauth.state' => state }) + end + + it 'returns the custom error message if the state is valid' do + get callback_path, **params + + expect(error.message).to eq("evil_key | #{message}") + end + + it 'returns the custom `error_reason` message if the `error_description` is blank' do + get callback_path, **params.merge(error_description: ' ', error_reason: 'custom reason') + + expect(error.message).to eq('evil_key | custom reason') + end + + it 'returns a CSRF error if the state is invalid' do + get callback_path, **params.merge(state: 'invalid') + + expect(error.message).to eq('csrf_detected | CSRF detected') + end + + it 'returns a CSRF error if the state is missing' do + get callback_path, **params.without(:state) + + expect(error.message).to eq('csrf_detected | CSRF detected') + end + end +end diff --git a/spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb b/spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb index f906870195a..876c23a91bd 100644 --- a/spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb +++ b/spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb @@ -3,33 +3,50 @@ require 'spec_helper' RSpec.describe Gitlab::Auth::TwoFactorAuthVerifier do - let(:user) { create(:user) } + using RSpec::Parameterized::TableSyntax - subject { described_class.new(user) } + subject(:verifier) { described_class.new(user) } - describe '#two_factor_authentication_required?' do - describe 'when it is required on application level' do - it 'returns true' do - stub_application_setting require_two_factor_authentication: true + let(:user) { build_stubbed(:user, otp_grace_period_started_at: Time.zone.now) } - expect(subject.two_factor_authentication_required?).to be_truthy - end - end + describe '#two_factor_authentication_enforced?' do + subject { verifier.two_factor_authentication_enforced? } - describe 'when it is required on group level' do - it 'returns true' do - allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(true) + where(:instance_level_enabled, :group_level_enabled, :grace_period_expired, :should_be_enforced) do + false | false | true | false + true | false | false | false + true | false | true | true + false | true | false | false + false | true | true | true + end - expect(subject.two_factor_authentication_required?).to be_truthy + with_them do + before do + stub_application_setting(require_two_factor_authentication: instance_level_enabled) + allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(group_level_enabled) + stub_application_setting(two_factor_grace_period: grace_period_expired ? 0 : 1.month.in_hours) end + + it { is_expected.to eq(should_be_enforced) } end + end - describe 'when it is not required' do - it 'returns false when not required on group level' do - allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(false) + describe '#two_factor_authentication_required?' do + subject { verifier.two_factor_authentication_required? } + + where(:instance_level_enabled, :group_level_enabled, :should_be_required) do + true | false | true + false | true | true + false | false | false + end - expect(subject.two_factor_authentication_required?).to be_falsey + with_them do + before do + stub_application_setting(require_two_factor_authentication: instance_level_enabled) + allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(group_level_enabled) end + + it { is_expected.to eq(should_be_required) } end end @@ -85,25 +102,21 @@ RSpec.describe Gitlab::Auth::TwoFactorAuthVerifier do end describe '#two_factor_grace_period_expired?' do - before do - allow(user).to receive(:otp_grace_period_started_at).and_return(4.hours.ago) - end - it 'returns true if the grace period has expired' do - allow(subject).to receive(:two_factor_grace_period).and_return(2) + stub_application_setting two_factor_grace_period: 0 expect(subject.two_factor_grace_period_expired?).to be_truthy end it 'returns false if the grace period has not expired' do - allow(subject).to receive(:two_factor_grace_period).and_return(6) + stub_application_setting two_factor_grace_period: 1.month.in_hours expect(subject.two_factor_grace_period_expired?).to be_falsey end context 'when otp_grace_period_started_at is nil' do it 'returns false' do - allow(user).to receive(:otp_grace_period_started_at).and_return(nil) + user.otp_grace_period_started_at = nil expect(subject.two_factor_grace_period_expired?).to be_falsey end diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index cc592bb8f24..5ec6e23774a 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -386,7 +386,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do shared_examples 'with an invalid access token' do it 'fails for a non-member' do expect(gl_auth.find_for_git_client(project_bot_user.username, access_token.token, project: project, ip: 'ip')) - .to have_attributes(auth_failure ) + .to have_attributes(auth_failure) end context 'when project bot user is blocked' do @@ -396,7 +396,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it 'fails for a blocked project bot' do expect(gl_auth.find_for_git_client(project_bot_user.username, access_token.token, project: project, ip: 'ip')) - .to have_attributes(auth_failure ) + .to have_attributes(auth_failure) end end end @@ -466,6 +466,41 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do .to have_attributes(auth_failure) end + context 'when 2fa is enabled globally' do + let_it_be(:user) do + create(:user, username: 'normal_user', password: 'my-secret', otp_grace_period_started_at: 1.day.ago) + end + + before do + stub_application_setting(require_two_factor_authentication: true) + end + + it 'fails if grace period expired' do + stub_application_setting(two_factor_grace_period: 0) + + expect { gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip') } + .to raise_error(Gitlab::Auth::MissingPersonalAccessTokenError) + end + + it 'goes through if grace period is not expired yet' do + stub_application_setting(two_factor_grace_period: 72) + + expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip')) + .to have_attributes(actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities) + end + end + + context 'when 2fa is enabled personally' do + let(:user) do + create(:user, :two_factor, username: 'normal_user', password: 'my-secret', otp_grace_period_started_at: 1.day.ago) + end + + it 'fails' do + expect { gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip') } + .to raise_error(Gitlab::Auth::MissingPersonalAccessTokenError) + end + end + it 'goes through lfs authentication' do user = create( :user, @@ -757,16 +792,16 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do describe 'find_with_user_password' do let!(:user) do create(:user, - username: username, - password: password, - password_confirmation: password) + username: username, + password: password, + password_confirmation: password) end let(:username) { 'John' } # username isn't lowercase, test this let(:password) { 'my-secret' } it "finds user by valid login/password" do - expect( gl_auth.find_with_user_password(username, password) ).to eql user + expect(gl_auth.find_with_user_password(username, password)).to eql user end it 'finds user by valid email/password with case-insensitive email' do @@ -779,12 +814,12 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it "does not find user with invalid password" do password = 'wrong' - expect( gl_auth.find_with_user_password(username, password) ).not_to eql user + expect(gl_auth.find_with_user_password(username, password)).not_to eql user end it "does not find user with invalid login" do user = 'wrong' - expect( gl_auth.find_with_user_password(username, password) ).not_to eql user + expect(gl_auth.find_with_user_password(username, password)).not_to eql user end include_examples 'user login operation with unique ip limit' do @@ -796,13 +831,13 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it 'finds the user in deactivated state' do user.deactivate! - expect( gl_auth.find_with_user_password(username, password) ).to eql user + expect(gl_auth.find_with_user_password(username, password)).to eql user end it "does not find user in blocked state" do user.block - expect( gl_auth.find_with_user_password(username, password) ).not_to eql user + expect(gl_auth.find_with_user_password(username, password)).not_to eql user end it 'does not find user in locked state' do @@ -814,13 +849,13 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it "does not find user in ldap_blocked state" do user.ldap_block - expect( gl_auth.find_with_user_password(username, password) ).not_to eql user + expect(gl_auth.find_with_user_password(username, password)).not_to eql user end it 'does not find user in blocked_pending_approval state' do user.block_pending_approval - expect( gl_auth.find_with_user_password(username, password) ).not_to eql user + expect(gl_auth.find_with_user_password(username, password)).not_to eql user end context 'with increment_failed_attempts' do diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index bf682e4e4c6..bf2e3c7f5f8 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -435,17 +435,19 @@ RSpec.describe Gitlab::GitAccess do it 'disallows users with expired password to pull' do project.add_maintainer(user) - user.update!(password_expires_at: 2.minutes.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.minutes.ago) expect { pull_access_check }.to raise_forbidden("Your password expired. Please access GitLab from a web browser to update your password.") end - it 'allows ldap users with expired password to pull' do - project.add_maintainer(user) - user.update!(password_expires_at: 2.minutes.ago) - allow(user).to receive(:ldap_user?).and_return(true) + context 'with an ldap user' do + let(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } - expect { pull_access_check }.not_to raise_error + it 'allows ldap users with expired password to pull' do + project.add_maintainer(user) + + expect { pull_access_check }.not_to raise_error + end end context 'when the project repository does not exist' do @@ -987,24 +989,23 @@ RSpec.describe Gitlab::GitAccess do end it 'disallows users with expired password to push' do - user.update!(password_expires_at: 2.minutes.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.minutes.ago) expect { push_access_check }.to raise_forbidden("Your password expired. Please access GitLab from a web browser to update your password.") end - it 'allows ldap users with expired password to push' do - user.update!(password_expires_at: 2.minutes.ago) - allow(user).to receive(:ldap_user?).and_return(true) + context 'with an ldap user' do + let(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } - expect { push_access_check }.not_to raise_error - end + it 'allows ldap users with expired password to push' do + expect { push_access_check }.not_to raise_error + end - it 'disallows blocked ldap users with expired password to push' do - user.block - user.update!(password_expires_at: 2.minutes.ago) - allow(user).to receive(:ldap_user?).and_return(true) + it 'disallows blocked ldap users with expired password to push' do + user.block - expect { push_access_check }.to raise_forbidden("Your account has been blocked.") + expect { push_access_check }.to raise_forbidden("Your account has been blocked.") + end end it 'cleans up the files' do diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index 82f465c4f9e..518a9337826 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -445,8 +445,8 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do expect(@project.merge_requests.size).to eq(9) end - it 'only restores valid triggers' do - expect(@project.triggers.size).to eq(1) + it 'does not restore triggers' do + expect(@project.triggers.size).to eq(0) end it 'has the correct number of pipelines and statuses' do diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index a9efa32f986..287be24d11f 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -401,15 +401,6 @@ Ci::Variable: - encrypted_value - encrypted_value_salt - encrypted_value_iv -Ci::Trigger: -- id -- token -- project_id -- created_at -- updated_at -- owner_id -- description -- ref Ci::PipelineSchedule: - id - description @@ -556,7 +547,6 @@ Project: - disable_overriding_approvers_per_merge_request - merge_requests_ff_only_enabled - issues_template -- repository_size_limit - sync_time - service_desk_enabled - last_repository_updated_at diff --git a/spec/lib/gitlab/legacy_github_import/client_spec.rb b/spec/lib/gitlab/legacy_github_import/client_spec.rb index 0929b90d1f4..83ba5858d81 100644 --- a/spec/lib/gitlab/legacy_github_import/client_spec.rb +++ b/spec/lib/gitlab/legacy_github_import/client_spec.rb @@ -86,6 +86,15 @@ RSpec.describe Gitlab::LegacyGithubImport::Client do it 'builds a endpoint with the given options' do expect(client.api.api_endpoint).to eq 'https://try.gitea.io/api/v3/' end + + context 'and hostname' do + subject(:client) { described_class.new(token, host: 'https://167.99.148.217/', api_version: 'v1', hostname: 'try.gitea.io') } + + it 'builds a endpoint with the given options' do + expect(client.api.connection_options.dig(:headers, :host)).to eq 'try.gitea.io' + expect(client.api.api_endpoint).to eq 'https://167.99.148.217/api/v1/' + end + end end end diff --git a/spec/lib/gitlab/lfs_token_spec.rb b/spec/lib/gitlab/lfs_token_spec.rb index a8472062f03..3bc0bd385a7 100644 --- a/spec/lib/gitlab/lfs_token_spec.rb +++ b/spec/lib/gitlab/lfs_token_spec.rb @@ -126,7 +126,7 @@ RSpec.describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do end context 'when the user password is expired' do - let(:actor) { create(:user, password_expires_at: 1.minute.ago, password_automatically_set: true) } + let(:actor) { create(:user, password_expires_at: 1.minute.ago) } it 'returns false' do expect(lfs_token.token_valid?(lfs_token.token)).to be false @@ -135,12 +135,12 @@ RSpec.describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do end context 'when the actor is an ldap user' do - before do - allow(actor).to receive(:ldap_user?).and_return(true) - end + let(:actor) { create(:omniauth_user, provider: 'ldap') } context 'when the user is blocked' do - let(:actor) { create(:user, :blocked) } + before do + actor.block! + end it 'returns false' do expect(lfs_token.token_valid?(lfs_token.token)).to be false @@ -148,7 +148,9 @@ RSpec.describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do end context 'when the user password is expired' do - let(:actor) { create(:user, password_expires_at: 1.minute.ago) } + before do + actor.update!(password_expires_at: 1.minute.ago) + end it 'returns true' do expect(lfs_token.token_valid?(lfs_token.token)).to be true diff --git a/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb b/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb new file mode 100644 index 00000000000..0d0f6a3df67 --- /dev/null +++ b/spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration!('cleanup_orphan_project_access_tokens') + +RSpec.describe CleanupOrphanProjectAccessTokens, :migration do + def create_user(**extra_options) + defaults = { state: 'active', projects_limit: 0, email: "#{extra_options[:username]}@example.com" } + + table(:users).create!(defaults.merge(extra_options)) + end + + def create_membership(**extra_options) + defaults = { access_level: 30, notification_level: 0, source_id: 1, source_type: 'Project' } + + table(:members).create!(defaults.merge(extra_options)) + end + + let!(:regular_user) { create_user(username: 'regular') } + let!(:orphan_bot) { create_user(username: 'orphaned_bot', user_type: 6) } + let!(:used_bot) do + create_user(username: 'used_bot', user_type: 6).tap do |bot| + create_membership(user_id: bot.id) + end + end + + it 'marks all bots without memberships as deactivated' do + expect do + migrate! + regular_user.reload + orphan_bot.reload + used_bot.reload + end.to change { + [regular_user.state, orphan_bot.state, used_bot.state] + }.from(%w[active active active]).to(%w[active deactivated active]) + end + + it 'schedules for deletion all bots without memberships' do + job_class = 'DeleteUserWorker'.safe_constantize + + if job_class + expect(job_class).to receive(:bulk_perform_async).with([[orphan_bot.id, orphan_bot.id, skip_authorization: true]]) + + migrate! + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 334f9b4ae30..ca4c38d4663 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -5407,43 +5407,11 @@ RSpec.describe User do end describe '#password_expired_if_applicable?' do - let(:user) { build(:user, password_expires_at: password_expires_at, password_automatically_set: set_automatically?) } + let(:user) { build(:user, password_expires_at: password_expires_at) } subject { user.password_expired_if_applicable? } - context 'when user is not ldap user' do - context 'when user has password set automatically' do - let(:set_automatically?) { true } - - context 'when password_expires_at is not set' do - let(:password_expires_at) {} - - it 'returns false' do - is_expected.to be_falsey - end - end - - context 'when password_expires_at is in the past' do - let(:password_expires_at) { 1.minute.ago } - - it 'returns true' do - is_expected.to be_truthy - end - end - - context 'when password_expires_at is in the future' do - let(:password_expires_at) { 1.minute.from_now } - - it 'returns false' do - is_expected.to be_falsey - end - end - end - end - - context 'when user has password not set automatically' do - let(:set_automatically?) { false } - + shared_examples 'password expired not applicable' do context 'when password_expires_at is not set' do let(:password_expires_at) {} @@ -5469,13 +5437,7 @@ RSpec.describe User do end end - context 'when user is ldap user' do - let(:user) { build(:user, password_expires_at: password_expires_at) } - - before do - allow(user).to receive(:ldap_user?).and_return(true) - end - + context 'with a regular user' do context 'when password_expires_at is not set' do let(:password_expires_at) {} @@ -5487,8 +5449,8 @@ RSpec.describe User do context 'when password_expires_at is in the past' do let(:password_expires_at) { 1.minute.ago } - it 'returns false' do - is_expected.to be_falsey + it 'returns true' do + is_expected.to be_truthy end end @@ -5501,32 +5463,26 @@ RSpec.describe User do end end - context 'when user is a project bot' do - let(:user) { build(:user, :project_bot, password_expires_at: password_expires_at) } - - context 'when password_expires_at is not set' do - let(:password_expires_at) {} - - it 'returns false' do - is_expected.to be_falsey - end + context 'when user is a bot' do + before do + allow(user).to receive(:bot?).and_return(true) end - context 'when password_expires_at is in the past' do - let(:password_expires_at) { 1.minute.ago } + it_behaves_like 'password expired not applicable' + end - it 'returns false' do - is_expected.to be_falsey - end - end + context 'when password_automatically_set is true' do + let(:user) { create(:omniauth_user, provider: 'ldap')} - context 'when password_expires_at is in the future' do - let(:password_expires_at) { 1.minute.from_now } + it_behaves_like 'password expired not applicable' + end - it 'returns false' do - is_expected.to be_falsey - end + context 'when allow_password_authentication is false' do + before do + allow(user).to receive(:allow_password_authentication?).and_return(false) end + + it_behaves_like 'password expired not applicable' end end diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb index 122612df355..ca9a5b1853c 100644 --- a/spec/policies/global_policy_spec.rb +++ b/spec/policies/global_policy_spec.rb @@ -249,15 +249,13 @@ RSpec.describe GlobalPolicy do context 'user with expired password' do before do - current_user.update!(password_expires_at: 2.minutes.ago, password_automatically_set: true) + current_user.update!(password_expires_at: 2.minutes.ago) end it { is_expected.not_to be_allowed(:access_api) } context 'when user is using ldap' do - before do - allow(current_user).to receive(:ldap_user?).and_return(true) - end + let(:current_user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } it { is_expected.to be_allowed(:access_api) } end @@ -445,15 +443,13 @@ RSpec.describe GlobalPolicy do context 'user with expired password' do before do - current_user.update!(password_expires_at: 2.minutes.ago, password_automatically_set: true) + current_user.update!(password_expires_at: 2.minutes.ago) end it { is_expected.not_to be_allowed(:access_git) } context 'when user is using ldap' do - before do - allow(current_user).to receive(:ldap_user?).and_return(true) - end + let(:current_user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } it { is_expected.to be_allowed(:access_git) } end @@ -537,15 +533,13 @@ RSpec.describe GlobalPolicy do context 'user with expired password' do before do - current_user.update!(password_expires_at: 2.minutes.ago, password_automatically_set: true) + current_user.update!(password_expires_at: 2.minutes.ago) end it { is_expected.not_to be_allowed(:use_slash_commands) } context 'when user is using ldap' do - before do - allow(current_user).to receive(:ldap_user?).and_return(true) - end + let(:current_user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } it { is_expected.to be_allowed(:use_slash_commands) } end diff --git a/spec/requests/api/import_bitbucket_server_spec.rb b/spec/requests/api/import_bitbucket_server_spec.rb index 2225f737f36..970416c7444 100644 --- a/spec/requests/api/import_bitbucket_server_spec.rb +++ b/spec/requests/api/import_bitbucket_server_spec.rb @@ -28,6 +28,20 @@ RSpec.describe API::ImportBitbucketServer do Grape::Endpoint.before_each nil end + it 'rejects requests when Bitbucket Server Importer is disabled' do + stub_application_setting(import_sources: nil) + + post api("/import/bitbucket_server", user), params: { + bitbucket_server_url: base_uri, + bitbucket_server_username: user, + personal_access_token: token, + bitbucket_server_project: project_key, + bitbucket_server_repo: repo_slug + } + + expect(response).to have_gitlab_http_status(:forbidden) + end + it 'returns 201 response when the project is imported successfully' do allow(Gitlab::BitbucketServerImport::ProjectCreator) .to receive(:new).with(project_key, repo_slug, anything, repo_slug, user.namespace, user, anything) diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index a16f5abf608..d2528600477 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -61,7 +61,7 @@ RSpec.describe 'Git HTTP requests' do shared_examples 'operations are not allowed with expired password' do context "when password is expired" do it "responds to downloads with status 401 Unauthorized" do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) download(path, user: user.username, password: user.password) do |response| expect(response).to have_gitlab_http_status(:unauthorized) @@ -69,7 +69,7 @@ RSpec.describe 'Git HTTP requests' do end it "responds to uploads with status 401 Unauthorized" do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) upload(path, user: user.username, password: user.password) do |response| expect(response).to have_gitlab_http_status(:unauthorized) @@ -614,7 +614,7 @@ RSpec.describe 'Git HTTP requests' do context "when password is expired" do it "responds to downloads with status 401 unauthorized" do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) download(path, **env) do |response| expect(response).to have_gitlab_http_status(:unauthorized) @@ -697,7 +697,7 @@ RSpec.describe 'Git HTTP requests' do context "when password is expired" do it "responds to uploads with status 401 unauthorized" do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) write_access_token = create(:personal_access_token, user: user, scopes: [:write_repository]) @@ -950,7 +950,7 @@ RSpec.describe 'Git HTTP requests' do context 'when users password is expired' do it 'rejects pulls with 401 unauthorized' do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) download(path, user: 'gitlab-ci-token', password: build.token) do |response| expect(response).to have_gitlab_http_status(:unauthorized) @@ -1245,7 +1245,7 @@ RSpec.describe 'Git HTTP requests' do context "when password is expired" do it "responds to downloads with status 401 unauthorized" do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) download(path, **env) do |response| expect(response).to have_gitlab_http_status(:unauthorized) @@ -1328,7 +1328,7 @@ RSpec.describe 'Git HTTP requests' do context "when password is expired" do it "responds to uploads with status 401 unauthorized" do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) write_access_token = create(:personal_access_token, user: user, scopes: [:write_repository]) @@ -1555,7 +1555,7 @@ RSpec.describe 'Git HTTP requests' do context 'when users password is expired' do it 'rejects pulls with 401 unauthorized' do - user.update!(password_expires_at: 2.days.ago, password_automatically_set: true) + user.update!(password_expires_at: 2.days.ago) download(path, user: 'gitlab-ci-token', password: build.token) do |response| expect(response).to have_gitlab_http_status(:unauthorized) diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 02eb4262690..656ae744ac1 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -126,7 +126,7 @@ RSpec.describe 'Git LFS API and storage' do it_behaves_like 'LFS http 200 blob response' context 'when user password is expired' do - let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago, password_automatically_set: true)} + let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago)} it_behaves_like 'LFS http 401 response' end @@ -344,7 +344,7 @@ RSpec.describe 'Git LFS API and storage' do end context 'when user password is expired' do - let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago, password_automatically_set: true)} + let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago)} let(:role) { :reporter} @@ -958,7 +958,7 @@ RSpec.describe 'Git LFS API and storage' do it_behaves_like 'LFS http 200 workhorse response' context 'when user password is expired' do - let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago, password_automatically_set: true) } + let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago) } it_behaves_like 'LFS http 401 response' end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 4a76347ea45..27688d8c966 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -450,6 +450,16 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do end end + context 'when project has project bots' do + let!(:project_bot) { create(:user, :project_bot).tap { |user| project.add_maintainer(user) } } + + it 'deletes bot user as well' do + expect do + destroy_project(project, user) + end.to change { User.find_by(id: project_bot.id) }.to(nil) + end + end + context 'error while destroying', :sidekiq_inline do let!(:pipeline) { create(:ci_pipeline, project: project) } let!(:builds) { create_list(:ci_build, 2, :artifacts, pipeline: pipeline) } |