diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-11 00:10:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-11 00:10:02 +0300 |
commit | b20c558db21c0ba8f033217bd6bc2b470716a105 (patch) | |
tree | 3c6cc717ae6074e581d76161fdcbf13b00612ecf /spec | |
parent | d715acda3b27b7ca9eacbd058d7cb9629638c52d (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
30 files changed, 257 insertions, 443 deletions
diff --git a/spec/factories/ci/job_token/project_scope_links.rb b/spec/factories/ci/job_token/project_scope_links.rb new file mode 100644 index 00000000000..a11edd87e4e --- /dev/null +++ b/spec/factories/ci/job_token/project_scope_links.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :ci_job_token_project_scope_link, class: 'Ci::JobToken::ProjectScopeLink' do + association :source_project, factory: :project + association :target_project, factory: :project + association :added_by, factory: :user + end +end diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index 994bf066cf0..80e94fa1628 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -66,6 +66,7 @@ RSpec.describe 'factories' do # associations must be unique and cannot be reused, or the factory default # is being mutated. skip_factory_defaults = %i[ + ci_job_token_project_scope_link evidence exported_protected_branch fork_network_member diff --git a/spec/features/admin/clusters/applications_spec.rb b/spec/features/admin/clusters/applications_spec.rb deleted file mode 100644 index e083e4fee4c..00000000000 --- a/spec/features/admin/clusters/applications_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../spec/features/clusters/installing_applications_shared_examples' - -RSpec.describe 'Instance-level Cluster Applications', :js do - include GoogleApi::CloudPlatformHelpers - - let(:user) { create(:admin) } - - before do - sign_in(user) - gitlab_enable_admin_mode_sign_in(user) - end - - describe 'Installing applications' do - include_examples "installing applications on a cluster" do - let(:cluster_path) { admin_cluster_path(cluster) } - let(:cluster_factory_args) { [:instance] } - end - end -end diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index 1281d890ef7..21da92c9f43 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -15,10 +15,9 @@ RSpec.describe 'Contributions Calendar', :js do issue_title = 'Bug in old browser' issue_params = { title: issue_title } - def get_cell_color_selector(contributions) - activity_colors = ["#ededed", "rgb(172, 213, 242)", "rgb(127, 168, 201)", "rgb(82, 123, 160)", "rgb(37, 78, 119)"] + def get_cell_level_selector(contributions) # We currently don't actually test the cases with contributions >= 20 - activity_colors_index = + activity_level_index = if contributions > 0 && contributions < 10 1 elsif contributions >= 10 && contributions < 20 @@ -31,7 +30,7 @@ RSpec.describe 'Contributions Calendar', :js do 0 end - ".user-contrib-cell[fill='#{activity_colors[activity_colors_index]}']" + ".user-contrib-cell:not(.contrib-legend)[data-level='#{activity_level_index}']" end def get_cell_date_selector(contributions, date) @@ -42,7 +41,7 @@ RSpec.describe 'Contributions Calendar', :js do "#{contributions} #{'contribution'.pluralize(contributions)}" end - "#{get_cell_color_selector(contributions)}[title='#{contribution_text}<br /><span class=\"gl-text-gray-300\">#{date}</span>']" + "#{get_cell_level_selector(contributions)}[title='#{contribution_text}<br /><span class=\"gl-text-gray-300\">#{date}</span>']" end def push_code_contribution @@ -137,7 +136,7 @@ RSpec.describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity square for 1 contribution', :sidekiq_might_not_need_inline do - expect(find('#js-overview')).to have_selector(get_cell_color_selector(contribution_count), count: 1) + expect(find('#js-overview')).to have_selector(get_cell_level_selector(contribution_count), count: 1) today = Date.today.strftime(date_format) expect(find('#js-overview')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) @@ -187,7 +186,7 @@ RSpec.describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity squares for both days', :sidekiq_might_not_need_inline do - expect(find('#js-overview')).to have_selector(get_cell_color_selector(1), count: 2) + expect(find('#js-overview')).to have_selector(get_cell_level_selector(1), count: 2) end it 'displays calendar activity square for yesterday', :sidekiq_might_not_need_inline do diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index 84a18a45d35..cba8aaef1ef 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -31,30 +31,6 @@ RSpec.describe 'Clusterable > Show page' do expect(page).to have_content('Kubernetes cluster was successfully updated.') end - context 'when there is a cluster with ingress and external ip', :js do - before do - cluster.create_application_ingress!(external_ip: '192.168.1.100') - - visit cluster_path - end - - it 'shows help text with the domain as an alternative to custom domain', :js do - within '.js-cluster-details-form' do - expect(find(cluster_ingress_help_text_selector).text).to include('192.168.1.100') - end - end - end - - context 'when there is no ingress' do - it 'alternative to custom domain is not shown' do - visit cluster_path - - within '.js-cluster-details-form' do - expect(page).not_to have_selector(cluster_ingress_help_text_selector) - end - end - end - it 'does not show the environments tab' do visit cluster_path diff --git a/spec/features/clusters/cluster_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb index 0e2739a45b1..20c07f4d6ac 100644 --- a/spec/features/clusters/cluster_health_dashboard_spec.rb +++ b/spec/features/clusters/cluster_health_dashboard_spec.rb @@ -27,8 +27,8 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory expect(page).to have_css('.cluster-health-graphs') end - context 'no prometheus enabled' do - it 'shows install prometheus message' do + context 'no prometheus available' do + it 'shows enable Prometheus message' do visit cluster_path click_link 'Health' @@ -82,12 +82,12 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory def stub_empty_response stub_prometheus_request(/prometheus-prometheus-server/, status: 204, body: {}) - stub_prometheus_request(/prometheus\/api\/v1/, status: 204, body: {}) + stub_prometheus_request(%r{prometheus/api/v1}, status: 204, body: {}) end def stub_connected stub_prometheus_request(/prometheus-prometheus-server/, body: prometheus_values_body) - stub_prometheus_request(/prometheus\/api\/v1/, body: prometheus_values_body) + stub_prometheus_request(%r{prometheus/api/v1}, body: prometheus_values_body) end end end diff --git a/spec/features/clusters/installing_applications_shared_examples.rb b/spec/features/clusters/installing_applications_shared_examples.rb deleted file mode 100644 index c422aa2be72..00000000000 --- a/spec/features/clusters/installing_applications_shared_examples.rb +++ /dev/null @@ -1,252 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples "installing applications for a cluster" do - before do - # Reduce interval from 10 seconds which is too long for an automated test - stub_const("#{Clusters::ClustersController}::STATUS_POLLING_INTERVAL", 500) - - visit cluster_path - end - - context 'when cluster is being created' do - let(:cluster) { create(:cluster, :providing_by_gcp, *cluster_factory_args) } - - it 'user is unable to install applications' do - expect(page).not_to have_text('Helm') - expect(page).not_to have_text('Install') - end - end - - context 'when cluster is created' do - let(:cluster) { create(:cluster, :provided_by_gcp, *cluster_factory_args) } - - before do - page.within('.js-edit-cluster-form') do - click_link 'Applications' - end - end - - it 'user can install applications' do - wait_for_requests - - application_row = '.js-cluster-application-row-ingress' - - page.within(application_row) do - expect(page).not_to have_css('.js-cluster-application-install-button[disabled]') - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install') - end - end - - it 'does not show the Helm application' do - expect(page).not_to have_selector(:css, '.js-cluster-application-row-helm') - end - - context 'when user installs Knative' do - context 'on an abac cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled, *cluster_factory_args) } - - it 'shows info block and not be installable' do - page.within('.js-cluster-application-row-knative') do - expect(page).to have_css('.rbac-notice') - expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') - end - end - end - - context 'on an rbac cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, *cluster_factory_args) } - - it 'does not show callout block and be installable' do - page.within('.js-cluster-application-row-knative') do - expect(page).not_to have_css('p', text: 'You must have an RBAC-enabled cluster', visible: :all) - expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') - end - end - - describe 'when user clicks install button' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) - - page.within('.js-cluster-application-row-knative') do - expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') - - page.find('.js-knative-domainname').set("domain.example.org") - - click_button 'Install' - - wait_for_requests - - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_knative.make_installing! - Clusters::Cluster.last.application_knative.make_installed! - Clusters::Cluster.last.application_knative.update_attribute(:external_ip, '127.0.0.1') - end - end - - it 'shows status transition' do - page.within('.js-cluster-application-row-knative') do - expect(page).to have_field('Knative Domain Name:', with: 'domain.example.org') - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - - expect(page).to have_content('Knative was successfully installed on your Kubernetes cluster') - expect(page).to have_css('.js-knative-save-domain-button'), exact_text: 'Save changes' - end - - it 'can then update the domain' do - page.within('.js-cluster-application-row-knative') do - expect(ClusterPatchAppWorker).to receive(:perform_async) - - expect(page).to have_field('Knative Domain Name:', with: 'domain.example.org') - - page.find('.js-knative-domainname').set("new.domain.example.org") - - click_button 'Save changes' - - wait_for_requests - - expect(page).to have_field('Knative Domain Name:', with: 'new.domain.example.org') - end - end - end - end - end - - context 'when user installs Cert Manager' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) - end - - it 'shows status transition' do - page.within('.js-cluster-application-row-cert_manager') do - click_button 'Install' - wait_for_requests - - expect(page).to have_field('Issuer Email', with: cluster.user.email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installing! - - expect(page).to have_field('Issuer Email', with: cluster.user.email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installed! - - expect(page).to have_field('Issuer Email', with: cluster.user.email) - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - - expect(page).to have_content('Cert-Manager was successfully installed on your Kubernetes cluster') - end - - it 'installs with custom email' do - custom_email = 'new_email@example.org' - - page.within('.js-cluster-application-row-cert_manager') do - # Wait for the polling to finish - wait_for_requests - - page.find('.js-email').set(custom_email) - click_button 'Install' - wait_for_requests - - expect(page).to have_field('Issuer Email', with: custom_email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installing! - - expect(page).to have_field('Issuer Email', with: custom_email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installed! - - expect(page).to have_field('Issuer Email', with: custom_email) - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - end - end - - context 'when user installs Elastic Stack' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - - page.within('.js-cluster-application-row-elastic_stack') do - click_button 'Install' - end - - wait_for_requests - end - - it 'shows status transition' do - page.within('.js-cluster-application-row-elastic_stack') do - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_elastic_stack.make_installing! - - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_elastic_stack.make_installed! - - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - - expect(page).to have_content('Elastic Stack was successfully installed on your Kubernetes cluster') - end - end - - context 'when user installs Ingress' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) - - page.within('.js-cluster-application-row-ingress') do - expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') - page.find(:css, '.js-cluster-application-install-button').click - - wait_for_requests - end - end - - it 'shows the status transition' do - page.within('.js-cluster-application-row-ingress') do - # FE sends request and gets the response, then the buttons is "Installing" - expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing') - - Clusters::Cluster.last.application_ingress.make_installing! - - # FE starts polling and update the buttons to "Installing" - expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing') - - # The application becomes installed but we keep waiting for external IP address - Clusters::Cluster.last.application_ingress.make_installed! - - expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installed') - expect(page).to have_selector('.js-no-endpoint-message') - expect(page).to have_selector('.js-ingress-ip-loading-icon') - - # We receive the external IP address and display - Clusters::Cluster.last.application_ingress.update!(external_ip: '192.168.1.100') - - expect(page).not_to have_css('button', exact_text: 'Install', visible: :all) - expect(page).not_to have_css('button', exact_text: 'Installing', visible: :all) - expect(page).to have_css('.js-cluster-application-uninstall-button:not([disabled])', exact_text: 'Uninstall') - expect(page).not_to have_css('p', text: 'The endpoint is in the process of being assigned', visible: :all) - expect(page.find('.js-endpoint').value).to eq('192.168.1.100') - end - - expect(page).to have_content('Ingress was successfully installed on your Kubernetes cluster') - end - end - end -end - -RSpec.shared_examples "installing applications on a cluster" do - it_behaves_like "installing applications for a cluster", false - it_behaves_like "installing applications for a cluster", true -end diff --git a/spec/features/groups/clusters/applications_spec.rb b/spec/features/groups/clusters/applications_spec.rb deleted file mode 100644 index 324ef24efc4..00000000000 --- a/spec/features/groups/clusters/applications_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../spec/features/clusters/installing_applications_shared_examples' - -RSpec.describe 'Group-level Cluster Applications', :js do - include GoogleApi::CloudPlatformHelpers - - let(:group) { create(:group) } - let(:user) { create(:user) } - - before do - group.add_maintainer(user) - sign_in(user) - end - - describe 'Installing applications' do - include_examples "installing applications on a cluster" do - let(:cluster_path) { group_cluster_path(group, cluster) } - let(:cluster_factory_args) { [:group, groups: [group]] } - end - end -end diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb deleted file mode 100644 index 74b477dd85d..00000000000 --- a/spec/features/projects/clusters/applications_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../spec/features/clusters/installing_applications_shared_examples' - -RSpec.describe 'Project-level Cluster Applications', :js do - include GoogleApi::CloudPlatformHelpers - - let(:project) { create(:project) } - let(:user) { create(:user) } - - before do - project.add_maintainer(user) - sign_in(user) - end - - describe 'Installing applications' do - include_examples "installing applications on a cluster" do - let(:cluster_path) { project_cluster_path(project, cluster) } - let(:cluster_factory_args) { [projects: [project]] } - end - end -end diff --git a/spec/finders/ci/auth_job_finder_spec.rb b/spec/finders/ci/auth_job_finder_spec.rb index 6cd58f5cd01..78827c9ddee 100644 --- a/spec/finders/ci/auth_job_finder_spec.rb +++ b/spec/finders/ci/auth_job_finder_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' RSpec.describe Ci::AuthJobFinder do - let_it_be(:job, reload: true) { create(:ci_build, status: :running) } + let_it_be(:user, reload: true) { create(:user) } + let_it_be(:job, reload: true) { create(:ci_build, status: :running, user: user) } let(:token) { job.token } @@ -55,10 +56,31 @@ RSpec.describe Ci::AuthJobFinder do describe '#execute' do subject(:execute) { finder.execute } - before do - job.success! + context 'when job is not running' do + before do + job.success! + end + + it { is_expected.to be_nil } end - it { is_expected.to be_nil } + context 'when job is running', :request_store do + it 'sets ci_job_token_scope on the job user', :aggregate_failures do + expect(subject).to eq(job) + expect(subject.user).to be_from_ci_job_token + expect(subject.user.ci_job_token_scope.source_project).to eq(job.project) + end + + context 'when feature flag ci_scoped_job_token is disabled' do + before do + stub_feature_flags(ci_scoped_job_token: false) + end + + it 'does not set ci_job_token_scope on the job user' do + expect(subject).to eq(job) + expect(subject.user).not_to be_from_ci_job_token + end + end + end end end diff --git a/spec/frontend/clusters/forms/components/integration_form_spec.js b/spec/frontend/clusters/forms/components/integration_form_spec.js index c5cec4c4fdb..b129baa2d83 100644 --- a/spec/frontend/clusters/forms/components/integration_form_spec.js +++ b/spec/frontend/clusters/forms/components/integration_form_spec.js @@ -15,7 +15,6 @@ describe('ClusterIntegrationForm', () => { editable: true, environmentScope: '*', baseDomain: 'testDomain', - applicationIngressExternalIp: null, }; const createWrapper = (storeValues = defaultStoreValues) => { @@ -72,18 +71,6 @@ describe('ClusterIntegrationForm', () => { expect(findSubmitButton().exists()).toBe(false); }); }); - - it('does not render external IP block if applicationIngressExternalIp was not passed', () => { - createWrapper({ ...defaultStoreValues }); - - expect(wrapper.find('.js-ingress-domain-help-text').exists()).toBe(false); - }); - - it('renders external IP block if applicationIngressExternalIp was passed', () => { - createWrapper({ ...defaultStoreValues, applicationIngressExternalIp: '127.0.0.1' }); - - expect(wrapper.find('.js-ingress-domain-help-text').exists()).toBe(true); - }); }); describe('reactivity', () => { diff --git a/spec/frontend/pages/users/activity_calendar_spec.js b/spec/frontend/pages/users/activity_calendar_spec.js new file mode 100644 index 00000000000..b33e92e14b2 --- /dev/null +++ b/spec/frontend/pages/users/activity_calendar_spec.js @@ -0,0 +1,16 @@ +import { getLevelFromContributions } from '~/pages/users/activity_calendar'; + +describe('getLevelFromContributions', () => { + it.each([ + [0, 0], + [1, 1], + [9, 1], + [10, 2], + [19, 2], + [20, 3], + [30, 4], + [99, 4], + ])('.getLevelFromContributions(%i, %i)', (count, expected) => { + expect(getLevelFromContributions(count)).toBe(expected); + }); +}); diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb index cddcaf09b74..7475ed2796f 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -50,7 +50,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do end shared_examples 'find user from job token' do |without_job_token_allowed| - context 'when route is allowed to be authenticated' do + context 'when route is allowed to be authenticated', :request_store do let(:route_authentication_setting) { { job_token_allowed: true } } context 'for an invalid token' do @@ -68,6 +68,8 @@ RSpec.describe Gitlab::Auth::AuthFinders do it 'return user' do expect(subject).to eq(user) expect(@current_authenticated_job).to eq job + expect(subject).to be_from_ci_job_token + expect(subject.ci_job_token_scope.source_project).to eq(job.project) end end @@ -81,7 +83,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do end end - context 'when route is not allowed to be authenticated' do + context 'when route is not allowed to be authenticated', :request_store do let(:route_authentication_setting) { { job_token_allowed: false } } context 'with a running job' do @@ -96,6 +98,8 @@ RSpec.describe Gitlab::Auth::AuthFinders do it 'returns the user' do expect(subject).to eq(user) expect(@current_authenticated_job).to eq job + expect(subject).to be_from_ci_job_token + expect(subject.ci_job_token_scope.source_project).to eq(job.project) end else it 'returns nil' do diff --git a/spec/models/ci/job_token/project_scope_link_spec.rb b/spec/models/ci/job_token/project_scope_link_spec.rb new file mode 100644 index 00000000000..d18495b9312 --- /dev/null +++ b/spec/models/ci/job_token/project_scope_link_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::JobToken::ProjectScopeLink do + it { is_expected.to belong_to(:source_project) } + it { is_expected.to belong_to(:target_project) } + it { is_expected.to belong_to(:added_by) } + + let_it_be(:project) { create(:project) } + + describe 'unique index' do + let!(:link) { create(:ci_job_token_project_scope_link) } + + it 'raises an error' do + expect do + create(:ci_job_token_project_scope_link, + source_project: link.source_project, + target_project: link.target_project) + end.to raise_error(ActiveRecord::RecordNotUnique) + end + end + + describe 'validations' do + it 'must have a source project', :aggregate_failures do + link = build(:ci_job_token_project_scope_link, source_project: nil) + + expect(link).not_to be_valid + expect(link.errors[:source_project]).to contain_exactly("can't be blank") + end + + it 'must have a target project', :aggregate_failures do + link = build(:ci_job_token_project_scope_link, target_project: nil) + + expect(link).not_to be_valid + expect(link.errors[:target_project]).to contain_exactly("can't be blank") + end + + it 'must have a target project different than source project', :aggregate_failures do + link = build(:ci_job_token_project_scope_link, target_project: project, source_project: project) + + expect(link).not_to be_valid + expect(link.errors[:target_project]).to contain_exactly("can't be the same as the source project") + end + end + + describe '.from_project' do + subject { described_class.from_project(project) } + + let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) } + let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) } + + it 'returns only the links having the given source project' do + expect(subject).to contain_exactly(source_link) + end + end + + describe '.to_project' do + subject { described_class.to_project(project) } + + let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) } + let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) } + + it 'returns only the links having the given target project' do + expect(subject).to contain_exactly(target_link) + end + end +end diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb new file mode 100644 index 00000000000..2fbfbac64f8 --- /dev/null +++ b/spec/models/ci/job_token/scope_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::JobToken::Scope do + let_it_be(:project) { create(:project) } + + let(:scope) { described_class.new(project) } + + describe '#all_projects' do + subject(:all_projects) { scope.all_projects } + + context 'when no projects are added to the scope' do + it 'returns the project defining the scope' do + expect(all_projects).to contain_exactly(project) + end + end + + context 'when other projects are added to the scope' do + let_it_be(:scoped_project) { create(:project) } + let_it_be(:unscoped_project) { create(:project) } + + let!(:link_in_scope) { create(:ci_job_token_project_scope_link, source_project: project, target_project: scoped_project) } + let!(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project) } + + it 'returns all projects that can be accessed from a given scope' do + expect(subject).to contain_exactly(project, scoped_project) + end + end + end + + describe 'includes?' do + subject { scope.includes?(target_project) } + + context 'when param is the project defining the scope' do + let(:target_project) { project } + + it { is_expected.to be_truthy } + end + + context 'when param is a project in scope' do + let(:target_link) { create(:ci_job_token_project_scope_link, source_project: project) } + let(:target_project) { target_link.target_project } + + it { is_expected.to be_truthy } + end + + context 'when param is a project in another scope' do + let(:scope_link) { create(:ci_job_token_project_scope_link) } + let(:target_project) { scope_link.target_project } + + it { is_expected.to be_falsey } + end + end +end diff --git a/spec/models/hooks/web_hook_log_archived_spec.rb b/spec/models/hooks/web_hook_log_archived_spec.rb deleted file mode 100644 index ac726dbaf4f..00000000000 --- a/spec/models/hooks/web_hook_log_archived_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe WebHookLogArchived do - let(:source_table) { WebHookLog } - let(:destination_table) { described_class } - - it 'has the same columns as the source table' do - column_names_from_source_table = column_names(source_table) - column_names_from_destination_table = column_names(destination_table) - - expect(column_names_from_destination_table).to match_array(column_names_from_source_table) - end - - it 'has the same null constraints as the source table' do - constraints_from_source_table = null_constraints(source_table) - constraints_from_destination_table = null_constraints(destination_table) - - expect(constraints_from_destination_table.to_a).to match_array(constraints_from_source_table.to_a) - end - - it 'inserts the same record as the one in the source table', :aggregate_failures do - expect { create(:web_hook_log) }.to change { destination_table.count }.by(1) - - event_from_source_table = source_table.connection.select_one( - "SELECT * FROM #{source_table.table_name} ORDER BY created_at desc LIMIT 1" - ) - event_from_destination_table = destination_table.connection.select_one( - "SELECT * FROM #{destination_table.table_name} ORDER BY created_at desc LIMIT 1" - ) - - expect(event_from_destination_table).to eq(event_from_source_table) - end - - def column_names(table) - table.connection.select_all(<<~SQL) - SELECT c.column_name - FROM information_schema.columns c - WHERE c.table_name = '#{table.table_name}' - SQL - end - - def null_constraints(table) - table.connection.select_all(<<~SQL) - SELECT c.column_name, c.is_nullable - FROM information_schema.columns c - WHERE c.table_name = '#{table.table_name}' - AND c.column_name != 'created_at' - SQL - end -end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index d0abcfbd091..53d9bdb01a3 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -1385,4 +1385,53 @@ RSpec.describe ProjectPolicy do end end end + + describe 'when user is authenticated via CI_JOB_TOKEN', :request_store do + let(:current_user) { developer } + let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) } + + before do + current_user.set_ci_job_token_scope!(job) + end + + context 'when accessing a private project' do + let(:project) { private_project } + + context 'when the job token comes from the same project' do + let(:scope_project) { project } + + it { is_expected.to be_allowed(:developer_access) } + end + + context 'when the job token comes from another project' do + let(:scope_project) { create(:project, :private) } + + before do + scope_project.add_developer(current_user) + end + + it { is_expected.to be_disallowed(:guest_access) } + end + end + + context 'when accessing a public project' do + let(:project) { public_project } + + context 'when the job token comes from the same project' do + let(:scope_project) { project } + + it { is_expected.to be_allowed(:developer_access) } + end + + context 'when the job token comes from another project' do + let(:scope_project) { create(:project, :private) } + + before do + scope_project.add_developer(current_user) + end + + it { is_expected.to be_disallowed(:public_access) } + end + end + end end diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index a5e40eec919..6886fff2282 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -18,7 +18,7 @@ RSpec.describe API::GenericPackages do let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) } let(:user) { personal_access_token.user } - let(:ci_build) { create(:ci_build, :running, user: user) } + let(:ci_build) { create(:ci_build, :running, user: user, project: project) } def auth_header return {} if user_role == :anonymous diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index e678b6cf1c8..0143340de11 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -11,7 +11,7 @@ RSpec.describe API::GoProxy do let_it_be(:base) { "#{Settings.build_gitlab_go_url}/#{project.full_path}" } let_it_be(:oauth) { create :oauth_access_token, scopes: 'api', resource_owner: user } - let_it_be(:job) { create :ci_build, user: user, status: :running } + let_it_be(:job) { create :ci_build, user: user, status: :running, project: project } let_it_be(:pa_token) { create :personal_access_token, user: user } let_it_be(:modules) do diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index 2bb6d05f54b..d9f11b19e6e 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -15,7 +15,7 @@ RSpec.describe API::MavenPackages do let_it_be(:package_file) { package.package_files.with_file_name_like('%.xml').first } let_it_be(:jar_file) { package.package_files.with_file_name_like('%.jar').first } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) } + let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index 8230061546f..ab74da4bda4 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -78,7 +78,7 @@ RSpec.describe API::NpmProjectPackages do context 'with a job token for a different user' do let_it_be(:other_user) { create(:user) } - let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) } + let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user, project: project) } let(:headers) { build_token_auth_header(other_job.token) } diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb index 98458cb8dfa..572736cfc86 100644 --- a/spec/requests/api/nuget_project_packages_spec.rb +++ b/spec/requests/api/nuget_project_packages_spec.rb @@ -192,7 +192,7 @@ RSpec.describe API::NugetProjectPackages do it_behaves_like 'deploy token for package uploads' it_behaves_like 'job token for package uploads', authorize_endpoint: true do - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } end it_behaves_like 'rejects nuget access with unknown target id' @@ -260,7 +260,7 @@ RSpec.describe API::NugetProjectPackages do it_behaves_like 'deploy token for package uploads' it_behaves_like 'job token for package uploads' do - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } end it_behaves_like 'rejects nuget access with unknown target id' diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index 552ef2b2120..86925e6a0ba 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -13,7 +13,7 @@ RSpec.describe API::PypiPackages do let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let(:headers) { {} } context 'simple API endpoint' do diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index dad3e34404b..81a4fcdbcac 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -775,7 +775,7 @@ RSpec.describe API::Releases do end context 'when using JOB-TOKEN auth' do - let(:job) { create(:ci_build, user: maintainer) } + let(:job) { create(:ci_build, user: maintainer, project: project) } let(:params) do { name: 'Another release', diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb index 09b63139e54..7d863b55bbe 100644 --- a/spec/requests/api/rubygem_packages_spec.rb +++ b/spec/requests/api/rubygem_packages_spec.rb @@ -10,7 +10,7 @@ RSpec.describe API::RubygemPackages do let_it_be_with_reload(:project) { create(:project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:headers) { {} } diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb index 6803c09b8c2..b04f5ad9a94 100644 --- a/spec/requests/api/terraform/modules/v1/packages_spec.rb +++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb @@ -12,7 +12,7 @@ RSpec.describe API::Terraform::Modules::V1::Packages do let_it_be(:package) { create(:terraform_module_package, project: project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 279c65fc2f4..7cf46f6adc6 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -883,10 +883,10 @@ RSpec.describe 'Git HTTP requests' do context 'when admin mode is enabled', :enable_admin_mode do it_behaves_like 'can download code only' - it 'downloads from other project get status 403' do + it 'downloads from other project get status 404' do clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(:not_found) end end diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 0e3a0252638..fda8b2ecec6 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -569,7 +569,7 @@ RSpec.describe 'Git LFS API and storage' do let(:pipeline) { create(:ci_empty_pipeline, project: other_project) } # I'm not sure what this tests that is different from the previous test - it_behaves_like 'LFS http 403 response' + it_behaves_like 'LFS http 404 response' end end @@ -1043,7 +1043,7 @@ RSpec.describe 'Git LFS API and storage' do let(:pipeline) { create(:ci_empty_pipeline, project: other_project) } # I'm not sure what this tests that is different from the previous test - it_behaves_like 'LFS http 403 response' + it_behaves_like 'LFS http 404 response' end end diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb index ac53be1a1cb..c69a987c00d 100644 --- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb @@ -8,11 +8,11 @@ RSpec.shared_context 'conan api setup' do let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } let_it_be(:base_secret) { SecureRandom.base64(64) } - let_it_be(:job) { create(:ci_build, :running, user: user) } - let_it_be(:job_token) { job.token } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let(:project) { package.project } + let(:job) { create(:ci_build, :running, user: user, project: project) } + let(:job_token) { job.token } let(:auth_token) { personal_access_token.token } let(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb index 815108be447..c737091df48 100644 --- a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb @@ -11,7 +11,7 @@ RSpec.shared_context 'npm api setup' do let_it_be(:package, reload: true) { create(:npm_package, project: project, name: "@#{group.path}/scoped_package") } let_it_be(:token) { create(:oauth_access_token, scopes: 'api', resource_owner: user) } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) } + let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } |