Commit 23f8f6f8 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'ce-to-ee-2018-05-01' into 'master'

CE upstream - 2018-05-01 00:27 UTC

See merge request gitlab-org/gitlab-ee!5524
parents 13dba821 38ecf364
......@@ -86,7 +86,7 @@ export default {
v-html="resolveSvg"
></span>
</span>
<span class=".line-resolve-text">
<span class="line-resolve-text">
{{ resolvedDiscussionCount }}/{{ discussionCount }} {{ countText }} resolved
</span>
</div>
......
import $ from 'jquery';
import _ from 'underscore';
function isValidProjectId(id) {
return id > 0;
......@@ -43,7 +44,7 @@ class SidebarMoveIssue {
renderRow: project => `
<li>
<a href="#" class="js-move-issue-dropdown-item">
${project.name_with_namespace}
${_.escape(project.name_with_namespace)}
</a>
</li>
`,
......
......@@ -772,7 +772,3 @@ ul.notes {
height: auto;
}
}
.line-resolve-text {
vertical-align: middle;
}
module Projects
class UpdatePagesService < BaseService
InvaildStateError = Class.new(StandardError)
InvalidStateError = Class.new(StandardError)
FailedToExtractError = Class.new(StandardError)
BLOCK_SIZE = 32.kilobytes
......@@ -21,8 +21,8 @@ module Projects
@status.enqueue!
@status.run!
raise InvaildStateError, 'missing pages artifacts' unless build.artifacts?
raise InvaildStateError, 'pages are outdated' unless latest?
raise InvalidStateError, 'missing pages artifacts' unless build.artifacts?
raise InvalidStateError, 'pages are outdated' unless latest?
# Create temporary directory in which we will extract the artifacts
FileUtils.mkdir_p(tmp_path)
......@@ -31,16 +31,16 @@ module Projects
# Check if we did extract public directory
archive_public_path = File.join(archive_path, 'public')
raise InvaildStateError, 'pages miss the public folder' unless Dir.exist?(archive_public_path)
raise InvaildStateError, 'pages are outdated' unless latest?
raise InvalidStateError, 'pages miss the public folder' unless Dir.exist?(archive_public_path)
raise InvalidStateError, 'pages are outdated' unless latest?
deploy_page!(archive_public_path)
success
end
rescue InvaildStateError => e
rescue InvalidStateError => e
error(e.message)
rescue => e
error(e.message, false)
error(e.message)
raise e
end
......@@ -48,17 +48,15 @@ module Projects
def success
@status.success
delete_artifact!
super
end
def error(message, allow_delete_artifact = true)
def error(message)
register_failure
log_error("Projects::UpdatePagesService: #{message}")
@status.allow_failure = !latest?
@status.description = message
@status.drop(:script_failure)
delete_artifact! if allow_delete_artifact
super
end
......@@ -77,18 +75,18 @@ module Projects
if artifacts.ends_with?('.zip')
extract_zip_archive!(temp_path)
else
raise InvaildStateError, 'unsupported artifacts format'
raise InvalidStateError, 'unsupported artifacts format'
end
end
def extract_zip_archive!(temp_path)
raise InvaildStateError, 'missing artifacts metadata' unless build.artifacts_metadata?
raise InvalidStateError, 'missing artifacts metadata' unless build.artifacts_metadata?
# Calculate page size after extract
public_entry = build.artifacts_metadata_entry(SITE_PATH, recursive: true)
if public_entry.total_size > max_size
raise InvaildStateError, "artifacts for pages are too large: #{public_entry.total_size}"
raise InvalidStateError, "artifacts for pages are too large: #{public_entry.total_size}"
end
# Requires UnZip at least 6.00 Info-ZIP.
......@@ -162,11 +160,6 @@ module Projects
build.artifacts_file.path
end
def delete_artifact!
build.reload # Reload stable object to prevent erase artifacts with old state
build.erase_artifacts! unless build.has_expiring_artifacts?
end
def latest_sha
project.commit(build.ref).try(:sha).to_s
ensure
......
......@@ -20,11 +20,12 @@ class RepositoryArchiveCleanUpService
private
def clean_up_old_archives
run(%W(find #{path} -not -path #{path} -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -maxdepth 2 -mmin +#{mmin} -delete))
run(%W(find #{path} -mindepth 1 -maxdepth 3 -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -mmin +#{mmin} -delete))
end
def clean_up_empty_directories
run(%W(find #{path} -not -path #{path} -type d -empty -name \*.git -maxdepth 1 -delete))
run(%W(find #{path} -mindepth 2 -maxdepth 2 -type d -empty -delete))
run(%W(find #{path} -mindepth 1 -maxdepth 1 -type d -empty -delete))
end
def run(cmd)
......
......@@ -8,18 +8,17 @@
%li{ class: "branch-item js-branch-#{branch.name}" }
.branch-info
.branch-title
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name' do
= sprite_icon('fork', size: 12)
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8' do
= branch.name
&nbsp;
- if branch.name == @repository.root_ref
%span.label.label-primary default
%span.label.label-primary.prepend-left-5 default
- elsif merged
%span.label.label-info.has-tooltip{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
%span.label.label-info.has-tooltip.prepend-left-5{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
= s_('Branches|merged')
- if protected_branch?(@project, branch)
%span.label.label-success
%span.label.label-success.prepend-left-5
= s_('Branches|protected')
- if @project.mirror_ever_updated_successfully? && @repository.diverged_from_upstream?(branch.name)
......
---
title: Don't automatically remove artifacts for pages jobs after pages:deploy has
run
merge_request: 18628
author:
type: fixed
---
title: Fix redirection error for applications using OpenID
merge_request: 18599
author:
type: fixed
---
title: Fixed inconsistent protected branch pill baseline
merge_request:
author:
type: fixed
---
title: Revert discussion counter height
merge_request: 18656
author: George Tsiolis
type: changed
---
title: Serve archive requests with the correct file in all cases
merge_request:
author:
type: security
---
title: Sanitizes user name to avoid XSS attacks
merge_request:
author:
type: security
......@@ -104,5 +104,5 @@ Doorkeeper.configure do
# set to true if you want this to be allowed
# wildcard_redirect_uri false
base_controller 'ApplicationController'
base_controller '::Gitlab::BaseDoorkeeperController'
end
......@@ -116,6 +116,14 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i
#### Merge Requests
- [Merge Requests](user/project/merge_requests/index.md)
- [Work In Progress "WIP" Merge Requests](user/project/merge_requests/work_in_progress_merge_requests.md)
- [Merge Request discussion resolution](user/discussions/index.md#moving-a-single-discussion-to-a-new-issue): Resolve discussions, move discussions in a merge request to an issue, only allow merge requests to be merged if all discussions are resolved.
- [Checkout merge requests locally](user/project/merge_requests/index.md#checkout-merge-requests-locally)
- [Cherry-pick](user/project/merge_requests/cherry_pick_changes.md)
#### Merge Requests
- [Merge Requests](user/project/merge_requests/index.md)
- [Work In Progress "WIP" Merge Requests](user/project/merge_requests/work_in_progress_merge_requests.md)
- [Merge Request discussion resolution](user/discussions/index.md#moving-a-single-discussion-to-a-new-issue): Resolve discussions, move discussions in a merge request to an issue, only allow merge requests to be merged if all discussions are resolved.
......
# This is a base controller for doorkeeper.
# It adds the `can?` helper used in the views.
module Gitlab
class BaseDoorkeeperController < ActionController::Base
include Gitlab::Allowable
helper_method :can?
end
end
......@@ -12,7 +12,7 @@ module Gitlab
# class.
#
class RemoteRepository
attr_reader :path, :relative_path, :gitaly_repository
attr_reader :relative_path, :gitaly_repository
def initialize(repository)
@relative_path = repository.relative_path
......@@ -21,7 +21,6 @@ module Gitlab
# These instance variables will not be available in gitaly-ruby, where
# we have no disk access to this repository.
@repository = repository
@path = repository.path
end
def empty?
......@@ -69,6 +68,10 @@ module Gitlab
env
end
def path
@repository.path
end
private
# Must return an object that responds to 'address' and 'storage'.
......
......@@ -391,18 +391,6 @@ module Gitlab
nil
end
def archive_prefix(ref, sha, append_sha:)
append_sha = (ref != sha) if append_sha.nil?
project_name = self.name.chomp('.git')
formatted_ref = ref.tr('/', '-')
prefix_segments = [project_name, formatted_ref]
prefix_segments << sha if append_sha
prefix_segments.join('-')
end
def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:)
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
......@@ -413,12 +401,44 @@ module Gitlab
{
'RepoPath' => path,
'ArchivePrefix' => prefix,
'ArchivePath' => archive_file_path(prefix, storage_path, format),
'ArchivePath' => archive_file_path(storage_path, commit.id, prefix, format),
'CommitId' => commit.id
}
end
def archive_file_path(name, storage_path, format = "tar.gz")
# This is both the filename of the archive (missing the extension) and the
# name of the top-level member of the archive under which all files go
#
# FIXME: The generated prefix is incorrect for projects with hashed
# storage enabled
def archive_prefix(ref, sha, append_sha:)
append_sha = (ref != sha) if append_sha.nil?
project_name = self.name.chomp('.git')
formatted_ref = ref.tr('/', '-')
prefix_segments = [project_name, formatted_ref]
prefix_segments << sha if append_sha
prefix_segments.join('-')
end
private :archive_prefix
# The full path on disk where the archive should be stored. This is used
# to cache the archive between requests.
#
# The path is a global namespace, so needs to be globally unique. This is
# achieved by including `gl_repository` in the path.
#
# Archives relating to a particular ref when the SHA is not present in the
# filename must be invalidated when the ref is updated to point to a new
# SHA. This is achieved by including the SHA in the path.
#
# As this is a full path on disk, it is not "cloud native". This should
# be resolved by either removing the cache, or moving the implementation
# into Gitaly and removing the ArchivePath parameter from the git-archive
# senddata response.
def archive_file_path(storage_path, sha, name, format = "tar.gz")
# Build file path
return nil unless name
......@@ -436,8 +456,9 @@ module Gitlab
end
file_name = "#{name}.#{extension}"
File.join(storage_path, self.name, file_name)
File.join(storage_path, self.gl_repository, sha, file_name)
end
private :archive_file_path
# Return repo size in megabytes
def size
......@@ -1183,6 +1204,8 @@ module Gitlab
if is_enabled
gitaly_fetch_ref(source_repository, source_ref: source_ref, target_ref: target_ref)
else
# When removing this code, also remove source_repository#path
# to remove deprecated method calls
local_fetch_ref(source_repository.path, source_ref: source_ref, target_ref: target_ref)
end
end
......
......@@ -142,7 +142,7 @@ module Gitlab
:repository_service,
:is_rebase_in_progress,
request,
timeout: GitalyClient.default_timeout
timeout: GitalyClient.fast_timeout
)
response.in_progress
......@@ -159,7 +159,7 @@ module Gitlab
:repository_service,
:is_squash_in_progress,
request,
timeout: GitalyClient.default_timeout
timeout: GitalyClient.fast_timeout
)
response.in_progress
......
......@@ -138,7 +138,7 @@ const RESPONSE_MAP = {
},
{
id: 20,
name_with_namespace: 'foo / bar',
name_with_namespace: '<img src=x onerror=alert(document.domain)> foo / bar',
},
],
},
......
......@@ -69,6 +69,15 @@ describe('SidebarMoveIssue', function () {
expect($.fn.glDropdown).toHaveBeenCalled();
});
it('escapes html from project name', (done) => {
this.$toggleButton.dropdown('toggle');
setTimeout(() => {
expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; foo / bar');
done();
});
});
});
describe('onConfirmClicked', () => {
......
......@@ -234,59 +234,72 @@ describe Gitlab::Git::Repository, seed_helper: true do
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
end
shared_examples 'archive check' do |extenstion|
it { expect(metadata['ArchivePath']).to match(%r{tmp/gitlab-git-test.git/gitlab-git-test-master-#{SeedRepo::LastCommit::ID}}) }
it { expect(metadata['ArchivePath']).to end_with extenstion }
end
describe '#archive_metadata' do
let(:storage_path) { '/tmp' }
let(:cache_key) { File.join(repository.gl_repository, SeedRepo::LastCommit::ID) }
describe '#archive_prefix' do
let(:project_name) { 'project-name'}
let(:append_sha) { true }
let(:ref) { 'master' }
let(:format) { nil }
before do
expect(repository).to receive(:name).once.and_return(project_name)
end
let(:expected_extension) { 'tar.gz' }
let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" }
let(:expected_path) { File.join(storage_path, cache_key, expected_filename) }
let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" }
it 'returns parameterised string for a ref containing slashes' do
prefix = repository.archive_prefix('test/branch', 'SHA', append_sha: nil)
subject(:metadata) { repository.archive_metadata(ref, storage_path, format, append_sha: append_sha) }
expect(prefix).to eq("#{project_name}-test-branch-SHA")
it 'sets RepoPath to the repository path' do
expect(metadata['RepoPath']).to eq(repository.path)
end
it 'returns correct string for a ref containing dots' do
prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: nil)
expect(prefix).to eq("#{project_name}-test.branch-SHA")
it 'sets CommitId to the commit SHA' do
expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID)
end
it 'returns string with sha when append_sha is false' do
prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: false)
expect(prefix).to eq("#{project_name}-test.branch")
end
it 'sets ArchivePrefix to the expected prefix' do
expect(metadata['ArchivePrefix']).to eq(expected_prefix)
end
describe '#archive' do
let(:metadata) { repository.archive_metadata('master', '/tmp', append_sha: true) }
it 'sets ArchivePath to the expected globally-unique path' do
# This is really important from a security perspective. Think carefully
# before changing it: https://gitlab.com/gitlab-org/gitlab-ce/issues/45689
expect(expected_path).to include(File.join(repository.gl_repository, SeedRepo::LastCommit::ID))
it_should_behave_like 'archive check', '.tar.gz'
expect(metadata['ArchivePath']).to eq(expected_path)
end
describe '#archive_zip' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip', append_sha: true) }
context 'append_sha varies archive path and filename' do
where(:append_sha, :ref, :expected_prefix) do
sha = SeedRepo::LastCommit::ID
it_should_behave_like 'archive check', '.zip'
true | 'master' | "gitlab-git-test-master-#{sha}"
true | sha | "gitlab-git-test-#{sha}-#{sha}"
false | 'master' | "gitlab-git-test-master"
false | sha | "gitlab-git-test-#{sha}"
nil | 'master' | "gitlab-git-test-master-#{sha}"
nil | sha | "gitlab-git-test-#{sha}"
end
describe '#archive_bz2' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2', append_sha: true) }
it_should_behave_like 'archive check', '.tar.bz2'
with_them do
it { expect(metadata['ArchivePrefix']).to eq(expected_prefix) }
it { expect(metadata['ArchivePath']).to eq(expected_path) }
end
end
describe '#archive_fallback' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup', append_sha: true) }
context 'format varies archive path and filename' do
where(:format, :expected_extension) do
nil | 'tar.gz'
'madeup' | 'tar.gz'
'tbz2' | 'tar.bz2'
'zip' | 'zip'
end
it_should_behave_like 'archive check', '.tar.gz'
with_them do
it { expect(metadata['ArchivePrefix']).to eq(expected_prefix) }
it { expect(metadata['ArchivePath']).to eq(expected_path) }
end
end
end
describe '#size' do
......
......@@ -153,4 +153,13 @@ describe 'OpenID Connect requests' do
end
end
end
context 'OpenID configuration information' do
it 'correctly returns the configuration' do
get '/.well-known/openid-configuration'
expect(response).to have_gitlab_http_status(200)
expect(json_response).to have_key('issuer')
end
end
end
......@@ -29,28 +29,13 @@ describe Projects::UpdatePagesService do
end
describe 'pages artifacts' do
context 'with expiry date' do
before do
build.artifacts_expire_in = "2 days"
build.save!
end
it "doesn't delete artifacts" do
it "doesn't delete artifacts after deploying" do
expect(execute).to eq(:success)
expect(build.reload.artifacts?).to eq(true)
end
end
context 'without expiry date' do
it "does delete artifacts" do
expect(execute).to eq(:success)
expect(build.reload.artifacts?).to eq(false)
end
end
end
it 'succeeds' do
expect(project.pages_deployed?).to be_falsey
expect(execute).to eq(:success)
......@@ -100,28 +85,13 @@ describe Projects::UpdatePagesService do
end
describe 'pages artifacts' do
context 'with expiry date' do
before do
build.artifacts_expire_in = "2 days"
build.save!
end
it "doesn't delete artifacts" do
it "doesn't delete artifacts after deploying" do
expect(execute).to eq(:success)
expect(build.artifacts?).to eq(true)
end
end
context 'without expiry date' do
it "does delete artifacts" do
expect(execute).to eq(:success)
expect(build.reload.artifacts?).to eq(false)
end
end
end
it 'succeeds' do
expect(project.pages_deployed?).to be_falsey
expect(execute).to eq(:success)
......@@ -171,13 +141,12 @@ describe Projects::UpdatePagesService do
build.reload
expect(deploy_status).to be_failed
expect(build.artifacts?).to be_truthy
end
end
context 'when failed to extract zip artifacts' do
before do
allow_any_instance_of(described_class)
expect_any_instance_of(described_class)
.to receive(:extract_zip_archive!)
.and_raise(Projects::UpdatePagesService::FailedToExtractError)
end
......@@ -188,21 +157,19 @@ describe Projects::UpdatePagesService do
build.reload
expect(deploy_status).to be_failed
expect(build.artifacts?).to be_truthy
end
end
context 'when missing artifacts metadata' do
before do
allow(build).to receive(:artifacts_metadata?).and_return(false)
expect(build).to receive(:artifacts_metadata?).and_return(false)
end
it 'does not raise an error and remove artifacts as failed job' do
it 'does not raise an error as failed job' do
execute
build.reload
expect(deploy_status).to be_failed
expect(build.artifacts?).to be_falsey
end
end
end
......
require 'spec_helper'
describe RepositoryArchiveCleanUpService do
describe '#execute' do
subject(:service) { described_class.new }
describe '#execute (new archive locations)' do
let(:sha) { "0" * 40 }
it 'removes outdated archives and directories in a new-style path' do
in_directory_with_files("project-999/#{sha}", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
service.execute
files.each { |filename| expect(File.exist?(filename)).to be_falsy }
expect(File.directory?(dirname)).to be_falsy
expect(File.directory?(File.dirname(dirname))).to be_falsy
end
end
it 'does not remove directories when they contain outdated non-archives' do
in_directory_with_files("project-999/#{sha}", %w[tar conf rb], 2.hours) do |dirname, files|
service.execute
expect(File.directory?(dirname)).to be_truthy
end
end
it 'does not remove in-date archives in a new-style path' do
in_directory_with_files("project-999/#{sha}", %w[tar tar.bz2 tar.gz zip], 1.hour) do |dirname, files|
service.execute
files.each { |filename| expect(File.exist?(filename)).to be_truthy }
end
end
end
describe '#execute (legacy archive locations)' do
context 'when the downloads directory does not exist' do
it 'does not remove any archives' do
path = '/invalid/path/'
stub_repository_downloads_path(path)
allow(File).to receive(:directory?).and_call_original
expect(File).to receive(:directory?).with(path).and_return(false)
expect(service).not_to receive(:clean_up_old_archives)
expect(service).not_to receive(:clean_up_empty_directories)
......@@ -19,7 +51,7 @@ describe RepositoryArchiveCleanUpService do
context 'when the downloads directory exists' do
shared_examples 'invalid archive files' do |dirname, extensions, mtime|
it 'does not remove files and directoy' do
it 'does not remove files and directory' do
in_directory_with_files(dirname, extensions, mtime) do |dir, files|
service.execute
......@@ -43,7 +75,7 @@ describe RepositoryArchiveCleanUpService do
end
context 'with files older than 2 hours inside invalid directories' do
it_behaves_like 'invalid archive files', 'john_doe/sample.git', %w[conf rb tar tar.gz], 2.hours
it_behaves_like 'invalid archive files', 'john/doe/sample.git', %w[conf rb tar tar.gz], 2.hours
end
context 'with files newer than 2 hours that matches valid archive extensions' do
......@@ -58,6 +90,7 @@ describe RepositoryArchiveCleanUpService do
it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb tar tar.gz], 1.hour
end
end
end
def in_directory_with_files(dirname, extensions, mtime)
Dir.mktmpdir do |tmpdir|
......@@ -77,5 +110,4 @@ describe RepositoryArchiveCleanUpService do
FileUtils.mkdir_p(dir)
FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now - mtime)
end
end
end
......@@ -22,13 +22,11 @@ describe NamespacelessProjectDestroyWorker do
end
end
# Only possible with schema 20180222043024 and lower.
# Project#namespace_id has not null constraint since then
context 'project has no namespace', :migration, schema: 20180222043024 do
let!(:project) do
project = build(:project, namespace_id: nil)
project.save(validate: false)
project
context 'project has no namespace' do
let!(:project) { create(:project) }
before do
allow_any_instance_of(Project).to receive(:namespace).and_return(nil)
end
context 'project not a fork of another project' do
......@@ -61,8 +59,7 @@ describe NamespacelessProjectDestroyWorker do
let!(:parent_project) { create(:project) }
let(:project) do
namespaceless_project = fork_project(parent_project)
namespaceless_project.namespace_id = nil
namespaceless_project.save(validate: false)
namespaceless_project.save
namespaceless_project
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