Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/concerns/sends_blob.rb69
-rw-r--r--app/controllers/projects/avatars_controller.rb12
-rw-r--r--app/controllers/projects/raw_controller.rb37
-rw-r--r--app/helpers/blob_helper.rb22
-rw-r--r--app/serializers/diff_file_entity.rb1
-rw-r--r--changelogs/unreleased/fj-47229-fix-logo-lfs-tracked.yml5
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb53
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb101
-rw-r--r--spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb89
9 files changed, 225 insertions, 164 deletions
diff --git a/app/controllers/concerns/sends_blob.rb b/app/controllers/concerns/sends_blob.rb
new file mode 100644
index 00000000000..971390d9118
--- /dev/null
+++ b/app/controllers/concerns/sends_blob.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module SendsBlob
+ extend ActiveSupport::Concern
+
+ included do
+ include BlobHelper
+ include SendFileUpload
+ end
+
+ def send_blob(blob, params = {})
+ if blob
+ headers['X-Content-Type-Options'] = 'nosniff'
+
+ return if cached_blob?(blob)
+
+ if blob.stored_externally?
+ send_lfs_object(blob)
+ else
+ send_git_blob(repository, blob, params)
+ end
+ else
+ render_404
+ end
+ end
+
+ private
+
+ def cached_blob?(blob)
+ stale = stale?(etag: blob.id) # The #stale? method sets cache headers.
+
+ # Because we are opinionated we set the cache headers ourselves.
+ response.cache_control[:public] = project.public?
+
+ response.cache_control[:max_age] =
+ if @ref && @commit && @ref == @commit.id # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # This is a link to a commit by its commit SHA. That means that the blob
+ # is immutable. The only reason to invalidate the cache is if the commit
+ # was deleted or if the user lost access to the repository.
+ Blob::CACHE_TIME_IMMUTABLE
+ else
+ # A branch or tag points at this blob. That means that the expected blob
+ # value may change over time.
+ Blob::CACHE_TIME
+ end
+
+ response.etag = blob.id
+ !stale
+ end
+
+ def send_lfs_object(blob)
+ lfs_object = find_lfs_object(blob)
+
+ if lfs_object && lfs_object.project_allowed_access?(project)
+ send_upload(lfs_object.file, attachment: blob.name)
+ else
+ render_404
+ end
+ end
+
+ def find_lfs_object(blob)
+ lfs_object = LfsObject.find_by_oid(blob.lfs_oid)
+ if lfs_object && lfs_object.file.exists?
+ lfs_object
+ else
+ nil
+ end
+ end
+end
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 53fdc5843b5..878c82cd183 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -1,24 +1,16 @@
class Projects::AvatarsController < Projects::ApplicationController
- include BlobHelper
+ include SendsBlob
before_action :authorize_admin_project!, only: [:destroy]
def show
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git)
- if @blob
- headers['X-Content-Type-Options'] = 'nosniff'
- return if cached_blob?
-
- send_git_blob @repository, @blob
- else
- render_404
- end
+ send_blob(@blob)
end
def destroy
@project.remove_avatar!
-
@project.save
redirect_to edit_project_path(@project, anchor: 'js-general-project-settings'), status: :found
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 1cba0011304..91cf35bc70b 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -1,8 +1,7 @@
# Controller for viewing a file's raw
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
- include BlobHelper
- include SendFileUpload
+ include SendsBlob
before_action :require_non_empty_project
before_action :assign_ref_vars
@@ -10,39 +9,7 @@ class Projects::RawController < Projects::ApplicationController
def show
@blob = @repository.blob_at(@commit.id, @path)
- if @blob
- headers['X-Content-Type-Options'] = 'nosniff'
- return if cached_blob?
-
- if @blob.stored_externally?
- send_lfs_object
- else
- send_git_blob @repository, @blob, inline: (params[:inline] != 'false')
- end
- else
- render_404
- end
- end
-
- private
-
- def send_lfs_object
- lfs_object = find_lfs_object
-
- if lfs_object && lfs_object.project_allowed_access?(@project)
- send_upload(lfs_object.file, attachment: @blob.name)
- else
- render_404
- end
- end
-
- def find_lfs_object
- lfs_object = LfsObject.find_by_oid(@blob.lfs_oid)
- if lfs_object && lfs_object.file.exists?
- lfs_object
- else
- nil
- end
+ send_blob(@blob, inline: (params[:inline] != 'false'))
end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index b61cbd5418a..00ebafd177b 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -157,28 +157,6 @@ module BlobHelper
end
end
- def cached_blob?
- stale = stale?(etag: @blob.id) # The #stale? method sets cache headers.
-
- # Because we are opionated we set the cache headers ourselves.
- response.cache_control[:public] = @project.public?
-
- response.cache_control[:max_age] =
- if @ref && @commit && @ref == @commit.id
- # This is a link to a commit by its commit SHA. That means that the blob
- # is immutable. The only reason to invalidate the cache is if the commit
- # was deleted or if the user lost access to the repository.
- Blob::CACHE_TIME_IMMUTABLE
- else
- # A branch or tag points at this blob. That means that the expected blob
- # value may change over time.
- Blob::CACHE_TIME
- end
-
- response.etag = @blob.id
- !stale
- end
-
def licenses_for_select
return @licenses_for_select if defined?(@licenses_for_select)
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index 79844c9210a..d49d4895d89 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -2,7 +2,6 @@
class DiffFileEntity < Grape::Entity
include RequestAwareEntity
- include BlobHelper
include CommitsHelper
include DiffHelper
include SubmoduleHelper
diff --git a/changelogs/unreleased/fj-47229-fix-logo-lfs-tracked.yml b/changelogs/unreleased/fj-47229-fix-logo-lfs-tracked.yml
new file mode 100644
index 00000000000..ed2af81f779
--- /dev/null
+++ b/changelogs/unreleased/fj-47229-fix-logo-lfs-tracked.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed bug when the project logo file is stored in LFS
+merge_request: 20948
+author:
+type: fixed
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index 17c9a61f339..14059cff74c 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -1,24 +1,55 @@
require 'spec_helper'
describe Projects::AvatarsController do
- let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
- let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
before do
- sign_in(user)
- project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
- it 'GET #show' do
- get :show, namespace_id: project.namespace.id, project_id: project.id
+ describe 'GET #show' do
+ subject { get :show, namespace_id: project.namespace, project_id: project }
- expect(response).to have_gitlab_http_status(404)
+ context 'when repository has no avatar' do
+ it 'shows 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when repository has an avatar' do
+ before do
+ allow(project).to receive(:avatar_in_git).and_return(filepath)
+ end
+
+ context 'when the avatar is stored in the repository' do
+ let(:filepath) { 'files/images/logo-white.png' }
+
+ it 'sends the avatar' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Type']).to eq('image/png')
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ end
+ end
+
+ context 'when the avatar is stored in lfs' do
+ it_behaves_like 'repository lfs file load' do
+ let(:filename) { 'lfs_object.iso' }
+ let(:filepath) { "files/lfs/#{filename}" }
+ end
+ end
+ end
end
- it 'removes avatar from DB by calling destroy' do
- delete :destroy, namespace_id: project.namespace.id, project_id: project.id
- expect(project.avatar.present?).to be_falsey
- expect(project).to be_valid
+ describe 'DELETE #destroy' do
+ it 'removes avatar from DB by calling destroy' do
+ delete :destroy, namespace_id: project.namespace.id, project_id: project.id
+
+ expect(project.avatar.present?).to be_falsey
+ expect(project).to be_valid
+ end
end
end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index c3468536ae1..6b658bf5295 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -1,14 +1,21 @@
require 'spec_helper'
describe Projects::RawController do
- let(:public_project) { create(:project, :public, :repository) }
+ let(:project) { create(:project, :public, :repository) }
+
+ describe 'GET #show' do
+ subject do
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: filepath)
+ end
- describe '#show' do
context 'regular filename' do
- let(:id) { 'master/README.md' }
+ let(:filepath) { 'master/README.md' }
it 'delivers ASCII file' do
- get_show(public_project, id)
+ subject
expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
@@ -19,10 +26,10 @@ describe Projects::RawController do
end
context 'image header' do
- let(:id) { 'master/files/images/6049019_460s.jpg' }
+ let(:filepath) { 'master/files/images/6049019_460s.jpg' }
it 'sets image content type header' do
- get_show(public_project, id)
+ subject
expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Type']).to eq('image/jpeg')
@@ -30,85 +37,9 @@ describe Projects::RawController do
end
end
- context 'lfs object' do
- let(:id) { 'be93687/files/lfs/lfs_object.iso' }
- let!(:lfs_object) { create(:lfs_object, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
-
- context 'when lfs is enabled' do
- before do
- allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true)
- end
-
- context 'when project has access' do
- before do
- public_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 }
- end
-
- it 'serves the file' do
- expect(controller).to receive(:send_file).with("#{LfsObjectUploader.root}/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: 'lfs_object.iso', disposition: 'attachment')
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(200)
- end
-
- context 'and lfs uses object storage' do
- let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
-
- before do
- stub_lfs_object_storage
- lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
- end
-
- it 'responds with redirect to file' do
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(302)
- expect(response.location).to include(lfs_object.reload.file.path)
- end
-
- it 'sets content disposition' do
- get_show(public_project, id)
-
- file_uri = URI.parse(response.location)
- params = CGI.parse(file_uri.query)
-
- expect(params["response-content-disposition"].first).to eq 'attachment;filename="lfs_object.iso"'
- end
- end
- end
-
- context 'when project does not have access' do
- it 'does not serve the file' do
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
- end
-
- context 'when lfs is not enabled' do
- before do
- allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(false)
- end
-
- it 'delivers ASCII file' do
- get_show(public_project, id)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition'])
- .to eq('inline')
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- end
- end
+ it_behaves_like 'repository lfs file load' do
+ let(:filename) { 'lfs_object.iso' }
+ let(:filepath) { "be93687/files/lfs/#{filename}" }
end
end
-
- def get_show(project, id)
- get(:show, namespace_id: project.namespace.to_param,
- project_id: project,
- id: id)
- end
end
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
new file mode 100644
index 00000000000..a3d31e26498
--- /dev/null
+++ b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+# Shared examples for controllers that load and send files from the git repository
+# (like Projects::RawController or Projects::AvatarsController)
+
+# These examples requires the following variables:
+# - `project`
+# - `filename`: filename of the file
+# - `filepath`: path of the file (contains filename)
+# - `subject`: the request to be made to the controller. Example:
+# subject { get :show, namespace_id: project.namespace, project_id: project }
+shared_examples 'repository lfs file load' do
+ context 'when file is stored in lfs' do
+ let(:lfs_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
+ let(:lfs_size) { '1575078' }
+ let!(:lfs_object) { create(:lfs_object, oid: lfs_oid, size: lfs_size) }
+
+ context 'when lfs is enabled' do
+ before do
+ allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true)
+ end
+
+ context 'when project has access' 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 }
+ end
+
+ it 'serves the file' do
+ expect(controller).to receive(:send_file).with("#{LfsObjectUploader.root}/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: filename, disposition: 'attachment')
+
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ context 'and lfs uses object storage' do
+ let(:lfs_object) { create(:lfs_object, :with_file, oid: lfs_oid, size: lfs_size) }
+
+ before do
+ stub_lfs_object_storage
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+ end
+
+ it 'responds with redirect to file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(response.location).to include(lfs_object.reload.file.path)
+ end
+
+ it 'sets content disposition' do
+ subject
+
+ file_uri = URI.parse(response.location)
+ params = CGI.parse(file_uri.query)
+
+ expect(params["response-content-disposition"].first).to eq "attachment;filename=\"#{filename}\""
+ end
+ end
+ end
+
+ context 'when project does not have access' do
+ it 'does not serve the file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ context 'when lfs is not enabled' do
+ before do
+ allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(false)
+ end
+
+ it 'delivers ASCII file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
+ expect(response.header['Content-Disposition'])
+ .to eq('inline')
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ end
+ end
+ end
+end