diff options
author | GitLab Release Tools Bot <delivery-team+release-tools@gitlab.com> | 2022-01-11 23:54:59 +0300 |
---|---|---|
committer | GitLab Release Tools Bot <delivery-team+release-tools@gitlab.com> | 2022-01-11 23:54:59 +0300 |
commit | 35e8389fe215c9d1dc27eda99fae0a3f0d8eb902 (patch) | |
tree | a22bf35a25cf42c235794dce6cfcc7901a092961 /spec | |
parent | 15995667681000021c37b52f087084db9f3c6048 (diff) | |
parent | a97774c8e1253b4291d085184c681834e3718eb9 (diff) |
Merge remote-tracking branch 'dev/14-4-stable' into 14-4-stable14-4-stable
Diffstat (limited to 'spec')
41 files changed, 833 insertions, 444 deletions
diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb index 9b13025cbe3..743759d5023 100644 --- a/spec/controllers/dashboard/projects_controller_spec.rb +++ b/spec/controllers/dashboard/projects_controller_spec.rb @@ -8,10 +8,6 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures do let_it_be(:user) { create(:user) } describe '#index' do - context 'user not logged in' do - it_behaves_like 'authenticates sessionless user', :index, :atom - end - context 'user logged in' do let_it_be(:project) { create(:project, name: 'Project 1') } let_it_be(:project2) { create(:project, name: 'Project Two') } diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index c838affa239..8fae617ea65 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -72,9 +72,6 @@ RSpec.describe DashboardController do end end - it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first - it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics - describe "#check_filters_presence!" do let(:user) { create(:user) } diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index a7625e65603..62171528695 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -1209,26 +1209,6 @@ RSpec.describe GroupsController, factory_default: :keep do end end - context 'token authentication' do - it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do - before do - default_params.merge!(id: group) - end - end - - it_behaves_like 'authenticates sessionless user', :issues, :atom, public: true do - before do - default_params.merge!(id: group, author_id: user.id) - end - end - - it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics, public: true do - before do - default_params.merge!(id: group) - end - end - end - describe 'external authorization' do before do group.add_owner(user) diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index d82fff1f7ae..fd380f9b763 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Import::GithubController do include ImportSpecHelper let(:provider) { :github } + let(:new_import_url) { public_send("new_import_#{provider}_url") } include_context 'a GitHub-ish import controller' @@ -50,13 +51,37 @@ RSpec.describe Import::GithubController do stub_omniauth_provider('github') end - it "updates access token" do - token = "asdasd12345" + context "when auth state param is missing from session" do + it "reports an error" do + get :callback - get :callback + expect(controller).to redirect_to(new_import_url) + expect(flash[:alert]).to eq('Access denied to your GitHub account.') + end + end + + context "when auth state param is present in session" do + let(:valid_auth_state) { "secret-state" } + + before do + session[:github_auth_state_key] = valid_auth_state + end - expect(session[:github_access_token]).to eq(token) - expect(controller).to redirect_to(status_import_github_url) + it "updates access token if state param is valid" do + token = "asdasd12345" + + get :callback, params: { state: valid_auth_state } + + expect(session[:github_access_token]).to eq(token) + expect(controller).to redirect_to(status_import_github_url) + end + + it "reports an error if state param is invalid" do + get :callback, params: { state: "different-state" } + + expect(controller).to redirect_to(new_import_url) + expect(flash[:alert]).to eq('Access denied to your GitHub account.') + end end end @@ -71,8 +96,6 @@ RSpec.describe Import::GithubController do end context 'when OAuth config is missing' do - let(:new_import_url) { public_send("new_import_#{provider}_url") } - before do allow(controller).to receive(:oauth_config).and_return(nil) end @@ -108,6 +131,16 @@ RSpec.describe Import::GithubController do get :status end + + it 'gets authorization url using legacy client' do + allow(controller).to receive(:logged_in_with_provider?).and_return(true) + expect(controller).to receive(:go_to_provider_for_permissions).and_call_original + expect_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client| + expect(client).to receive(:authorize_url).and_call_original + end + + get :new + end end context 'when feature remove_legacy_github_client is enabled' do @@ -130,6 +163,16 @@ RSpec.describe Import::GithubController do get :status end + it 'gets authorization url using oauth client' do + allow(controller).to receive(:logged_in_with_provider?).and_return(true) + expect(controller).to receive(:go_to_provider_for_permissions).and_call_original + expect_next_instance_of(OAuth2::Client) do |client| + expect(client.auth_code).to receive(:authorize_url).and_call_original + end + + get :new + end + context 'pagination' do context 'when no page is specified' do it 'requests first page' do diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index 4cf77fde3a1..82393aacac2 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -139,27 +139,4 @@ RSpec.describe Projects::CommitsController do end end end - - context 'token authentication' do - context 'public project' do - it_behaves_like 'authenticates sessionless user', :show, :atom, { public: true, ignore_incrementing: true } do - before do - public_project = create(:project, :repository, :public) - - default_params.merge!(namespace_id: public_project.namespace, project_id: public_project, id: "master.atom") - end - end - end - - context 'private project' do - it_behaves_like 'authenticates sessionless user', :show, :atom, { public: false, ignore_incrementing: true } do - before do - private_project = create(:project, :repository, :private) - private_project.add_maintainer(user) - - default_params.merge!(namespace_id: private_project.namespace, project_id: private_project, id: "master.atom") - end - end - end - end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 0b3bd4d78ac..35f08f47f5d 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1963,40 +1963,4 @@ RSpec.describe Projects::IssuesController do end end end - - context 'private project with token authentication' do - let_it_be(:private_project) { create(:project, :private) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do - before do - default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - - it_behaves_like 'authenticates sessionless user', :calendar, :ics, ignore_incrementing: true do - before do - default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - end - - context 'public project with token authentication' do - let_it_be(:public_project) { create(:project, :public) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do - before do - default_params.merge!(project_id: public_project, namespace_id: public_project.namespace) - end - end - - it_behaves_like 'authenticates sessionless user', :calendar, :ics, public: true do - before do - default_params.merge!(project_id: public_project, namespace_id: public_project.namespace) - end - end - end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index a81173ccaac..d16919d2a50 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -193,6 +193,8 @@ RSpec.describe Projects::RawController do let_it_be(:user) { create(:user, static_object_token: 'very-secure-token') } let_it_be(:file_path) { 'master/README.md' } + let(:token) { user.static_object_token } + before do project.add_developer(user) end @@ -207,17 +209,46 @@ RSpec.describe Projects::RawController do end context 'when a token param is present' do + subject(:execute_raw_request_with_token_in_params) do + execute_raw_requests(requests: 1, project: project, file_path: file_path, token: token) + end + context 'when token is correct' do it 'calls the action normally' do - execute_raw_requests(requests: 1, project: project, file_path: file_path, token: user.static_object_token) + execute_raw_request_with_token_in_params expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + execute_raw_request_with_token_in_params + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + execute_raw_request_with_token_in_params + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do + let(:token) { 'foobar' } + it 'redirects to sign in page' do - execute_raw_requests(requests: 1, project: project, file_path: file_path, token: 'foobar') + execute_raw_request_with_token_in_params expect(response).to have_gitlab_http_status(:found) expect(response.location).to end_with('/users/sign_in') @@ -226,19 +257,47 @@ RSpec.describe Projects::RawController do end context 'when a token header is present' do + subject(:execute_raw_request_with_token_in_headers) do + request.headers['X-Gitlab-Static-Object-Token'] = token + execute_raw_requests(requests: 1, project: project, file_path: file_path) + end + context 'when token is correct' do it 'calls the action normally' do - request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token - execute_raw_requests(requests: 1, project: project, file_path: file_path) + execute_raw_request_with_token_in_headers expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + execute_raw_request_with_token_in_headers + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + execute_raw_request_with_token_in_headers + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do + let(:token) { 'foobar' } + it 'redirects to sign in page' do - request.headers['X-Gitlab-Static-Object-Token'] = 'foobar' - execute_raw_requests(requests: 1, project: project, file_path: file_path) + execute_raw_request_with_token_in_headers expect(response).to have_gitlab_http_status(:found) expect(response.location).to end_with('/users/sign_in') diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index cb2579b800a..a1755f3ab4e 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -178,6 +178,29 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master', token: user.static_object_token }, format: 'zip' + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master', token: user.static_object_token }, format: 'zip' + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do @@ -197,6 +220,31 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: 'zip' + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: 'zip' + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index d0719643b7f..0c772dc7c77 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -117,28 +117,6 @@ RSpec.describe Projects::TagsController do end end - context 'private project with token authentication' do - let(:private_project) { create(:project, :repository, :private) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do - before do - default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - end - - context 'public project with token authentication' do - let(:public_project) { create(:project, :repository, :public) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do - before do - default_params.merge!(project_id: public_project, namespace_id: public_project.namespace) - end - end - end - describe 'POST #create' do before do project.add_developer(user) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index b34cfedb767..5a9d5ff1458 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1554,28 +1554,6 @@ RSpec.describe ProjectsController do end end - context 'private project with token authentication' do - let_it_be(:private_project) { create(:project, :private) } - - it_behaves_like 'authenticates sessionless user', :show, :atom, ignore_incrementing: true do - before do - default_params.merge!(id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - end - - context 'public project with token authentication' do - let_it_be(:public_project) { create(:project, :public) } - - it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do - before do - default_params.merge!(id: public_project, namespace_id: public_project.namespace) - end - end - end - context 'GET show.atom' do let_it_be(:public_project) { create(:project, :public) } let_it_be(:event) { create(:event, :commented, project: public_project, target: create(:note, project: public_project)) } diff --git a/spec/frontend/behaviors/gl_emoji_spec.js b/spec/frontend/behaviors/gl_emoji_spec.js index 286ed269421..5cf5bfea453 100644 --- a/spec/frontend/behaviors/gl_emoji_spec.js +++ b/spec/frontend/behaviors/gl_emoji_spec.js @@ -102,6 +102,18 @@ describe('gl_emoji', () => { }); }); + it('escapes gl-emoji name', async () => { + const glEmojiElement = markupToDomElement( + "<gl-emoji data-name='"x="y" onload="alert(document.location.href)"' data-unicode-version='x'>abc</gl-emoji>", + ); + + await waitForPromises(); + + expect(glEmojiElement.outerHTML).toBe( + '<gl-emoji data-name=""x="y" onload="alert(document.location.href)"" data-unicode-version="x"><img class="emoji" title=":"x="y" onload="alert(document.location.href)":" alt=":"x="y" onload="alert(document.location.href)":" src="/-/emojis/1/grey_question.png" width="20" height="20" align="absmiddle"></gl-emoji>', + ); + }); + it('Adds sprite CSS if emojis are not supported', async () => { const testPath = '/test-path.css'; jest.spyOn(EmojiUnicodeSupport, 'default').mockReturnValue(false); diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb index 9897e697009..0691cf307b7 100644 --- a/spec/graphql/resolvers/issues_resolver_spec.rb +++ b/spec/graphql/resolvers/issues_resolver_spec.rb @@ -518,13 +518,13 @@ RSpec.describe Resolvers::IssuesResolver do end it 'finds a specific issue with iid', :request_store do - result = batch_sync(max_queries: 5) { resolve_issues(iid: issue1.iid).to_a } + result = batch_sync(max_queries: 6) { resolve_issues(iid: issue1.iid).to_a } expect(result).to contain_exactly(issue1) end it 'batches queries that only include IIDs', :request_store do - result = batch_sync(max_queries: 5) do + result = batch_sync(max_queries: 6) do [issue1, issue2] .map { |issue| resolve_issues(iid: issue.iid.to_s) } .flat_map(&:to_a) @@ -534,7 +534,7 @@ RSpec.describe Resolvers::IssuesResolver do end it 'finds a specific issue with iids', :request_store do - result = batch_sync(max_queries: 5) do + result = batch_sync(max_queries: 6) do resolve_issues(iids: [issue1.iid]).to_a end diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index e076815c4f6..0713475d59b 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -531,24 +531,6 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do end end end - - def stub_domain_resolv(domain, ip, port = 80, &block) - address = instance_double(Addrinfo, - ip_address: ip, - ipv4_private?: true, - ipv6_linklocal?: false, - ipv4_loopback?: false, - ipv6_loopback?: false, - ipv4?: false, - ip_port: port - ) - allow(Addrinfo).to receive(:getaddrinfo).with(domain, port, any_args).and_return([address]) - allow(address).to receive(:ipv6_v4mapped?).and_return(false) - - yield - - allow(Addrinfo).to receive(:getaddrinfo).and_call_original - end end context 'when enforce_user is' do @@ -611,6 +593,44 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do expect(described_class).to be_blocked_url('http://foobar.x') end + + context 'when gitlab is running on a non-default port' do + let(:gitlab_port) { 3000 } + + before do + stub_config(gitlab: { protocol: 'http', host: 'gitlab.local', port: gitlab_port }) + end + + it 'returns true for url targeting the wrong port' do + stub_domain_resolv('gitlab.local', '127.0.0.1') do + expect(described_class).to be_blocked_url("http://gitlab.local/foo") + end + end + + it 'does not block url on gitlab port' do + stub_domain_resolv('gitlab.local', '127.0.0.1') do + expect(described_class).not_to be_blocked_url("http://gitlab.local:#{gitlab_port}/foo") + end + end + end + + def stub_domain_resolv(domain, ip, port = 80, &block) + address = instance_double(Addrinfo, + ip_address: ip, + ipv4_private?: true, + ipv6_linklocal?: false, + ipv4_loopback?: false, + ipv6_loopback?: false, + ipv4?: false, + ip_port: port + ) + allow(Addrinfo).to receive(:getaddrinfo).with(domain, port, any_args).and_return([address]) + allow(address).to receive(:ipv6_v4mapped?).and_return(false) + + yield + + allow(Addrinfo).to receive(:getaddrinfo).and_call_original + end end describe '#validate_hostname' do diff --git a/spec/models/integrations/chat_message/alert_message_spec.rb b/spec/models/integrations/chat_message/alert_message_spec.rb index 9866b2d9185..162df1a774c 100644 --- a/spec/models/integrations/chat_message/alert_message_spec.rb +++ b/spec/models/integrations/chat_message/alert_message_spec.rb @@ -16,6 +16,8 @@ RSpec.describe Integrations::ChatMessage::AlertMessage do }.merge(Gitlab::DataBuilder::Alert.build(alert)) end + it_behaves_like Integrations::ChatMessage + describe '#message' do it 'returns the correct message' do expect(subject.message).to eq("Alert firing in #{args[:project_name]}") diff --git a/spec/models/integrations/chat_message/base_message_spec.rb b/spec/models/integrations/chat_message/base_message_spec.rb index eada5d1031d..0f0ab11f2ac 100644 --- a/spec/models/integrations/chat_message/base_message_spec.rb +++ b/spec/models/integrations/chat_message/base_message_spec.rb @@ -31,4 +31,22 @@ RSpec.describe Integrations::ChatMessage::BaseMessage do it { is_expected.to eq('Check this out https://gitlab-domain.com/uploads/Screenshot1.png. And this https://gitlab-domain.com/uploads/Screenshot2.png') } end end + + describe '#strip_markup' do + using RSpec::Parameterized::TableSyntax + + where(:input, :output) do + nil | nil + '' | '' + '[label](url)' | 'label(url)' + '<url|label>' | 'urllabel' + '<a href="url">label</a>' | 'a href="url"label/a' + end + + with_them do + it 'returns the expected output' do + expect(base_message.send(:strip_markup, input)).to eq(output) + end + end + end end diff --git a/spec/models/integrations/chat_message/deployment_message_spec.rb b/spec/models/integrations/chat_message/deployment_message_spec.rb index ff255af11a3..6bcd29c0a00 100644 --- a/spec/models/integrations/chat_message/deployment_message_spec.rb +++ b/spec/models/integrations/chat_message/deployment_message_spec.rb @@ -3,83 +3,79 @@ require 'spec_helper' RSpec.describe Integrations::ChatMessage::DeploymentMessage do - describe '#pretext' do - it 'returns a message with the data returned by the deployment data builder' do - environment = create(:environment, name: "myenvironment") - project = create(:project, :repository) - commit = project.commit('HEAD') - deployment = create(:deployment, status: :success, environment: environment, project: project, sha: commit.sha) - data = Gitlab::DataBuilder::Deployment.build(deployment, Time.current) + subject { described_class.new(args) } + + let_it_be(:user) { create(:user, name: 'John Smith', username: 'smith') } + let_it_be(:namespace) { create(:namespace, name: 'myspace') } + let_it_be(:project) { create(:project, :repository, namespace: namespace, name: 'myproject') } + let_it_be(:commit) { project.commit('HEAD') } + let_it_be(:ci_build) { create(:ci_build, project: project) } + let_it_be(:environment) { create(:environment, name: 'myenvironment', project: project) } + let_it_be(:deployment) { create(:deployment, status: :success, deployable: ci_build, environment: environment, project: project, user: user, sha: commit.sha) } + + let(:args) do + Gitlab::DataBuilder::Deployment.build(deployment, Time.current) + end - message = described_class.new(data) + it_behaves_like Integrations::ChatMessage - expect(message.pretext).to eq("Deploy to myenvironment succeeded") + describe '#pretext' do + it 'returns a message with the data returned by the deployment data builder' do + expect(subject.pretext).to eq("Deploy to myenvironment succeeded") end it 'returns a message for a successful deployment' do - data = { + args.merge!( status: 'success', environment: 'production' - } + ) - message = described_class.new(data) - - expect(message.pretext).to eq('Deploy to production succeeded') + expect(subject.pretext).to eq('Deploy to production succeeded') end it 'returns a message for a failed deployment' do - data = { + args.merge!( status: 'failed', environment: 'production' - } + ) - message = described_class.new(data) - - expect(message.pretext).to eq('Deploy to production failed') + expect(subject.pretext).to eq('Deploy to production failed') end it 'returns a message for a canceled deployment' do - data = { + args.merge!( status: 'canceled', environment: 'production' - } - - message = described_class.new(data) + ) - expect(message.pretext).to eq('Deploy to production canceled') + expect(subject.pretext).to eq('Deploy to production canceled') end it 'returns a message for a deployment to another environment' do - data = { + args.merge!( status: 'success', environment: 'staging' - } - - message = described_class.new(data) + ) - expect(message.pretext).to eq('Deploy to staging succeeded') + expect(subject.pretext).to eq('Deploy to staging succeeded') end it 'returns a message for a deployment with any other status' do - data = { + args.merge!( status: 'unknown', environment: 'staging' - } + ) - message = described_class.new(data) - - expect(message.pretext).to eq('Deploy to staging unknown') + expect(subject.pretext).to eq('Deploy to staging unknown') end it 'returns a message for a running deployment' do - data = { - status: 'running', - environment: 'production' - } - - message = described_class.new(data) + args.merge!( + status: 'running', + environment: 'production' + ) - expect(message.pretext).to eq('Starting deploy to production') + expect(subject.pretext).to eq('Starting deploy to production') end end @@ -108,21 +104,11 @@ RSpec.describe Integrations::ChatMessage::DeploymentMessage do end it 'returns attachments with the data returned by the deployment data builder' do - user = create(:user, name: "John Smith", username: "smith") - namespace = create(:namespace, name: "myspace") - project = create(:project, :repository, namespace: namespace, name: "myproject") - commit = project.commit('HEAD') - environment = create(:environment, name: "myenvironment", project: project) - ci_build = create(:ci_build, project: project) - deployment = create(:deployment, :success, deployable: ci_build, environment: environment, project: project, user: user, sha: commit.sha) job_url = Gitlab::Routing.url_helpers.project_job_url(project, ci_build) commit_url = Gitlab::UrlBuilder.build(deployment.commit) user_url = Gitlab::Routing.url_helpers.user_url(user) - data = Gitlab::DataBuilder::Deployment.build(deployment, Time.current) - message = described_class.new(data) - - expect(message.attachments).to eq([{ + expect(subject.attachments).to eq([{ text: "[myspace/myproject](#{project.web_url}) with job [##{ci_build.id}](#{job_url}) by [John Smith (smith)](#{user_url})\n[#{deployment.short_sha}](#{commit_url}): #{commit.title}", color: "good" }]) diff --git a/spec/models/integrations/chat_message/issue_message_spec.rb b/spec/models/integrations/chat_message/issue_message_spec.rb index 31b80ad3169..7026a314b78 100644 --- a/spec/models/integrations/chat_message/issue_message_spec.rb +++ b/spec/models/integrations/chat_message/issue_message_spec.rb @@ -28,6 +28,8 @@ RSpec.describe Integrations::ChatMessage::IssueMessage do } end + it_behaves_like Integrations::ChatMessage + context 'without markdown' do let(:color) { '#C95823' } diff --git a/spec/models/integrations/chat_message/merge_message_spec.rb b/spec/models/integrations/chat_message/merge_message_spec.rb index ed1ad6837e2..52f15667b03 100644 --- a/spec/models/integrations/chat_message/merge_message_spec.rb +++ b/spec/models/integrations/chat_message/merge_message_spec.rb @@ -29,6 +29,8 @@ RSpec.describe Integrations::ChatMessage::MergeMessage do } end + it_behaves_like Integrations::ChatMessage + context 'without markdown' do let(:color) { '#345' } diff --git a/spec/models/integrations/chat_message/note_message_spec.rb b/spec/models/integrations/chat_message/note_message_spec.rb index 668c0da26ae..197df216814 100644 --- a/spec/models/integrations/chat_message/note_message_spec.rb +++ b/spec/models/integrations/chat_message/note_message_spec.rb @@ -19,6 +19,10 @@ RSpec.describe Integrations::ChatMessage::NoteMessage do name: 'project_name', url: 'http://somewhere.com' }, + commit: { + id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23', + message: "Added a commit message\ndetails\n123\n" + }, object_attributes: { id: 10, note: 'comment on a commit', @@ -28,16 +32,9 @@ RSpec.describe Integrations::ChatMessage::NoteMessage do } end - context 'commit notes' do - before do - args[:object_attributes][:note] = 'comment on a commit' - args[:object_attributes][:noteable_type] = 'Commit' - args[:commit] = { - id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23', - message: "Added a commit message\ndetails\n123\n" - } - end + it_behaves_like Integrations::ChatMessage + context 'commit notes' do context 'without markdown' do it 'returns a message regarding notes on commits' do expect(subject.pretext).to eq("Test User (test.user) <http://url.com|commented on " \ diff --git a/spec/models/integrations/chat_message/pipeline_message_spec.rb b/spec/models/integrations/chat_message/pipeline_message_spec.rb index a80d13d7f5d..68ef0ccb2e4 100644 --- a/spec/models/integrations/chat_message/pipeline_message_spec.rb +++ b/spec/models/integrations/chat_message/pipeline_message_spec.rb @@ -40,6 +40,8 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do let(:has_yaml_errors) { false } + it_behaves_like Integrations::ChatMessage + before do test_commit = double("A test commit", committer: args[:user], title: "A test commit message") test_project = double("A test project", commit_by: test_commit, name: args[:project][:name], web_url: args[:project][:web_url]) diff --git a/spec/models/integrations/chat_message/push_message_spec.rb b/spec/models/integrations/chat_message/push_message_spec.rb index 167487449c3..8d2d0f9f9a8 100644 --- a/spec/models/integrations/chat_message/push_message_spec.rb +++ b/spec/models/integrations/chat_message/push_message_spec.rb @@ -19,6 +19,8 @@ RSpec.describe Integrations::ChatMessage::PushMessage do let(:color) { '#345' } + it_behaves_like Integrations::ChatMessage + context 'push' do before do args[:commits] = [ diff --git a/spec/models/integrations/chat_message/wiki_page_message_spec.rb b/spec/models/integrations/chat_message/wiki_page_message_spec.rb index ded467dc910..4aa96c7e031 100644 --- a/spec/models/integrations/chat_message/wiki_page_message_spec.rb +++ b/spec/models/integrations/chat_message/wiki_page_message_spec.rb @@ -33,6 +33,8 @@ RSpec.describe Integrations::ChatMessage::WikiPageMessage do } end + it_behaves_like Integrations::ChatMessage + context 'without markdown' do describe '#pretext' do context 'when :action == "create"' do diff --git a/spec/requests/api/dependency_proxy_spec.rb b/spec/requests/api/dependency_proxy_spec.rb index 2837d1c02c4..067852ef1e9 100644 --- a/spec/requests/api/dependency_proxy_spec.rb +++ b/spec/requests/api/dependency_proxy_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe API::DependencyProxy, api: true do - include ExclusiveLeaseHelpers - let_it_be(:user) { create(:user) } let_it_be(:blob) { create(:dependency_proxy_blob )} let_it_be(:group, reload: true) { blob.group } @@ -20,11 +18,8 @@ RSpec.describe API::DependencyProxy, api: true do shared_examples 'responding to purge requests' do context 'with feature available and enabled' do - let_it_be(:lease_key) { "dependency_proxy:delete_group_blobs:#{group.id}" } - context 'an admin user' do it 'deletes the blobs and returns no content' do - stub_exclusive_lease(lease_key, timeout: 1.hour) expect(PurgeDependencyProxyCacheWorker).to receive(:perform_async) subject @@ -32,23 +27,6 @@ RSpec.describe API::DependencyProxy, api: true do expect(response).to have_gitlab_http_status(:accepted) expect(response.body).to eq('202') end - - context 'called multiple times in one hour', :clean_gitlab_redis_shared_state do - it 'returns 409 with an error message' do - stub_exclusive_lease_taken(lease_key, timeout: 1.hour) - - subject - - expect(response).to have_gitlab_http_status(:conflict) - expect(response.body).to include('This request has already been made.') - end - - it 'executes service only for the first time' do - expect(PurgeDependencyProxyCacheWorker).to receive(:perform_async).once - - 2.times { subject } - end - end end context 'a non-admin' do diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb index 7d182a3414b..0d2dff8b9ba 100644 --- a/spec/requests/api/graphql_spec.rb +++ b/spec/requests/api/graphql_spec.rb @@ -241,7 +241,7 @@ RSpec.describe 'GraphQL' do end context 'with token authentication' do - let(:token) { create(:personal_access_token) } + let(:token) { create(:personal_access_token, user: user) } it 'authenticates users with a PAT' do stub_authentication_activity_metrics(debug: false) @@ -264,6 +264,32 @@ RSpec.describe 'GraphQL' do expect(graphql_errors).to include({ 'message' => /API not accessible/ }) end + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'does not authenticate user' do + post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token }) + + expect(response).to have_gitlab_http_status(:ok) + + expect(graphql_data['echo']).to eq('nil says: Hello world') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'authenticates user' do + post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token }) + + expect(response).to have_gitlab_http_status(:ok) + + expect(graphql_data['echo']).to eq("\"#{token.user.username}\" says: Hello world") + end + end + end + context 'when the personal access token has no api scope' do it 'does not log the user in' do token.update!(scopes: [:read_user]) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 1c79093a831..e1a2866f967 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -3086,6 +3086,29 @@ RSpec.describe API::Projects do expect(json_response['message']).to eq('404 Project Not Found') end + it 'returns 404 if the source project members cannot be viewed by the requester' do + private_project = create(:project, :private) + + expect do + post api("/projects/#{project.id}/import_project_members/#{private_project.id}", user) + end.not_to change { project.members.count } + + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response['message']).to eq('404 Project Not Found') + end + + it 'returns 403 if the source project members cannot be administered by the requester' do + project.add_maintainer(user2) + project2.add_developer(user2) + + expect do + post api("/projects/#{project.id}/import_project_members/#{project2.id}", user2) + end.not_to change { project.members.count } + + expect(response).to have_gitlab_http_status(:forbidden) + expect(json_response['message']).to eq('403 Forbidden - Project') + end + it 'returns 422 if the import failed for valid projects' do allow_next_instance_of(::ProjectTeam) do |project_team| allow(project_team).to receive(:import).and_return(false) diff --git a/spec/requests/dashboard/projects_controller_spec.rb b/spec/requests/dashboard/projects_controller_spec.rb new file mode 100644 index 00000000000..4cd3b6c4f9e --- /dev/null +++ b/spec/requests/dashboard/projects_controller_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Dashboard::ProjectsController do + context 'token authentication' do + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false do + let(:url) { dashboard_projects_url(:atom) } + end + end +end diff --git a/spec/requests/dashboard_controller_spec.rb b/spec/requests/dashboard_controller_spec.rb new file mode 100644 index 00000000000..62655d720c5 --- /dev/null +++ b/spec/requests/dashboard_controller_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DashboardController do + context 'token authentication' do + it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false do + let(:url) { issues_dashboard_url(:atom, assignee_username: user.username) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: false do + let(:url) { issues_dashboard_url(:ics, assignee_username: user.username) } + end + end +end diff --git a/spec/requests/groups_controller_spec.rb b/spec/requests/groups_controller_spec.rb new file mode 100644 index 00000000000..422c108f2ad --- /dev/null +++ b/spec/requests/groups_controller_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GroupsController do + context 'token authentication' do + context 'when public group' do + let_it_be(:public_group) { create(:group, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { group_path(public_group, format: :atom) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: true do + let(:url) { issues_group_path(public_group, format: :atom) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: true do + let(:url) { issues_group_calendar_url(public_group, format: :ics) } + end + end + + context 'when private project' do + let_it_be(:private_group) { create(:group, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do + let(:url) { group_path(private_group, format: :atom) } + + before do + private_group.add_maintainer(user) + end + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false, ignore_metrics: true do + let(:url) { issues_group_path(private_group, format: :atom) } + + before do + private_group.add_maintainer(user) + end + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: false, ignore_metrics: true do + let(:url) { issues_group_calendar_url(private_group, format: :ics) } + + before do + private_group.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/commits_controller_spec.rb b/spec/requests/projects/commits_controller_spec.rb new file mode 100644 index 00000000000..158902c0ffd --- /dev/null +++ b/spec/requests/projects/commits_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::CommitsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :repository, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { project_commits_url(public_project, public_project.default_branch, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :repository, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do + let(:url) { project_commits_url(private_project, private_project.default_branch, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb new file mode 100644 index 00000000000..4e61cd338d6 --- /dev/null +++ b/spec/requests/projects/issues_controller_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::IssuesController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do + let(:url) { project_issues_url(public_project, format: :atom) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'calendar ics', public_resource: true do + let(:url) { project_issues_url(public_project, format: :ics) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do + let(:url) { project_issues_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'calendar ics', public_resource: false, ignore_metrics: true do + let(:url) { project_issues_url(private_project, format: :ics) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/merge_requests_controller_spec.rb b/spec/requests/projects/merge_requests_controller_spec.rb new file mode 100644 index 00000000000..3b1ce569033 --- /dev/null +++ b/spec/requests/projects/merge_requests_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::MergeRequestsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do + let(:url) { project_merge_requests_url(public_project, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do + let(:url) { project_merge_requests_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/tags_controller_spec.rb b/spec/requests/projects/tags_controller_spec.rb new file mode 100644 index 00000000000..b9531a2739c --- /dev/null +++ b/spec/requests/projects/tags_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::TagsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :repository, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do + let(:url) { project_tags_url(public_project, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :repository, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do + let(:url) { project_tags_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects_controller_spec.rb b/spec/requests/projects_controller_spec.rb new file mode 100644 index 00000000000..d2200d5a4ec --- /dev/null +++ b/spec/requests/projects_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ProjectsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { project_url(public_project, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do + let(:url) { project_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index accacd705e7..d513c63dbd7 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -792,9 +792,9 @@ RSpec.describe UsersController do end context 'token authentication' do - let(:url) { user_url(user.username, format: :atom) } - - it_behaves_like 'authenticates sessionless user for the request spec', public: true + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { user_url(user, format: :atom) } + end end def user_moved_message(redirect_route, user) diff --git a/spec/services/packages/npm/create_package_service_spec.rb b/spec/services/packages/npm/create_package_service_spec.rb index ba5729eaf59..e06e0d8f71a 100644 --- a/spec/services/packages/npm/create_package_service_spec.rb +++ b/spec/services/packages/npm/create_package_service_spec.rb @@ -11,10 +11,8 @@ RSpec.describe Packages::Npm::CreatePackageService do Gitlab::Json.parse(fixture_file('packages/npm/payload.json') .gsub('@root/npm-test', package_name) .gsub('1.0.1', version)).with_indifferent_access - .merge!(override) end - let(:override) { {} } let(:package_name) { "@#{namespace.path}/my-app" } subject { described_class.new(project, user, params).execute } @@ -81,13 +79,53 @@ RSpec.describe Packages::Npm::CreatePackageService do it { expect(subject[:message]).to be 'Package already exists.' } end - context 'file size above maximum limit' do + describe 'max file size validation' do + let(:max_file_size) { 5.bytes} + + shared_examples_for 'max file size validation failure' do + it 'returns a 400 error', :aggregate_failures do + expect(subject[:http_status]).to eq 400 + expect(subject[:message]).to be 'File is too large.' + end + end + before do - params['_attachments']["#{package_name}-#{version}.tgz"]['length'] = project.actual_limits.npm_max_file_size + 1 + project.actual_limits.update!(npm_max_file_size: max_file_size) end - it { expect(subject[:http_status]).to eq 400 } - it { expect(subject[:message]).to be 'File is too large.' } + context 'when max file size is exceeded' do + # NOTE: The base64 encoded package data in the fixture file is the "hello\n" string, whose byte size is 6. + it_behaves_like 'max file size validation failure' + end + + context 'when file size is faked by setting the attachment length param to a lower size' do + let(:params) { super().deep_merge!( { _attachments: { "#{package_name}-#{version}.tgz" => { data: encoded_package_data, length: 1 } } }) } + + # TODO (technical debt): Extract the package size calculation outside the service and add separate specs for it. + # Right now we have several contexts here to test the calculation's different scenarios. + context "when encoded package data is not padded" do + # 'Hello!' (size = 6 bytes) => 'SGVsbG8h' + let(:encoded_package_data) { 'SGVsbG8h' } + + it_behaves_like 'max file size validation failure' + end + + context "when encoded package data is padded with '='" do + let(:max_file_size) { 4.bytes} + # 'Hello' (size = 5 bytes) => 'SGVsbG8=' + let(:encoded_package_data) { 'SGVsbG8=' } + + it_behaves_like 'max file size validation failure' + end + + context "when encoded package data is padded with '=='" do + let(:max_file_size) { 3.bytes} + # 'Hell' (size = 4 bytes) => 'SGVsbA==' + let(:encoded_package_data) { 'SGVsbA==' } + + it_behaves_like 'max file size validation failure' + end + end end [ @@ -106,7 +144,7 @@ RSpec.describe Packages::Npm::CreatePackageService do end context 'with empty versions' do - let(:override) { { versions: {} } } + let(:params) { super().merge!({ versions: {} } ) } it { expect(subject[:http_status]).to eq 400 } it { expect(subject[:message]).to eq 'Version is empty.' } diff --git a/spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb deleted file mode 100644 index 041695d8111..00000000000 --- a/spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -# This controller shared examples will be migrated to -# spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb -# See also https://gitlab.com/groups/gitlab-org/-/epics/5076 - -RSpec.shared_examples 'authenticates sessionless user' do |path, format, params| - params ||= {} - - before do - stub_authentication_activity_metrics(debug: false) - end - - let(:user) { create(:user) } - let(:personal_access_token) { create(:personal_access_token, user: user) } - let(:default_params) { { format: format }.merge(params.except(:public) || {}) } - - context "when the 'personal_access_token' param is populated with the personal access token" do - it 'logs the user in' do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) - - get path, params: default_params.merge(private_token: personal_access_token.token) - - expect(response).to have_gitlab_http_status(:ok) - expect(controller.current_user).to eq(user) - end - - it 'does not log the user in if page is public', if: params[:public] do - get path, params: default_params - - expect(response).to have_gitlab_http_status(:ok) - expect(controller.current_user).to be_nil - end - end - - context 'when the personal access token has no api scope', unless: params[:public] do - it 'does not log the user in' do - # Several instances of where these specs are shared route the request - # through ApplicationController#route_not_found which does not involve - # the usual auth code from Devise, so does not increment the - # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) - end - - personal_access_token.update!(scopes: [:read_user]) - - get path, params: default_params.merge(private_token: personal_access_token.token) - - expect(response).not_to have_gitlab_http_status(:ok) - end - end - - context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do - it 'logs the user in' do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) - - @request.headers['PRIVATE-TOKEN'] = personal_access_token.token - get path, params: default_params - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context "when the 'feed_token' param is populated with the feed token", if: format == :rss do - it "logs the user in" do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) - - get path, params: default_params.merge(feed_token: user.feed_token) - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context "when the 'feed_token' param is populated with an invalid feed token", if: format == :rss, unless: params[:public] do - it "logs the user" do - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) - - get path, params: default_params.merge(feed_token: 'token') - - expect(response).not_to have_gitlab_http_status(:ok) - end - end - - it "doesn't log the user in otherwise", unless: params[:public] do - # Several instances of where these specs are shared route the request - # through ApplicationController#route_not_found which does not involve - # the usual auth code from Devise, so does not increment the - # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) - end - - get path, params: default_params.merge(private_token: 'token') - - expect(response).not_to have_gitlab_http_status(:ok) - end -end diff --git a/spec/support/shared_examples/models/integrations/chat_message_shared_examples.rb b/spec/support/shared_examples/models/integrations/chat_message_shared_examples.rb new file mode 100644 index 00000000000..2665f249ded --- /dev/null +++ b/spec/support/shared_examples/models/integrations/chat_message_shared_examples.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +RSpec.shared_examples Integrations::ChatMessage do + context 'when input contains link markup' do + let(:evil_input) { '[Markdown](http://evil.com) <a href="http://evil.com">HTML</a> <http://evil.com|Slack>' } + + # Attributes returned from #activity and #attributes which should be sanitized. + let(:sanitized_attributes) do + %i[title subtitle text fallback author_name] + end + + # Attributes passed to #initialize which can contain user input. + before do + args.deep_merge!( + project_name: evil_input, + user_name: evil_input, + user_full_name: evil_input, + commit_title: evil_input, + environment: evil_input, + project: { + name: evil_input + }, + user: { + name: evil_input, + username: evil_input + }, + object_attributes: { + title: evil_input + } + ) + end + + # NOTE: The `include` matcher is used here so the RSpec error messages will tell us + # which method or attribute is failing, even though it makes the spec a bit less readable. + it 'strips all link markup characters', :aggregate_failures do + expect(subject).not_to have_attributes( + pretext: include(evil_input), + summary: include(evil_input) + ) + + begin + sanitized_attributes.each do |attribute| + expect(subject.activity).not_to include(attribute => include(evil_input)) + end + rescue NotImplementedError + end + + begin + sanitized_attributes.each do |attribute| + expect(subject.attachments).not_to include(include(attribute => include(evil_input))) + end + rescue NotImplementedError + end + end + end +end diff --git a/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb b/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb index d82da1b01e1..56e90a6ec34 100644 --- a/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb +++ b/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb @@ -1,84 +1,160 @@ # frozen_string_literal: true -RSpec.shared_examples 'authenticates sessionless user for the request spec' do |params| - params ||= {} - +RSpec.shared_examples 'authenticates sessionless user for the request spec' do |name, public_resource:, ignore_metrics: false, params: {}| before do stub_authentication_activity_metrics(debug: false) end - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } let(:personal_access_token) { create(:personal_access_token, user: user) } - let(:default_params) { params.except(:public) || {} } - context "when the 'personal_access_token' param is populated with the personal access token" do - it 'logs the user in' do + shared_examples 'authenticates user and returns response with ok status' do + it 'authenticates user and returns response with ok status' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) - get url, params: default_params.merge(private_token: personal_access_token.token) + subject - expect(response).to have_gitlab_http_status(:ok) expect(controller.current_user).to eq(user) + expect(response).to have_gitlab_http_status(:ok) end + end - it 'does not log the user in if page is public', if: params[:public] do - get url, params: default_params + shared_examples 'does not authenticate user and returns response with ok status' do + it 'does not authenticate user and returns response with ok status' do + subject - expect(response).to have_gitlab_http_status(:ok) expect(controller.current_user).to be_nil + expect(response).to have_gitlab_http_status(:ok) end end - context 'when the personal access token has no api scope', unless: params[:public] do - it 'does not log the user in' do + shared_examples 'does not return response with ok status' do + it 'does not return response with ok status' do # Several instances of where these specs are shared route the request # through ApplicationController#route_not_found which does not involve # the usual auth code from Devise, so does not increment the # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] + unless ignore_metrics expect(authentication_metrics) .to increment(:user_unauthenticated_counter) end - personal_access_token.update!(scopes: [:read_user]) - - get url, params: default_params.merge(private_token: personal_access_token.token) + subject expect(response).not_to have_gitlab_http_status(:ok) end end - context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do - it 'logs the user in' do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) + shared_examples 'using valid token' do + context 'when resource is private', unless: public_resource do + include_examples 'authenticates user and returns response with ok status' - headers = { 'PRIVATE-TOKEN': personal_access_token.token } - get url, params: default_params, headers: headers + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } - expect(response).to have_gitlab_http_status(:ok) + include_examples 'does not return response with ok status' + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + include_examples 'authenticates user and returns response with ok status' + end + end + end + + context 'when resource is public', if: public_resource do + include_examples 'authenticates user and returns response with ok status' + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + include_examples 'does not authenticate user and returns response with ok status' + end end end - it "doesn't log the user in otherwise", unless: params[:public] do - # Several instances of where these specs are shared route the request - # through ApplicationController#route_not_found which does not involve - # the usual auth code from Devise, so does not increment the - # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) + shared_examples 'using invalid token' do + context 'when resource is private', unless: public_resource do + include_examples 'does not return response with ok status' + end + + context 'when resource is public', if: public_resource do + include_examples 'does not authenticate user and returns response with ok status' + end + end + + shared_examples 'personal access token has no api scope' do + context 'when the personal access token has no api scope' do + before do + personal_access_token.update!(scopes: [:read_user]) + end + + context 'when resource is private', unless: public_resource do + include_examples 'does not return response with ok status' + end + + context 'when resource is public', if: public_resource do + include_examples 'does not authenticate user and returns response with ok status' + end + end + end + + describe name do + context "when the 'private_token' param is populated with the personal access token" do + context 'when valid token' do + subject { get url, params: params.merge(private_token: personal_access_token.token) } + + include_examples 'using valid token' + + include_examples 'personal access token has no api scope' + end + + context 'when invalid token' do + subject { get url, params: params.merge(private_token: 'invalid token') } + + include_examples 'using invalid token' + end end - get url, params: default_params.merge(private_token: 'token') + context "when the 'PRIVATE-TOKEN' header is populated with the personal access token" do + context 'when valid token' do + subject do + headers = { 'PRIVATE-TOKEN': personal_access_token.token } + get url, params: params, headers: headers + end - expect(response).not_to have_gitlab_http_status(:ok) + include_examples 'using valid token' + + include_examples 'personal access token has no api scope' + end + + context 'when invalid token' do + subject do + headers = { 'PRIVATE-TOKEN': 'invalid token' } + get url, params: params, headers: headers + end + + include_examples 'using invalid token' + end + end + + context "when the 'feed_token' param is populated with the feed token" do + context 'when valid token' do + subject { get url, params: params.merge(feed_token: user.feed_token) } + + include_examples 'using valid token' + end + + context 'when invalid token' do + subject { get url, params: params.merge(feed_token: 'invalid token') } + + include_examples 'using invalid token' + end + end end end diff --git a/spec/workers/dependency_proxy/cleanup_dependency_proxy_worker_spec.rb b/spec/workers/dependency_proxy/cleanup_dependency_proxy_worker_spec.rb new file mode 100644 index 00000000000..ed0bdefbdb8 --- /dev/null +++ b/spec/workers/dependency_proxy/cleanup_dependency_proxy_worker_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DependencyProxy::CleanupDependencyProxyWorker do + describe '#perform' do + subject { described_class.new.perform } + + context 'when there are records to be deleted' do + it_behaves_like 'an idempotent worker' do + it 'queues the cleanup jobs', :aggregate_failures do + create(:dependency_proxy_blob, :expired) + create(:dependency_proxy_manifest, :expired) + + expect(DependencyProxy::CleanupBlobWorker).to receive(:perform_with_capacity).twice + expect(DependencyProxy::CleanupManifestWorker).to receive(:perform_with_capacity).twice + + subject + end + end + end + + context 'when there are not records to be deleted' do + it_behaves_like 'an idempotent worker' do + it 'does not queue the cleanup jobs', :aggregate_failures do + expect(DependencyProxy::CleanupBlobWorker).not_to receive(:perform_with_capacity) + expect(DependencyProxy::CleanupManifestWorker).not_to receive(:perform_with_capacity) + + subject + end + end + end + end +end diff --git a/spec/workers/dependency_proxy/image_ttl_group_policy_worker_spec.rb b/spec/workers/dependency_proxy/image_ttl_group_policy_worker_spec.rb index d3234f4c212..efa6dccbf06 100644 --- a/spec/workers/dependency_proxy/image_ttl_group_policy_worker_spec.rb +++ b/spec/workers/dependency_proxy/image_ttl_group_policy_worker_spec.rb @@ -17,13 +17,6 @@ RSpec.describe DependencyProxy::ImageTtlGroupPolicyWorker do let_it_be_with_reload(:new_blob) { create(:dependency_proxy_blob, group: group) } let_it_be_with_reload(:new_manifest) { create(:dependency_proxy_manifest, group: group) } - it 'calls the limited capacity workers', :aggregate_failures do - expect(DependencyProxy::CleanupBlobWorker).to receive(:perform_with_capacity) - expect(DependencyProxy::CleanupManifestWorker).to receive(:perform_with_capacity) - - subject - end - it 'updates the old images to expired' do expect { subject } .to change { old_blob.reload.status }.from('default').to('expired') @@ -33,15 +26,6 @@ RSpec.describe DependencyProxy::ImageTtlGroupPolicyWorker do end end - context 'when there are no images to expire' do - it 'does not do anything', :aggregate_failures do - expect(DependencyProxy::CleanupBlobWorker).not_to receive(:perform_with_capacity) - expect(DependencyProxy::CleanupManifestWorker).not_to receive(:perform_with_capacity) - - subject - end - end - context 'counts logging' do let_it_be(:expired_blob) { create(:dependency_proxy_blob, :expired, group: group) } let_it_be(:expired_blob2) { create(:dependency_proxy_blob, :expired, group: group) } diff --git a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb index 393745958be..b928104fb58 100644 --- a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb +++ b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb @@ -4,18 +4,18 @@ require 'spec_helper' RSpec.describe PurgeDependencyProxyCacheWorker do let_it_be(:user) { create(:admin) } - let_it_be(:blob) { create(:dependency_proxy_blob )} - let_it_be(:group, reload: true) { blob.group } - let_it_be(:manifest) { create(:dependency_proxy_manifest, group: group )} + let_it_be_with_refind(:blob) { create(:dependency_proxy_blob )} + let_it_be_with_reload(:group) { blob.group } + let_it_be_with_refind(:manifest) { create(:dependency_proxy_manifest, group: group )} let_it_be(:group_id) { group.id } subject { described_class.new.perform(user.id, group_id) } describe '#perform' do - shared_examples 'not removing blobs and manifests' do - it 'does not remove blobs and manifests', :aggregate_failures do - expect { subject }.not_to change { group.dependency_proxy_blobs.size } - expect { subject }.not_to change { group.dependency_proxy_manifests.size } + shared_examples 'not expiring blobs and manifests' do + it 'does not expire blobs and manifests', :aggregate_failures do + expect { subject }.not_to change { blob.status } + expect { subject }.not_to change { manifest.status } expect(subject).to be_nil end end @@ -25,39 +25,36 @@ RSpec.describe PurgeDependencyProxyCacheWorker do include_examples 'an idempotent worker' do let(:job_args) { [user.id, group_id] } - it 'deletes the blobs and returns ok', :aggregate_failures do - expect(group.dependency_proxy_blobs.size).to eq(1) - expect(group.dependency_proxy_manifests.size).to eq(1) - + it 'expires the blobs and returns ok', :aggregate_failures do subject - expect(group.dependency_proxy_blobs.size).to eq(0) - expect(group.dependency_proxy_manifests.size).to eq(0) + expect(blob).to be_expired + expect(manifest).to be_expired end end end context 'when admin mode is disabled' do - it_behaves_like 'not removing blobs and manifests' + it_behaves_like 'not expiring blobs and manifests' end end context 'a non-admin user' do let(:user) { create(:user) } - it_behaves_like 'not removing blobs and manifests' + it_behaves_like 'not expiring blobs and manifests' end context 'an invalid user id' do let(:user) { double('User', id: 99999 ) } - it_behaves_like 'not removing blobs and manifests' + it_behaves_like 'not expiring blobs and manifests' end context 'an invalid group' do let(:group_id) { 99999 } - it_behaves_like 'not removing blobs and manifests' + it_behaves_like 'not expiring blobs and manifests' end end end |