summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/banzai/filter/autolink_filter_spec.rb20
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb151
-rw-r--r--spec/lib/container_registry/blob_spec.rb52
-rw-r--r--spec/lib/container_registry/tag_spec.rb49
-rw-r--r--spec/lib/gitlab/badge/build_spec.rb2
-rw-r--r--spec/lib/gitlab/bitbucket_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb14
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb3
-rw-r--r--spec/lib/gitlab/git_access_spec.rb319
-rw-r--r--spec/lib/gitlab/import_export/avatar_restorer_spec.rb25
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb27
-rw-r--r--spec/lib/gitlab/import_export/project.json18
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb9
-rw-r--r--spec/lib/gitlab/lfs/lfs_router_spec.rb730
-rw-r--r--spec/lib/gitlab/user_access_spec.rb88
16 files changed, 547 insertions, 969 deletions
diff --git a/spec/lib/banzai/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb
index 84c2ddf444e..dca7f997570 100644
--- a/spec/lib/banzai/filter/autolink_filter_spec.rb
+++ b/spec/lib/banzai/filter/autolink_filter_spec.rb
@@ -15,6 +15,16 @@ describe Banzai::Filter::AutolinkFilter, lib: true do
expect(filter(act).to_html).to eq exp
end
+ context 'when the input contains no links' do
+ it 'does not parse_html back the rinku returned value' do
+ act = HTML::Pipeline.parse('<p>This text contains no links to autolink</p>')
+
+ expect_any_instance_of(described_class).not_to receive(:parse_html)
+
+ filter(act).to_html
+ end
+ end
+
context 'Rinku schemes' do
it 'autolinks http' do
doc = filter("See #{link}")
@@ -58,6 +68,16 @@ describe Banzai::Filter::AutolinkFilter, lib: true do
expect(filter(act).to_html).to eq exp
end
end
+
+ context 'when the input contains link' do
+ it 'does parse_html back the rinku returned value' do
+ act = HTML::Pipeline.parse("<p>See #{link}</p>")
+
+ expect_any_instance_of(described_class).to receive(:parse_html).at_least(:once).and_call_original
+
+ filter(act).to_html
+ end
+ end
end
context 'other schemes' do
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index bcbf409c8b0..d20fd4ab7dd 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -19,15 +19,14 @@ module Ci
expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
stage: "test",
stage_idx: 1,
- except: nil,
name: :rspec,
- only: nil,
commands: "pwd\nrspec",
tag_list: [],
options: {},
allow_failure: false,
when: "on_success",
environment: nil,
+ yaml_variables: []
})
end
@@ -432,11 +431,9 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
- except: nil,
stage: "test",
stage_idx: 1,
name: :rspec,
- only: nil,
commands: "pwd\nrspec",
tag_list: [],
options: {
@@ -446,6 +443,7 @@ module Ci
allow_failure: false,
when: "on_success",
environment: nil,
+ yaml_variables: []
})
end
@@ -461,11 +459,9 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
- except: nil,
stage: "test",
stage_idx: 1,
name: :rspec,
- only: nil,
commands: "pwd\nrspec",
tag_list: [],
options: {
@@ -475,101 +471,126 @@ module Ci
allow_failure: false,
when: "on_success",
environment: nil,
+ yaml_variables: []
})
end
end
describe 'Variables' do
- context 'when global variables are defined' do
- it 'returns global variables' do
- variables = {
- VAR1: 'value1',
- VAR2: 'value2',
- }
+ let(:config_processor) { GitlabCiYamlProcessor.new(YAML.dump(config), path) }
- config = YAML.dump({
+ subject { config_processor.builds.first[:yaml_variables] }
+
+ context 'when global variables are defined' do
+ let(:variables) do
+ { VAR1: 'value1', VAR2: 'value2' }
+ end
+ let(:config) do
+ {
variables: variables,
before_script: ['pwd'],
rspec: { script: 'rspec' }
- })
+ }
+ end
- config_processor = GitlabCiYamlProcessor.new(config, path)
+ it 'returns global variables' do
+ expect(subject).to contain_exactly(
+ { key: :VAR1, value: 'value1', public: true },
+ { key: :VAR2, value: 'value2', public: true }
+ )
+ end
+ end
+
+ context 'when job and global variables are defined' do
+ let(:global_variables) do
+ { VAR1: 'global1', VAR3: 'global3' }
+ end
+ let(:job_variables) do
+ { VAR1: 'value1', VAR2: 'value2' }
+ end
+ let(:config) do
+ {
+ before_script: ['pwd'],
+ variables: global_variables,
+ rspec: { script: 'rspec', variables: job_variables }
+ }
+ end
- expect(config_processor.global_variables).to eq(variables)
+ it 'returns all unique variables' do
+ expect(subject).to contain_exactly(
+ { key: :VAR3, value: 'global3', public: true },
+ { key: :VAR1, value: 'value1', public: true },
+ { key: :VAR2, value: 'value2', public: true }
+ )
end
end
context 'when job variables are defined' do
- context 'when syntax is correct' do
- it 'returns job variables' do
- variables = {
- KEY1: 'value1',
- SOME_KEY_2: 'value2'
- }
+ let(:config) do
+ {
+ before_script: ['pwd'],
+ rspec: { script: 'rspec', variables: variables }
+ }
+ end
+
+ context 'when also global variables are defined' do
- config = YAML.dump(
- { before_script: ['pwd'],
- rspec: {
- variables: variables,
- script: 'rspec' }
- })
+ end
- config_processor = GitlabCiYamlProcessor.new(config, path)
+ context 'when syntax is correct' do
+ let(:variables) do
+ { VAR1: 'value1', VAR2: 'value2' }
+ end
- expect(config_processor.job_variables(:rspec)).to eq variables
+ it 'returns job variables' do
+ expect(subject).to contain_exactly(
+ { key: :VAR1, value: 'value1', public: true },
+ { key: :VAR2, value: 'value2', public: true }
+ )
end
end
context 'when syntax is incorrect' do
context 'when variables defined but invalid' do
- it 'raises error' do
- variables = [:KEY1, 'value1', :KEY2, 'value2']
-
- config = YAML.dump(
- { before_script: ['pwd'],
- rspec: {
- variables: variables,
- script: 'rspec' }
- })
+ let(:variables) do
+ [ :VAR1, 'value1', :VAR2, 'value2' ]
+ end
- expect { GitlabCiYamlProcessor.new(config, path) }
+ it 'raises error' do
+ expect { subject }
.to raise_error(GitlabCiYamlProcessor::ValidationError,
- /job: variables should be a map/)
+ /job: variables should be a map/)
end
end
context 'when variables key defined but value not specified' do
- it 'returns empty array' do
- config = YAML.dump(
- { before_script: ['pwd'],
- rspec: {
- variables: nil,
- script: 'rspec' }
- })
-
- config_processor = GitlabCiYamlProcessor.new(config, path)
+ let(:variables) do
+ nil
+ end
+ it 'returns empty array' do
##
# When variables config is empty, we assume this is a valid
# configuration, see issue #18775
#
- expect(config_processor.job_variables(:rspec))
- .to be_an_instance_of(Array).and be_empty
+ expect(subject).to be_an_instance_of(Array)
+ expect(subject).to be_empty
end
end
end
end
context 'when job variables are not defined' do
- it 'returns empty array' do
- config = YAML.dump({
+ let(:config) do
+ {
before_script: ['pwd'],
rspec: { script: 'rspec' }
- })
-
- config_processor = GitlabCiYamlProcessor.new(config, path)
+ }
+ end
- expect(config_processor.job_variables(:rspec)).to eq []
+ it 'returns empty array' do
+ expect(subject).to be_an_instance_of(Array)
+ expect(subject).to be_empty
end
end
end
@@ -681,11 +702,9 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
- except: nil,
stage: "test",
stage_idx: 1,
name: :rspec,
- only: nil,
commands: "pwd\nrspec",
tag_list: [],
options: {
@@ -701,6 +720,7 @@ module Ci
when: "on_success",
allow_failure: false,
environment: nil,
+ yaml_variables: []
})
end
@@ -819,17 +839,16 @@ module Ci
it "doesn't create jobs that start with dot" do
expect(subject.size).to eq(1)
expect(subject.first).to eq({
- except: nil,
stage: "test",
stage_idx: 1,
name: :normal_job,
- only: nil,
commands: "test",
tag_list: [],
options: {},
when: "on_success",
allow_failure: false,
environment: nil,
+ yaml_variables: []
})
end
end
@@ -865,30 +884,28 @@ module Ci
it "is correctly supported for jobs" do
expect(subject.size).to eq(2)
expect(subject.first).to eq({
- except: nil,
stage: "build",
stage_idx: 0,
name: :job1,
- only: nil,
commands: "execute-script-for-job",
tag_list: [],
options: {},
when: "on_success",
allow_failure: false,
environment: nil,
+ yaml_variables: []
})
expect(subject.second).to eq({
- except: nil,
stage: "build",
stage_idx: 0,
name: :job2,
- only: nil,
commands: "execute-script-for-job",
tag_list: [],
options: {},
when: "on_success",
allow_failure: false,
environment: nil,
+ yaml_variables: []
})
end
end
@@ -1124,7 +1141,7 @@ EOT
config = YAML.dump({ rspec: { script: "test", when: 1 } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure, always or manual")
end
it "returns errors if job artifacts:name is not an a string" do
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index 4d8cb787dde..bbacdc67ebd 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -9,8 +9,9 @@ describe ContainerRegistry::Blob do
'size' => 1000
}
end
+ let(:token) { 'authorization-token' }
- let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
+ let(:registry) { ContainerRegistry::Registry.new('http://example.com', token: token) }
let(:repository) { registry.repository('group/test') }
let(:blob) { repository.blob(config) }
@@ -58,4 +59,53 @@ describe ContainerRegistry::Blob do
it { is_expected.to be_truthy }
end
+
+ context '#data' do
+ let(:data) { '{"key":"value"}' }
+
+ subject { blob.data }
+
+ context 'when locally stored' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
+ to_return(
+ status: 200,
+ headers: { 'Content-Type' => 'application/json' },
+ body: data)
+ end
+
+ it { is_expected.to eq(data) }
+ end
+
+ context 'when externally stored' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
+ with(headers: { 'Authorization' => "bearer #{token}" }).
+ to_return(
+ status: 307,
+ headers: { 'Location' => location })
+ end
+
+ context 'for a valid address' do
+ let(:location) { 'http://external.com/blob/file' }
+
+ before do
+ stub_request(:get, location).
+ with(headers: { 'Authorization' => nil }).
+ to_return(
+ status: 200,
+ headers: { 'Content-Type' => 'application/json' },
+ body: data)
+ end
+
+ it { is_expected.to eq(data) }
+ end
+
+ context 'for invalid file' do
+ let(:location) { 'file:///etc/passwd' }
+
+ it { expect{ subject }.to raise_error(ArgumentError, 'invalid address') }
+ end
+ end
+ end
end
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index c7324c2bf77..c5e31ae82b6 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -77,24 +77,47 @@ describe ContainerRegistry::Tag do
end
context 'config processing' do
- before do
- stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
- with(headers: { 'Accept' => 'application/octet-stream' }).
- to_return(
- status: 200,
- body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
- end
+ shared_examples 'a processable' do
+ context '#config' do
+ subject { tag.config }
- context '#config' do
- subject { tag.config }
+ it { is_expected.not_to be_nil }
+ end
+
+ context '#created_at' do
+ subject { tag.created_at }
- it { is_expected.not_to be_nil }
+ it { is_expected.not_to be_nil }
+ end
end
- context '#created_at' do
- subject { tag.created_at }
+ context 'when locally stored' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+ with(headers: { 'Accept' => 'application/octet-stream' }).
+ to_return(
+ status: 200,
+ body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
+ end
+
+ it_behaves_like 'a processable'
+ end
- it { is_expected.not_to be_nil }
+ context 'when externally stored' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+ with(headers: { 'Accept' => 'application/octet-stream' }).
+ to_return(
+ status: 307,
+ headers: { 'Location' => 'http://external.com/blob/file' })
+
+ stub_request(:get, 'http://external.com/blob/file').
+ to_return(
+ status: 200,
+ body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
+ end
+
+ it_behaves_like 'a processable'
end
end
end
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
index 2034445a197..f3b522a02f5 100644
--- a/spec/lib/gitlab/badge/build_spec.rb
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -113,7 +113,7 @@ describe Gitlab::Badge::Build do
sha: sha,
ref: branch)
- create(:ci_build, pipeline: pipeline)
+ create(:ci_build, pipeline: pipeline, stage: 'notify')
end
def status_node(data, status)
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
index 760d66a1488..7543c29bcc4 100644
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do
context 'project import' do
it 'calls .from_project with no errors' do
project = create(:empty_project)
+ project.import_url = "ssh://git@bitbucket.org/test/test.git"
project.create_or_update_import_data(credentials:
{ user: "git",
password: nil,
bb_session: { bitbucket_access_token: "test",
bitbucket_access_token_secret: "test" } })
- project.import_url = "ssh://git@bitbucket.org/test/test.git"
expect { described_class.from_project(project) }.not_to raise_error
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 0460dcf4658..e883a6eb9c2 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -32,4 +32,18 @@ describe Gitlab::Diff::File, lib: true do
expect(diff_file.too_large?).to eq(false)
end
end
+
+ describe '#collapsed?' do
+ it 'returns true for a file that is quite big' do
+ expect(diff).to receive(:collapsed?).and_return(true)
+
+ expect(diff_file.collapsed?).to eq(true)
+ end
+
+ it 'returns false for a file that is small enough' do
+ expect(diff).to receive(:collapsed?).and_return(false)
+
+ expect(diff_file.collapsed?).to eq(false)
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index 08312e60f4a..c268f84c759 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -1639,7 +1639,8 @@ describe Gitlab::Diff::PositionTracer, lib: true do
committer: committer
}
- repository.merge(current_user, second_create_file_commit.sha, branch_name, options)
+ merge_request = create(:merge_request, source_branch: second_create_file_commit.sha, target_branch: branch_name, source_project: project)
+ repository.merge(current_user, merge_request, options)
project.commit(branch_name)
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index c79ba11f782..ae064a878b0 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -6,67 +6,6 @@ describe Gitlab::GitAccess, lib: true do
let(:user) { create(:user) }
let(:actor) { user }
- describe 'can_push_to_branch?' do
- describe 'push to none protected branch' do
- it "returns true if user is a master" do
- project.team << [user, :master]
- expect(access.can_push_to_branch?("random_branch")).to be_truthy
- end
-
- it "returns true if user is a developer" do
- project.team << [user, :developer]
- expect(access.can_push_to_branch?("random_branch")).to be_truthy
- end
-
- it "returns false if user is a reporter" do
- project.team << [user, :reporter]
- expect(access.can_push_to_branch?("random_branch")).to be_falsey
- end
- end
-
- describe 'push to protected branch' do
- before do
- @branch = create :protected_branch, project: project
- end
-
- it "returns true if user is a master" do
- project.team << [user, :master]
- expect(access.can_push_to_branch?(@branch.name)).to be_truthy
- end
-
- it "returns false if user is a developer" do
- project.team << [user, :developer]
- expect(access.can_push_to_branch?(@branch.name)).to be_falsey
- end
-
- it "returns false if user is a reporter" do
- project.team << [user, :reporter]
- expect(access.can_push_to_branch?(@branch.name)).to be_falsey
- end
- end
-
- describe 'push to protected branch if allowed for developers' do
- before do
- @branch = create :protected_branch, project: project, developers_can_push: true
- end
-
- it "returns true if user is a master" do
- project.team << [user, :master]
- expect(access.can_push_to_branch?(@branch.name)).to be_truthy
- end
-
- it "returns true if user is a developer" do
- project.team << [user, :developer]
- expect(access.can_push_to_branch?(@branch.name)).to be_truthy
- end
-
- it "returns false if user is a reporter" do
- project.team << [user, :reporter]
- expect(access.can_push_to_branch?(@branch.name)).to be_falsey
- end
- end
- end
-
describe '#check with single protocols allowed' do
def disable_protocol(protocol)
settings = ::ApplicationSetting.create_from_defaults
@@ -105,12 +44,12 @@ describe Gitlab::GitAccess, lib: true do
end
describe 'download_access_check' do
+ subject { access.check('git-upload-pack') }
+
describe 'master permissions' do
before { project.team << [user, :master] }
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_truthy }
end
end
@@ -119,8 +58,6 @@ describe Gitlab::GitAccess, lib: true do
before { project.team << [user, :guest] }
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_falsey }
end
end
@@ -132,16 +69,12 @@ describe Gitlab::GitAccess, lib: true do
end
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_falsey }
end
end
describe 'without acccess to project' do
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_falsey }
end
end
@@ -151,110 +84,208 @@ describe Gitlab::GitAccess, lib: true do
let(:actor) { key }
context 'pull code' do
- before { key.projects << project }
- subject { access.download_access_check }
+ context 'when project is authorized' do
+ before { key.projects << project }
- it { expect(subject.allowed?).to be_truthy }
+ it { expect(subject).to be_allowed }
+ end
+
+ context 'when unauthorized' do
+ context 'from public project' do
+ let(:project) { create(:project, :public) }
+
+ it { expect(subject).to be_allowed }
+ end
+
+ context 'from internal project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'from private project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+ end
end
end
end
describe 'push_access_check' do
- def protect_feature_branch
- create(:protected_branch, name: 'feature', project: project)
- end
+ before { merge_into_protected_branch }
+ let(:unprotected_branch) { FFaker::Internet.user_name }
- def changes
- {
- push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
+ let(:changes) do
+ { push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
push_master: '6f6d7e7ed 570e7b2ab refs/heads/master',
push_protected_branch: '6f6d7e7ed 570e7b2ab refs/heads/feature',
push_remove_protected_branch: "570e7b2ab #{Gitlab::Git::BLANK_SHA} "\
'refs/heads/feature',
push_tag: '6f6d7e7ed 570e7b2ab refs/tags/v1.0.0',
push_new_tag: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/tags/v7.8.9",
- push_all: ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature']
- }
+ push_all: ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature'],
+ merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" }
end
- def self.permissions_matrix
- {
- master: {
- push_new_branch: true,
- push_master: true,
- push_protected_branch: true,
- push_remove_protected_branch: false,
- push_tag: true,
- push_new_tag: true,
- push_all: true,
- },
-
- developer: {
- push_new_branch: true,
- push_master: true,
- push_protected_branch: false,
- push_remove_protected_branch: false,
- push_tag: false,
- push_new_tag: true,
- push_all: false,
- },
-
- reporter: {
- push_new_branch: false,
- push_master: false,
- push_protected_branch: false,
- push_remove_protected_branch: false,
- push_tag: false,
- push_new_tag: false,
- push_all: false,
- },
-
- guest: {
- push_new_branch: false,
- push_master: false,
- push_protected_branch: false,
- push_remove_protected_branch: false,
- push_tag: false,
- push_new_tag: false,
- push_all: false,
- }
- }
+ def stub_git_hooks
+ # Running the `pre-receive` hook is expensive, and not necessary for this test.
+ allow_any_instance_of(GitHooksService).to receive(:execute).and_yield
end
- def self.updated_permissions_matrix
- updated_permissions_matrix = permissions_matrix.dup
- updated_permissions_matrix[:developer][:push_protected_branch] = true
- updated_permissions_matrix[:developer][:push_all] = true
- updated_permissions_matrix
+ def merge_into_protected_branch
+ @protected_branch_merge_commit ||= begin
+ stub_git_hooks
+ project.repository.add_branch(user, unprotected_branch, 'feature')
+ target_branch = project.repository.lookup('feature')
+ source_branch = project.repository.commit_file(user, FFaker::InternetSE.login_user_name, FFaker::HipsterIpsum.paragraph, FFaker::HipsterIpsum.sentence, unprotected_branch, false)
+ rugged = project.repository.rugged
+ author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
+
+ merge_index = rugged.merge_commits(target_branch, source_branch)
+ Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged))
+ end
end
- permissions_matrix.keys.each do |role|
- describe "#{role} access" do
- before { protect_feature_branch }
- before { project.team << [user, role] }
+ def self.run_permission_checks(permissions_matrix)
+ permissions_matrix.keys.each do |role|
+ describe "#{role} access" do
+ before { project.team << [user, role] }
- permissions_matrix[role].each do |action, allowed|
- context action do
- subject { access.push_access_check(changes[action]) }
+ permissions_matrix[role].each do |action, allowed|
+ context action do
+ subject { access.push_access_check(changes[action]) }
- it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ end
end
end
end
end
- context "with enabled developers push to protected branches " do
- updated_permissions_matrix.keys.each do |role|
- describe "#{role} access" do
- before { create(:protected_branch, name: 'feature', developers_can_push: true, project: project) }
- before { project.team << [user, role] }
+ permissions_matrix = {
+ master: {
+ push_new_branch: true,
+ push_master: true,
+ push_protected_branch: true,
+ push_remove_protected_branch: false,
+ push_tag: true,
+ push_new_tag: true,
+ push_all: true,
+ merge_into_protected_branch: true
+ },
+
+ developer: {
+ push_new_branch: true,
+ push_master: true,
+ push_protected_branch: false,
+ push_remove_protected_branch: false,
+ push_tag: false,
+ push_new_tag: true,
+ push_all: false,
+ merge_into_protected_branch: false
+ },
+
+ reporter: {
+ push_new_branch: false,
+ push_master: false,
+ push_protected_branch: false,
+ push_remove_protected_branch: false,
+ push_tag: false,
+ push_new_tag: false,
+ push_all: false,
+ merge_into_protected_branch: false
+ },
+
+ guest: {
+ push_new_branch: false,
+ push_master: false,
+ push_protected_branch: false,
+ push_remove_protected_branch: false,
+ push_tag: false,
+ push_new_tag: false,
+ push_all: false,
+ merge_into_protected_branch: false
+ }
+ }
- updated_permissions_matrix[role].each do |action, allowed|
- context action do
- subject { access.push_access_check(changes[action]) }
+ [['feature', 'exact'], ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type|
+ context do
+ before { create(:protected_branch, name: protected_branch_name, project: project) }
- it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ run_permission_checks(permissions_matrix)
+ end
+
+ context "when 'developers can push' is turned on for the #{protected_branch_type} protected branch" do
+ before { create(:protected_branch, name: protected_branch_name, developers_can_push: true, project: project) }
+
+ run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
+ end
+
+ context "when 'developers can merge' is turned on for the #{protected_branch_type} protected branch" do
+ before { create(:protected_branch, name: protected_branch_name, developers_can_merge: true, project: project) }
+
+ context "when a merge request exists for the given source/target branch" do
+ context "when the merge request is in progress" do
+ before do
+ create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', state: 'locked', in_progress_merge_commit_sha: merge_into_protected_branch)
end
+
+ run_permission_checks(permissions_matrix.deep_merge(developer: { merge_into_protected_branch: true }))
+ end
+
+ context "when the merge request is not in progress" do
+ before do
+ create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', in_progress_merge_commit_sha: nil)
+ end
+
+ run_permission_checks(permissions_matrix.deep_merge(developer: { merge_into_protected_branch: false }))
+ end
+ end
+
+ context "when a merge request does not exist for the given source/target branch" do
+ run_permission_checks(permissions_matrix.deep_merge(developer: { merge_into_protected_branch: false }))
+ end
+ end
+
+ context "when 'developers can merge' and 'developers can push' are turned on for the #{protected_branch_type} protected branch" do
+ before { create(:protected_branch, name: protected_branch_name, developers_can_merge: true, developers_can_push: true, project: project) }
+
+ run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
+ end
+ end
+
+ describe 'deploy key permissions' do
+ let(:key) { create(:deploy_key) }
+ let(:actor) { key }
+
+ context 'push code' do
+ subject { access.check('git-receive-pack') }
+
+ context 'when project is authorized' do
+ before { key.projects << project }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'when unauthorized' do
+ context 'to public project' do
+ let(:project) { create(:project, :public) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'to internal project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'to private project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
end
end
end
diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
new file mode 100644
index 00000000000..5ae178414cc
--- /dev/null
+++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AvatarRestorer, lib: true do
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
+ let(:project) { create(:empty_project) }
+
+ before do
+ allow_any_instance_of(described_class).to receive(:avatar_export_file)
+ .and_return(Rails.root + "spec/fixtures/dk.png")
+ end
+
+ after do
+ project.remove_avatar!
+ end
+
+ it 'restores a project avatar' do
+ expect(described_class.new(project: project, shared: shared).restore).to be true
+ end
+
+ it 'saves the avatar into the project' do
+ described_class.new(project: project, shared: shared).restore
+
+ expect(project.reload.avatar.file.exists?).to be true
+ end
+end
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
new file mode 100644
index 00000000000..d6ee94442cb
--- /dev/null
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AvatarSaver, lib: true do
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
+ let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
+ let(:project_with_avatar) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:project) { create(:empty_project) }
+
+ before do
+ FileUtils.mkdir_p("#{shared.export_path}/avatar/")
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ end
+
+ after do
+ FileUtils.rm_rf("#{shared.export_path}/avatar")
+ end
+
+ it 'saves a project avatar' do
+ described_class.new(project: project_with_avatar, shared: shared).save
+
+ expect(File).to exist("#{shared.export_path}/avatar/dk.png")
+ end
+
+ it 'is fine not to have an avatar' do
+ expect(described_class.new(project: project, shared: shared).save).to be true
+ end
+end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 4113d829c3c..b1a5d72c624 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2765,7 +2765,7 @@
"committer_email": "dmitriy.zaporozhets@gmail.com"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "Binary files a/.DS_Store and /dev/null differ\n",
"new_path": ".DS_Store",
@@ -3138,7 +3138,7 @@
"committer_email": "dmitriy.zaporozhets@gmail.com"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n",
"new_path": "files/ruby/feature.rb",
@@ -3423,7 +3423,7 @@
"committer_email": "james@jameslopez.es"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "--- /dev/null\n+++ b/test\n",
"new_path": "test",
@@ -3960,7 +3960,7 @@
"committer_email": "dmitriy.zaporozhets@gmail.com"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "Binary files a/.DS_Store and /dev/null differ\n",
"new_path": ".DS_Store",
@@ -4597,7 +4597,7 @@
"committer_email": "marmis85@gmail.com"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n",
"new_path": "CHANGELOG",
@@ -5108,7 +5108,7 @@
"committer_email": "stanhu@packetzoom.com"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n",
"new_path": "CHANGELOG",
@@ -5434,7 +5434,7 @@
"id": 11,
"state": "empty",
"st_commits": null,
- "st_diffs": [
+ "utf8_st_diffs": [
],
"merge_request_id": 11,
@@ -5961,7 +5961,7 @@
"committer_email": "dmitriy.zaporozhets@gmail.com"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "Binary files a/.DS_Store and /dev/null differ\n",
"new_path": ".DS_Store",
@@ -6400,7 +6400,7 @@
"committer_email": "james@jameslopez.es"
}
],
- "st_diffs": [
+ "utf8_st_diffs": [
{
"diff": "--- /dev/null\n+++ b/test\n",
"new_path": "test",
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 877be300262..6ae20c943b1 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
describe 'restore project tree' do
+
let(:user) { create(:user) }
let(:namespace) { create(:namespace, owner: user) }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') }
@@ -53,6 +54,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(event.note.noteable.project).not_to be_nil
end
end
+
+ it 'has the correct data for merge request st_diffs' do
+ # makes sure we are renaming the custom method +utf8_st_diffs+ into +st_diffs+
+
+ expect { restored_project_json }.to change(MergeRequestDiff.where.not(st_diffs: nil), :count).by(9)
+ end
end
end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 1424de9e60b..057ef6e76a0 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -102,12 +102,17 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
it 'has ci pipeline notes' do
expect(saved_project_json['pipelines'].first['notes']).not_to be_empty
end
+
+ it 'does not complain about non UTF-8 characters in MR diffs' do
+ ActiveRecord::Base.connection.execute("UPDATE merge_request_diffs SET st_diffs = '---\n- :diff: !binary |-\n LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n YXR'")
+
+ expect(project_tree_saver.save).to be true
+ end
end
end
def setup_project
issue = create(:issue, assignee: user)
- merge_request = create(:merge_request)
label = create(:label)
snippet = create(:project_snippet)
release = create(:release)
@@ -115,12 +120,12 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
project = create(:project,
:public,
issues: [issue],
- merge_requests: [merge_request],
labels: [label],
snippets: [snippet],
releases: [release]
)
+ merge_request = create(:merge_request, source_project: project)
commit_status = create(:commit_status, project: project)
ci_pipeline = create(:ci_pipeline,
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
deleted file mode 100644
index 659facd6c19..00000000000
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ /dev/null
@@ -1,730 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Lfs::Router, lib: true do
- let(:project) { create(:project) }
- let(:public_project) { create(:project, :public) }
- let(:forked_project) { fork_project(public_project, user) }
-
- let(:user) { create(:user) }
- let(:user_two) { create(:user) }
- let!(:lfs_object) { create(:lfs_object, :with_file) }
-
- let(:request) { Rack::Request.new(env) }
- let(:env) do
- {
- 'rack.input' => '',
- 'REQUEST_METHOD' => 'GET',
- }
- end
-
- let(:lfs_router_auth) { new_lfs_router(project, user: user) }
- let(:lfs_router_ci_auth) { new_lfs_router(project, ci: true) }
- let(:lfs_router_noauth) { new_lfs_router(project) }
- let(:lfs_router_public_auth) { new_lfs_router(public_project, user: user) }
- let(:lfs_router_public_ci_auth) { new_lfs_router(public_project, ci: true) }
- let(:lfs_router_public_noauth) { new_lfs_router(public_project) }
- let(:lfs_router_forked_noauth) { new_lfs_router(forked_project) }
- let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user: user_two) }
- let(:lfs_router_forked_ci_auth) { new_lfs_router(forked_project, ci: true) }
-
- let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
- let(:sample_size) { 499013 }
- let(:respond_with_deprecated) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
- let(:respond_with_disabled) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
-
- describe 'when lfs is disabled' do
- before do
- allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
- env['REQUEST_METHOD'] = 'POST'
- body = {
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- },
- { 'oid' => sample_oid,
- 'size' => sample_size
- }
- ],
- 'operation' => 'upload'
- }.to_json
- env['rack.input'] = StringIO.new(body)
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
- end
-
- it 'responds with 501' do
- expect(lfs_router_auth.try_call).to match_array(respond_with_disabled)
- end
- end
-
- describe 'when fetching lfs object using deprecated API' do
- before do
- enable_lfs
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
- end
-
- it 'responds with 501' do
- expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
- end
- end
-
- describe 'when fetching lfs object' do
- before do
- enable_lfs
- env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}"
- end
-
- describe 'and request comes from gitlab-workhorse' do
- context 'without user being authorized' do
- it "responds with status 401" do
- expect(lfs_router_noauth.try_call.first).to eq(401)
- end
- end
-
- context 'with required headers' do
- before do
- project.lfs_objects << lfs_object
- env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
- end
-
- context 'when user does not have project access' do
- it "responds with status 403" do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
-
- context 'when user has project access' do
- before do
- project.team << [user, :master]
- end
-
- it "responds with status 200" do
- expect(lfs_router_auth.try_call.first).to eq(200)
- end
-
- it "responds with the file location" do
- expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
- expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
- end
- end
-
- context 'when CI is authorized' do
- it "responds with status 200" do
- expect(lfs_router_ci_auth.try_call.first).to eq(200)
- end
-
- it "responds with the file location" do
- expect(lfs_router_ci_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
- expect(lfs_router_ci_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
- end
- end
- end
-
- context 'without required headers' do
- it "responds with status 403" do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
- end
- end
-
- describe 'when handling lfs request using deprecated API' do
- before do
- enable_lfs
- env['REQUEST_METHOD'] = 'POST'
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects"
- end
-
- it 'responds with 501' do
- expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
- end
- end
-
- describe 'when handling lfs batch request' do
- before do
- enable_lfs
- env['REQUEST_METHOD'] = 'POST'
- env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
- end
-
- describe 'download' do
- before do
- body = { 'operation' => 'download',
- 'objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- shared_examples 'an authorized requests' do
- context 'when downloading an lfs object that is assigned to our project' do
- before do
- project.lfs_objects << lfs_object
- end
-
- it 'responds with status 200 and href to download' do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'actions' => {
- 'download' => {
- 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
- 'header' => { 'Authorization' => auth }
- }
- }
- }])
- end
- end
-
- context 'when downloading an lfs object that is assigned to other project' do
- before do
- public_project.lfs_objects << lfs_object
- end
-
- it 'responds with status 200 and error message' do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- }])
- end
- end
-
- context 'when downloading a lfs object that does not exist' do
- before do
- body = { 'operation' => 'download',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- it "responds with status 200 and error message" do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- }])
- end
- end
-
- context 'when downloading one new and one existing lfs object' do
- before do
- body = { 'operation' => 'download',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- },
- { 'oid' => sample_oid,
- 'size' => sample_size
- }
- ]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- project.lfs_objects << lfs_object
- end
-
- it "responds with status 200 with upload hypermedia link for the new object" do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- },
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'actions' => {
- 'download' => {
- 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
- 'header' => { 'Authorization' => auth }
- }
- }
- }])
- end
- end
- end
-
- context 'when user is authenticated' do
- let(:auth) { authorize(user) }
-
- before do
- env["HTTP_AUTHORIZATION"] = auth
- project.team << [user, role]
- end
-
- it_behaves_like 'an authorized requests' do
- let(:role) { :reporter }
- let(:router) { lfs_router_auth }
- end
-
- context 'when user does is not member of the project' do
- let(:role) { :guest }
-
- it 'responds with 403' do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
-
- context 'when user does not have download access' do
- let(:role) { :guest }
-
- it 'responds with 403' do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
- end
-
- context 'when CI is authorized' do
- let(:auth) { 'gitlab-ci-token:password' }
-
- before do
- env["HTTP_AUTHORIZATION"] = auth
- end
-
- it_behaves_like 'an authorized requests' do
- let(:router) { lfs_router_ci_auth }
- end
- end
-
- context 'when user is not authenticated' do
- describe 'is accessing public project' do
- before do
- public_project.lfs_objects << lfs_object
- end
-
- it 'responds with status 200 and href to download' do
- response = lfs_router_public_noauth.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'actions' => {
- 'download' => {
- 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
- 'header' => {}
- }
- }
- }])
- end
- end
-
- describe 'is accessing non-public project' do
- before do
- project.lfs_objects << lfs_object
- end
-
- it 'responds with authorization required' do
- expect(lfs_router_noauth.try_call.first).to eq(401)
- end
- end
- end
- end
-
- describe 'upload' do
- before do
- body = { 'operation' => 'upload',
- 'objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- describe 'when request is authenticated' do
- describe 'when user has project push access' do
- before do
- @auth = authorize(user)
- env["HTTP_AUTHORIZATION"] = @auth
- project.team << [user, :developer]
- end
-
- context 'when pushing an lfs object that already exists' do
- before do
- public_project.lfs_objects << lfs_object
- end
-
- it "responds with status 200 and links the object to the project" do
- response_body = lfs_router_auth.try_call.last
- response = ActiveSupport::JSON.decode(response_body.first)
-
- expect(response['objects']).to be_kind_of(Array)
- expect(response['objects'].first['oid']).to eq(sample_oid)
- expect(response['objects'].first['size']).to eq(sample_size)
- expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
- expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
- expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
- expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
- end
- end
-
- context 'when pushing a lfs object that does not exist' do
- before do
- body = { 'operation' => 'upload',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- it "responds with status 200 and upload hypermedia link" do
- response = lfs_router_auth.try_call
- expect(response.first).to eq(200)
-
- response_body = ActiveSupport::JSON.decode(response.last.first)
- expect(response_body['objects']).to be_kind_of(Array)
- expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
- expect(response_body['objects'].first['size']).to eq(1575078)
- expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
- expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
- expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
- end
- end
-
- context 'when pushing one new and one existing lfs object' do
- before do
- body = { 'operation' => 'upload',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- },
- { 'oid' => sample_oid,
- 'size' => sample_size
- }
- ]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- project.lfs_objects << lfs_object
- end
-
- it "responds with status 200 with upload hypermedia link for the new object" do
- response = lfs_router_auth.try_call
- expect(response.first).to eq(200)
-
- response_body = ActiveSupport::JSON.decode(response.last.first)
- expect(response_body['objects']).to be_kind_of(Array)
-
- expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
- expect(response_body['objects'].first['size']).to eq(1575078)
- expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
- expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth)
-
- expect(response_body['objects'].last['oid']).to eq(sample_oid)
- expect(response_body['objects'].last['size']).to eq(sample_size)
- expect(response_body['objects'].last).not_to have_key('actions')
- end
- end
- end
-
- context 'when user does not have push access' do
- it 'responds with 403' do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
-
- context 'when CI is authorized' do
- it 'responds with 401' do
- expect(lfs_router_ci_auth.try_call.first).to eq(401)
- end
- end
- end
-
- context 'when user is not authenticated' do
- context 'when user has push access' do
- before do
- project.team << [user, :master]
- end
-
- it "responds with status 401" do
- expect(lfs_router_public_noauth.try_call.first).to eq(401)
- end
- end
-
- context 'when user does not have push access' do
- it "responds with status 401" do
- expect(lfs_router_public_noauth.try_call.first).to eq(401)
- end
- end
- end
-
- context 'when CI is authorized' do
- let(:auth) { 'gitlab-ci-token:password' }
-
- before do
- env["HTTP_AUTHORIZATION"] = auth
- end
-
- it "responds with status 403" do
- expect(lfs_router_public_ci_auth.try_call.first).to eq(401)
- end
- end
- end
-
- describe 'unsupported' do
- before do
- body = { 'operation' => 'other',
- 'objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- it 'responds with status 404' do
- expect(lfs_router_public_noauth.try_call.first).to eq(404)
- end
- end
- end
-
- describe 'when pushing a lfs object' do
- before do
- enable_lfs
- env['REQUEST_METHOD'] = 'PUT'
- end
-
- shared_examples 'unauthorized' do
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(router.project)
- end
-
- it 'responds with status 401' do
- expect(router.try_call.first).to eq(401)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(router.project)
- end
-
- it 'responds with status 401' do
- expect(router.try_call.first).to eq(401)
- end
- end
-
- context 'and request is sent with a malformed headers' do
- before do
- env["PATH_INFO"] = "#{router.project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
- env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
- end
-
- it 'does not recognize it as a valid lfs command' do
- expect(router.try_call).to eq(nil)
- end
- end
- end
-
- shared_examples 'forbidden' do
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(router.project)
- end
-
- it 'responds with 403' do
- expect(router.try_call.first).to eq(403)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(router.project)
- end
-
- it 'responds with 403' do
- expect(router.try_call.first).to eq(403)
- end
- end
- end
-
- describe 'to one project' do
- describe 'when user is authenticated' do
- describe 'when user has push access to the project' do
- before do
- project.team << [user, :developer]
- end
-
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(project)
- end
-
- it 'responds with status 200, location of lfs store and object details' do
- json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
-
- expect(lfs_router_auth.try_call.first).to eq(200)
- expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(project)
- end
-
- it 'responds with status 200 and lfs object is linked to the project' do
- expect(lfs_router_auth.try_call.first).to eq(200)
- expect(lfs_object.projects.pluck(:id)).to include(project.id)
- end
- end
- end
-
- describe 'and user does not have push access' do
- let(:router) { lfs_router_auth }
-
- it_behaves_like 'forbidden'
- end
- end
-
- context 'when CI is authenticated' do
- let(:router) { lfs_router_ci_auth }
-
- it_behaves_like 'unauthorized'
- end
-
- context 'for unauthenticated' do
- let(:router) { new_lfs_router(project) }
-
- it_behaves_like 'unauthorized'
- end
- end
-
- describe 'to a forked project' do
- let(:forked_project) { fork_project(public_project, user) }
-
- describe 'when user is authenticated' do
- describe 'when user has push access to the project' do
- before do
- forked_project.team << [user_two, :developer]
- end
-
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(forked_project)
- end
-
- it 'responds with status 200, location of lfs store and object details' do
- json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
-
- expect(lfs_router_forked_auth.try_call.first).to eq(200)
- expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(forked_project)
- end
-
- it 'responds with status 200 and lfs object is linked to the source project' do
- expect(lfs_router_forked_auth.try_call.first).to eq(200)
- expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
- end
- end
- end
-
- describe 'and user does not have push access' do
- let(:router) { lfs_router_forked_auth }
-
- it_behaves_like 'forbidden'
- end
- end
-
- context 'when CI is authenticated' do
- let(:router) { lfs_router_forked_ci_auth }
-
- it_behaves_like 'unauthorized'
- end
-
- context 'for unauthenticated' do
- let(:router) { lfs_router_forked_noauth }
-
- it_behaves_like 'unauthorized'
- end
-
- describe 'and second project not related to fork or a source project' do
- let(:second_project) { create(:project) }
- let(:lfs_router_second_project) { new_lfs_router(second_project, user: user) }
-
- before do
- public_project.lfs_objects << lfs_object
- headers_for_upload_finalize(second_project)
- end
-
- context 'when pushing the same lfs object to the second project' do
- before do
- second_project.team << [user, :master]
- end
-
- it 'responds with 200 and links the lfs object to the project' do
- expect(lfs_router_second_project.try_call.first).to eq(200)
- expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
- end
- end
- end
- end
- end
-
- def enable_lfs
- allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- end
-
- def authorize(user)
- ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
- end
-
- def new_lfs_router(project, user: nil, ci: false)
- Gitlab::Lfs::Router.new(project, user, ci, request)
- end
-
- def header_for_upload_authorize(project)
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize"
- end
-
- def headers_for_upload_finalize(project)
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
- env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
- end
-
- def fork_project(project, user, object = nil)
- allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
- Projects::ForkService.new(project, user, {}).execute
- end
-end
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
new file mode 100644
index 00000000000..aa9ec243498
--- /dev/null
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe Gitlab::UserAccess, lib: true do
+ let(:access) { Gitlab::UserAccess.new(user, project: project) }
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ describe 'can_push_to_branch?' do
+ describe 'push to none protected branch' do
+ it 'returns true if user is a master' do
+ project.team << [user, :master]
+ expect(access.can_push_to_branch?('random_branch')).to be_truthy
+ end
+
+ it 'returns true if user is a developer' do
+ project.team << [user, :developer]
+ expect(access.can_push_to_branch?('random_branch')).to be_truthy
+ end
+
+ it 'returns false if user is a reporter' do
+ project.team << [user, :reporter]
+ expect(access.can_push_to_branch?('random_branch')).to be_falsey
+ end
+ end
+
+ describe 'push to protected branch' do
+ let(:branch) { create :protected_branch, project: project }
+
+ it 'returns true if user is a master' do
+ project.team << [user, :master]
+ expect(access.can_push_to_branch?(branch.name)).to be_truthy
+ end
+
+ it 'returns false if user is a developer' do
+ project.team << [user, :developer]
+ expect(access.can_push_to_branch?(branch.name)).to be_falsey
+ end
+
+ it 'returns false if user is a reporter' do
+ project.team << [user, :reporter]
+ expect(access.can_push_to_branch?(branch.name)).to be_falsey
+ end
+ end
+
+ describe 'push to protected branch if allowed for developers' do
+ before do
+ @branch = create :protected_branch, project: project, developers_can_push: true
+ end
+
+ it 'returns true if user is a master' do
+ project.team << [user, :master]
+ expect(access.can_push_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it 'returns true if user is a developer' do
+ project.team << [user, :developer]
+ expect(access.can_push_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it 'returns false if user is a reporter' do
+ project.team << [user, :reporter]
+ expect(access.can_push_to_branch?(@branch.name)).to be_falsey
+ end
+ end
+
+ describe 'merge to protected branch if allowed for developers' do
+ before do
+ @branch = create :protected_branch, project: project, developers_can_merge: true
+ end
+
+ it 'returns true if user is a master' do
+ project.team << [user, :master]
+ expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it 'returns true if user is a developer' do
+ project.team << [user, :developer]
+ expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it 'returns false if user is a reporter' do
+ project.team << [user, :reporter]
+ expect(access.can_merge_to_branch?(@branch.name)).to be_falsey
+ end
+ end
+
+ end
+end