diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index f73aa2705b759839c51fa2ca514704b91945fa91..d31d62265597a93a74ed189e6dd3db2708665923 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -24,7 +24,13 @@ def update_file_store end def project_allowed_access?(project) - projects.exists?(project.lfs_storage_project.id) + if project.fork_network_member + lfs_objects_projects + .where("EXISTS(?)", project.fork_network.fork_network_members.select(1).where("fork_network_members.project_id = lfs_objects_projects.project_id")) + .exists? + else + lfs_objects_projects.where(project_id: project.id).exists? + end end def local_store? diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb index 85bfc3f1387f93e1428bd59e03569f8027f4efe6..5b22f9e3e811a5ecaeb707e669ad00a04fddcf08 100644 --- a/spec/models/lfs_object_spec.rb +++ b/spec/models/lfs_object_spec.rb @@ -31,6 +31,46 @@ end end + describe '#project_allowed_access?' do + set(:lfs_object) { create(:lfs_objects_project).lfs_object } + set(:project) { create(:project) } + + it 'returns true when project is linked' do + create(:lfs_objects_project, lfs_object: lfs_object, project: project) + + expect(lfs_object.project_allowed_access?(project)).to eq(true) + end + + it 'returns false when project is not linked' do + expect(lfs_object.project_allowed_access?(project)).to eq(false) + end + + context 'when project is a member of a fork network' do + set(:fork_network) { create(:fork_network) } + set(:fork_network_root_project) { fork_network.root_project } + set(:fork_network_membership) { create(:fork_network_member, project: project, fork_network: fork_network) } + + it 'returns true for all members when forked project is linked' do + create(:lfs_objects_project, lfs_object: lfs_object, project: project) + + expect(lfs_object.project_allowed_access?(project)).to eq(true) + expect(lfs_object.project_allowed_access?(fork_network_root_project)).to eq(true) + end + + it 'returns true for all members when root of network is linked' do + create(:lfs_objects_project, lfs_object: lfs_object, project: fork_network_root_project) + + expect(lfs_object.project_allowed_access?(project)).to eq(true) + expect(lfs_object.project_allowed_access?(fork_network_root_project)).to eq(true) + end + + it 'returns false when no member of fork network is linked' do + expect(lfs_object.project_allowed_access?(project)).to eq(false) + expect(lfs_object.project_allowed_access?(fork_network_root_project)).to eq(false) + end + end + end + describe '#schedule_background_upload' do before do stub_lfs_setting(enabled: true) diff --git a/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb index d3cadf2ba7c653404102130fd00b236bc471c1c0..5dea17069f9b1ecb67979bbb8437964bc5c83ddf 100644 --- a/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb +++ b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb @@ -25,13 +25,17 @@ context 'when lfs is enabled' do before do allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true) + allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true) + allow(controller).to receive(:send_file) { controller.head :ok } end - context 'when project has access' do + def link_project(project) + project.lfs_objects << lfs_object + end + + context 'when the project is linked to the LfsObject' do before do - project.lfs_objects << lfs_object - allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true) - allow(controller).to receive(:send_file) { controller.head :ok } + link_project(project) end it 'serves the file' do @@ -76,13 +80,68 @@ end end - context 'when project does not have access' do + context 'when project is not linked to the LfsObject' do it 'does not serve the file' do subject expect(response).to have_gitlab_http_status(404) end end + + context 'when the project is part of a fork network' do + shared_examples 'a controller that correctly serves lfs files within a fork network' do + it do + expect(fork_network_member).not_to eq(fork_network.root_project) + end + + it 'does not serve the file if no members are linked to the LfsObject' do + subject + + expect(response).to have_gitlab_http_status(404) + end + + it 'serves the file when the fork network root is linked to the LfsObject' do + link_project(fork_network.root_project) + + subject + + expect(response).to have_gitlab_http_status(200) + end + + it 'serves the file when the fork network member is linked to the LfsObject' do + link_project(fork_network_member) + + subject + + expect(response).to have_gitlab_http_status(200) + end + end + + context 'when the project is the root of the fork network' do + let!(:fork_network) { create(:fork_network, root_project: project) } + let!(:fork_network_member) { create(:fork_network_member, fork_network: fork_network).project } + + before do + project.reload + end + + it_behaves_like 'a controller that correctly serves lfs files within a fork network' + end + + context 'when the project is a downstream member of the fork network' do + let!(:fork_network) { create(:fork_network) } + let!(:fork_network_member) do + create(:fork_network_member, project: project, fork_network: fork_network) + project + end + + before do + project.reload + end + + it_behaves_like 'a controller that correctly serves lfs files within a fork network' + end + end end context 'when lfs is not enabled' do