diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-13 00:10:43 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-13 00:10:43 +0300 |
commit | 3c0d198d0c0fff5a7949f676bb6cb414908bca02 (patch) | |
tree | 3101b330803e94e2110fc71ecc6b8324a71fb66a /spec | |
parent | b0fe37ef58e19bc6f5d1db99f2ddc809f180a6f5 (diff) |
Add latest changes from gitlab-org/security/gitlab@12-7-stable-ee
Diffstat (limited to 'spec')
5 files changed, 387 insertions, 61 deletions
diff --git a/spec/factories/project_authorizations.rb b/spec/factories/project_authorizations.rb new file mode 100644 index 00000000000..ffdf5576f84 --- /dev/null +++ b/spec/factories/project_authorizations.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :project_authorization do + user + project + access_level { Gitlab::Access::REPORTER } + end +end diff --git a/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb b/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb new file mode 100644 index 00000000000..1ef2c451aa2 --- /dev/null +++ b/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb @@ -0,0 +1,243 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, :migration, schema: 20200204113223 do + let(:users_table) { table(:users) } + let(:namespaces_table) { table(:namespaces) } + let(:projects_table) { table(:projects) } + let(:project_authorizations_table) { table(:project_authorizations) } + let(:members_table) { table(:members) } + let(:group_group_links) { table(:group_group_links) } + let(:project_group_links) { table(:project_group_links) } + + let(:user) { users_table.create!(id: 1, email: 'user@example.com', projects_limit: 10) } + let(:group) { namespaces_table.create!(type: 'Group', name: 'group', path: 'group') } + + subject { described_class.new.perform([user.id]) } + + context 'missing authorization' do + context 'personal project' do + before do + user_namespace = namespaces_table.create!(owner_id: user.id, name: 'User', path: 'user') + projects_table.create!(id: 1, + name: 'personal-project', + path: 'personal-project', + visibility_level: 0, + namespace_id: user_namespace.id) + end + + it 'creates correct authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(0).to(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 40)])) + end + end + + context 'group membership' do + before do + projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: group.id) + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 20, notification_level: 3) + end + + it 'creates correct authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(0).to(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)])) + end + end + + context 'inherited group membership' do + before do + sub_group = namespaces_table.create!(type: 'Group', name: 'subgroup', + path: 'subgroup', parent_id: group.id) + projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: sub_group.id) + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 20, notification_level: 3) + end + + it 'creates correct authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(0).to(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)])) + end + end + + context 'project membership' do + before do + project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: group.id) + members_table.create!(user_id: user.id, source_id: project.id, source_type: 'Project', + type: 'ProjectMember', access_level: 20, notification_level: 3) + end + + it 'creates correct authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(0).to(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)])) + end + end + + context 'shared group' do + before do + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 30, notification_level: 3) + + shared_group = namespaces_table.create!(type: 'Group', name: 'shared group', + path: 'shared-group') + projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0, + namespace_id: shared_group.id) + + group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id, + group_access: 20) + end + + it 'creates correct authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(0).to(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)])) + end + end + + context 'shared project' do + before do + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 30, notification_level: 3) + + another_group = namespaces_table.create!(type: 'Group', name: 'another group', path: 'another-group') + shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project', + visibility_level: 0, namespace_id: another_group.id) + + project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20) + end + + it 'creates correct authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(0).to(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)])) + end + end + end + + context 'unapproved access requests' do + context 'group membership' do + before do + projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: group.id) + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 20, requested_at: Time.now, notification_level: 3) + end + + it 'does not create authorization' do + expect { subject }.not_to change { project_authorizations_table.count }.from(0) + end + end + + context 'inherited group membership' do + before do + sub_group = namespaces_table.create!(type: 'Group', name: 'subgroup', path: 'subgroup', + parent_id: group.id) + projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: sub_group.id) + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 20, requested_at: Time.now, notification_level: 3) + end + + it 'does not create authorization' do + expect { subject }.not_to change { project_authorizations_table.count }.from(0) + end + end + + context 'project membership' do + before do + project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: group.id) + members_table.create!(user_id: user.id, source_id: project.id, source_type: 'Project', + type: 'ProjectMember', access_level: 20, requested_at: Time.now, notification_level: 3) + end + + it 'does not create authorization' do + expect { subject }.not_to change { project_authorizations_table.count }.from(0) + end + end + + context 'shared group' do + before do + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 30, requested_at: Time.now, notification_level: 3) + + shared_group = namespaces_table.create!(type: 'Group', name: 'shared group', + path: 'shared-group') + projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0, + namespace_id: shared_group.id) + + group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id, + group_access: 20) + end + + it 'does not create authorization' do + expect { subject }.not_to change { project_authorizations_table.count }.from(0) + end + end + + context 'shared project' do + before do + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 30, requested_at: Time.now, notification_level: 3) + + another_group = namespaces_table.create!(type: 'Group', name: 'another group', path: 'another-group') + shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project', + visibility_level: 0, namespace_id: another_group.id) + + project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20) + end + + it 'does not create authorization' do + expect { subject }.not_to change { project_authorizations_table.count }.from(0) + end + end + end + + context 'incorrect authorization' do + before do + project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: group.id) + members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace', + type: 'GroupMember', access_level: 30, notification_level: 3) + + project_authorizations_table.create!(user_id: user.id, project_id: project.id, + access_level: 10) + end + + it 'fixes authorization' do + expect { subject }.not_to change { project_authorizations_table.count }.from(1) + expect(project_authorizations_table.all).to( + match_array([have_attributes(user_id: 1, project_id: 1, access_level: 30)])) + end + end + + context 'unwanted authorization' do + before do + project = projects_table.create!(name: 'group-project', path: 'group-project', + visibility_level: 0, namespace_id: group.id) + + project_authorizations_table.create!(user_id: user.id, project_id: project.id, + access_level: 10) + end + + it 'deletes authorization' do + expect { subject }.to change { project_authorizations_table.count }.from(1).to(0) + end + end + + context 'deleted user' do + let(:nonexistent_user_id) { User.maximum(:id).to_i + 999 } + + it 'does not fail' do + expect { described_class.new.perform([nonexistent_user_id]) }.not_to raise_error + end + end +end diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb index 6e5c36172e2..1c579128223 100644 --- a/spec/lib/gitlab/project_authorizations_spec.rb +++ b/spec/lib/gitlab/project_authorizations_spec.rb @@ -97,87 +97,68 @@ describe Gitlab::ProjectAuthorizations do create(:group_group_link, shared_group: shared_group, shared_with_group: group) end - context 'when feature flag share_group_with_group is enabled' do - before do - stub_feature_flags(share_group_with_group: true) - end - - context 'group user' do - let(:user) { group_user } + context 'group user' do + let(:user) { group_user } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'creates proper authorizations' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) - expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER) - end + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) + expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER) end + end - context 'parent group user' do - let(:user) { parent_group_user } + context 'parent group user' do + let(:user) { parent_group_user } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'creates proper authorizations' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil - end + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil end + end - context 'child group user' do - let(:user) { child_group_user } + context 'child group user' do + let(:user) { child_group_user } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'creates proper authorizations' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil - end + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil end end - context 'when feature flag share_group_with_group is disabled' do - before do - stub_feature_flags(share_group_with_group: false) - end - - context 'group user' do - let(:user) { group_user } - - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + context 'user without accepted access request' do + let!(:user) { create(:user) } - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil - end - end + it 'does not have access to group and its projects' do + create(:group_member, :developer, :access_request, user: user, group: group) - context 'parent group user' do - let(:user) { parent_group_user } + mapping = map_access_levels(authorizations) - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) - - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil - end + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil end + end - context 'child group user' do - let(:user) { child_group_user } + context 'unrelated project owner' do + let(:common_id) { [Project.maximum(:id).to_i, Namespace.maximum(:id).to_i].max + 999 } + let!(:group) { create(:group, id: common_id) } + let!(:unrelated_project) { create(:project, id: common_id) } + let(:user) { unrelated_project.owner } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'does not have access to group and its projects' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil - end + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil end end end diff --git a/spec/migrations/schedule_recalculate_project_authorizations_spec.rb b/spec/migrations/schedule_recalculate_project_authorizations_spec.rb new file mode 100644 index 00000000000..a739606ca8f --- /dev/null +++ b/spec/migrations/schedule_recalculate_project_authorizations_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20200204113223_schedule_recalculate_project_authorizations.rb') + +describe ScheduleRecalculateProjectAuthorizations, :migration, :sidekiq do + let(:users_table) { table(:users) } + let(:namespaces_table) { table(:namespaces) } + let(:projects_table) { table(:projects) } + let(:project_authorizations_table) { table(:project_authorizations) } + + let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 1) } + let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 1) } + let(:group) { namespaces_table.create!(id: 1, type: 'Group', name: 'group', path: 'group') } + let(:project) do + projects_table.create!(id: 1, name: 'project', path: 'project', + visibility_level: 0, namespace_id: group.id) + end + + before do + stub_const("#{described_class}::BATCH_SIZE", 1) + + project_authorizations_table.create!(user_id: user1.id, project_id: project.id, access_level: 30) + project_authorizations_table.create!(user_id: user2.id, project_id: project.id, access_level: 30) + end + + it 'schedules background migration' do + Sidekiq::Testing.fake! do + Timecop.freeze do + migrate! + + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + expect(described_class::MIGRATION).to be_scheduled_migration([user1.id]) + expect(described_class::MIGRATION).to be_scheduled_migration([user2.id]) + end + end + end + + it 'ignores projects with higher id than maximum group id' do + another_user = users_table.create!(name: 'another user', email: 'another-user@example.com', + projects_limit: 1) + ignored_project = projects_table.create!(id: 2, name: 'ignored-project', path: 'ignored-project', + visibility_level: 0, namespace_id: group.id) + project_authorizations_table.create!(user_id: another_user.id, project_id: ignored_project.id, + access_level: 30) + + Sidekiq::Testing.fake! do + Timecop.freeze do + migrate! + + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + expect(described_class::MIGRATION).to be_scheduled_migration([user1.id]) + expect(described_class::MIGRATION).to be_scheduled_migration([user2.id]) + end + end + end +end diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index f5a914bb482..d7ba7f5f69e 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -22,6 +22,42 @@ describe Users::RefreshAuthorizedProjectsService do service.execute end + + context 'callbacks' do + let(:callback) { double('callback') } + + context 'incorrect_auth_found_callback callback' do + let(:user) { create(:user) } + let(:service) do + described_class.new(user, + incorrect_auth_found_callback: callback) + end + + it 'is called' do + access_level = Gitlab::Access::DEVELOPER + create(:project_authorization, user: user, project: project, access_level: access_level) + + expect(callback).to receive(:call).with(project.id, access_level).once + + service.execute + end + end + + context 'missing_auth_found_callback callback' do + let(:service) do + described_class.new(user, + missing_auth_found_callback: callback) + end + + it 'is called' do + ProjectAuthorization.delete_all + + expect(callback).to receive(:call).with(project.id, Gitlab::Access::MAINTAINER).once + + service.execute + end + end + end end describe '#execute_without_lease' do |