Commit b08bb9e9 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-08-30

# Conflicts:
#	app/services/users/build_service.rb
#	locale/gitlab.pot

[ci skip]
parents 490d0f94 40935fd7
...@@ -4,7 +4,9 @@ entry. ...@@ -4,7 +4,9 @@ entry.
## 11.2.3 (2018-08-28) ## 11.2.3 (2018-08-28)
- No changes. ### Fixed (1 change)
- Fixed cache invalidation issue with diff lines from 11.2.2.
## 11.2.2 (2018-08-27) ## 11.2.2 (2018-08-27)
...@@ -271,7 +273,9 @@ entry. ...@@ -271,7 +273,9 @@ entry.
## 11.1.6 (2018-08-28) ## 11.1.6 (2018-08-28)
- No changes. ### Fixed (1 change)
- Fixed cache invalidation issue with diff lines from 11.2.2.
## 11.1.5 (2018-08-27) ## 11.1.5 (2018-08-27)
......
# 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
class Projects::AvatarsController < Projects::ApplicationController class Projects::AvatarsController < Projects::ApplicationController
include BlobHelper include SendsBlob
before_action :authorize_admin_project!, only: [:destroy] before_action :authorize_admin_project!, only: [:destroy]
def show def show
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git) @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_blob(@blob)
send_git_blob @repository, @blob
else
render_404
end
end end
def destroy def destroy
@project.remove_avatar! @project.remove_avatar!
@project.save @project.save
redirect_to edit_project_path(@project, anchor: 'js-general-project-settings'), status: :found redirect_to edit_project_path(@project, anchor: 'js-general-project-settings'), status: :found
......
# Controller for viewing a file's raw # Controller for viewing a file's raw
class Projects::RawController < Projects::ApplicationController class Projects::RawController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include BlobHelper include SendsBlob
include SendFileUpload
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
...@@ -10,39 +9,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -10,39 +9,7 @@ class Projects::RawController < Projects::ApplicationController
def show def show
@blob = @repository.blob_at(@commit.id, @path) @blob = @repository.blob_at(@commit.id, @path)
if @blob
headers['X-Content-Type-Options'] = 'nosniff'
return if cached_blob? send_blob(@blob, inline: (params[:inline] != 'false'))
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
end end
end end
...@@ -157,28 +157,6 @@ module BlobHelper ...@@ -157,28 +157,6 @@ module BlobHelper
end end
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 def licenses_for_select
return @licenses_for_select if defined?(@licenses_for_select) return @licenses_for_select if defined?(@licenses_for_select)
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
class DiffFileEntity < Grape::Entity class DiffFileEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
include BlobHelper
include CommitsHelper include CommitsHelper
include DiffHelper include DiffHelper
include SubmoduleHelper include SubmoduleHelper
......
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
module Users module Users
class BuildService < BaseService class BuildService < BaseService
<<<<<<< HEAD
prepend ::EE::Users::BuildService prepend ::EE::Users::BuildService
=======
>>>>>>> upstream/master
delegate :user_default_internal_regex_enabled?, delegate :user_default_internal_regex_enabled?,
:user_default_internal_regex_instance, :user_default_internal_regex_instance,
to: :'Gitlab::CurrentSettings.current_application_settings' to: :'Gitlab::CurrentSettings.current_application_settings'
......
---
title: Fixed bug when the project logo file is stored in LFS
merge_request: 20948
author:
type: fixed
...@@ -5976,9 +5976,12 @@ msgid_plural "Refreshing in %d seconds to show the updated status..." ...@@ -5976,9 +5976,12 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
<<<<<<< HEAD
msgid "Regenerate key" msgid "Regenerate key"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Regex pattern" msgid "Regex pattern"
msgstr "" msgstr ""
...@@ -7470,6 +7473,7 @@ msgstr "" ...@@ -7470,6 +7473,7 @@ msgstr ""
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}." msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect." msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr "" msgstr ""
...@@ -7479,6 +7483,8 @@ msgstr "" ...@@ -7479,6 +7483,8 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}." msgid "To connect an SVN repository, check out %{svn_link}."
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "To define internal users, first enable new users set to external" msgid "To define internal users, first enable new users set to external"
msgstr "" msgstr ""
......
require 'spec_helper' require 'spec_helper'
describe Projects::AvatarsController do describe Projects::AvatarsController do
let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do before do
sign_in(user)
project.add_maintainer(user)
controller.instance_variable_set(:@project, project) controller.instance_variable_set(:@project, project)
end end
it 'GET #show' do describe 'GET #show' do
get :show, namespace_id: project.namespace.id, project_id: project.id subject { get :show, namespace_id: project.namespace, project_id: project }
context 'when repository has no avatar' do
it 'shows 404' do
subject
expect(response).to have_gitlab_http_status(404) expect(response).to have_gitlab_http_status(404)
end 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
describe 'DELETE #destroy' do
it 'removes avatar from DB by calling destroy' do it 'removes avatar from DB by calling destroy' do
delete :destroy, namespace_id: project.namespace.id, project_id: project.id delete :destroy, namespace_id: project.namespace.id, project_id: project.id
expect(project.avatar.present?).to be_falsey expect(project.avatar.present?).to be_falsey
expect(project).to be_valid expect(project).to be_valid
end end
end
end end
require 'spec_helper' require 'spec_helper'
describe Projects::RawController do 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 context 'regular filename' do
let(:id) { 'master/README.md' } let(:filepath) { 'master/README.md' }
it 'delivers ASCII file' do it 'delivers ASCII file' do
get_show(public_project, id) subject
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
...@@ -19,10 +26,10 @@ describe Projects::RawController do ...@@ -19,10 +26,10 @@ describe Projects::RawController do
end end
context 'image header' do 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 it 'sets image content type header' do
get_show(public_project, id) subject
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Type']).to eq('image/jpeg') expect(response.header['Content-Type']).to eq('image/jpeg')
...@@ -30,85 +37,9 @@ describe Projects::RawController do ...@@ -30,85 +37,9 @@ describe Projects::RawController do
end end
end end
context 'lfs object' do it_behaves_like 'repository lfs file load' do
let(:id) { 'be93687/files/lfs/lfs_object.iso' } let(:filename) { 'lfs_object.iso' }
let!(:lfs_object) { create(:lfs_object, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') } let(:filepath) { "be93687/files/lfs/#{filename}" }
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 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
end
end
def get_show(project, id)
get(:show, namespace_id: project.namespace.to_param,
project_id: project,
id: id)
end end
end end
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment