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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2016-09-19 16:36:54 +0300
committerRémy Coutable <remy@rymai.me>2016-09-19 16:36:54 +0300
commitfe084819b4c0aa83ec80b5915e7b3f444b693e9f (patch)
tree3e0816289920f6e1ef4f9e3d4e189e7e53217f08 /spec
parent1e72de669018252c2eb0bc086d66c74cfbbe1a0a (diff)
parent135be3cabb01ca3c825829f18ede4e8720383d7b (diff)
Merge branch 'per-build-token-without-lfs' into 'master'
Make CI to use the permission of the user who is trigger the build This is continuation of https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5735, but with removed all LFS code that is added by: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6043. This also incorporates most of LFS code added in !6043 to simplify further merge. See merge request !6409
Diffstat (limited to 'spec')
-rw-r--r--spec/lib/ci/mask_secret_spec.rb19
-rw-r--r--spec/lib/gitlab/auth_spec.rb78
-rw-r--r--spec/lib/gitlab/git_access_spec.rb116
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb9
-rw-r--r--spec/models/build_spec.rb78
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/requests/ci/api/builds_spec.rb86
-rw-r--r--spec/requests/git_http_spec.rb74
-rw-r--r--spec/requests/jwt_controller_spec.rb30
-rw-r--r--spec/requests/lfs_http_spec.rb237
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb64
11 files changed, 699 insertions, 94 deletions
diff --git a/spec/lib/ci/mask_secret_spec.rb b/spec/lib/ci/mask_secret_spec.rb
new file mode 100644
index 00000000000..518de76911c
--- /dev/null
+++ b/spec/lib/ci/mask_secret_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Ci::MaskSecret, lib: true do
+ subject { described_class }
+
+ describe '#mask' do
+ it 'masks exact number of characters' do
+ expect(subject.mask('token', 'oke')).to eq('txxxn')
+ end
+
+ it 'masks multiple occurrences' do
+ expect(subject.mask('token token token', 'oke')).to eq('txxxn txxxn txxxn')
+ end
+
+ it 'does not mask if not found' do
+ expect(subject.mask('token', 'not')).to eq('token')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 7c23e02d05a..8807a68a0a2 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -4,15 +4,53 @@ describe Gitlab::Auth, lib: true do
let(:gl_auth) { described_class }
describe 'find_for_git_client' do
- it 'recognizes CI' do
- token = '123'
+ context 'build token' do
+ subject { gl_auth.find_for_git_client('gitlab-ci-token', build.token, project: project, ip: 'ip') }
+
+ context 'for running build' do
+ let!(:build) { create(:ci_build, :running) }
+ let(:project) { build.project }
+
+ before do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'gitlab-ci-token')
+ end
+
+ it 'recognises user-less build' do
+ expect(subject).to eq(Gitlab::Auth::Result.new(nil, build.project, :ci, build_authentication_abilities))
+ end
+
+ it 'recognises user token' do
+ build.update(user: create(:user))
+
+ expect(subject).to eq(Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities))
+ end
+ end
+
+ (HasStatus::AVAILABLE_STATUSES - ['running']).each do |build_status|
+ context "for #{build_status} build" do
+ let!(:build) { create(:ci_build, status: build_status) }
+ let(:project) { build.project }
+
+ before do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'gitlab-ci-token')
+ end
+
+ it 'denies authentication' do
+ expect(subject).to eq(Gitlab::Auth::Result.new)
+ end
+ end
+ end
+ end
+
+ it 'recognizes other ci services' do
project = create(:empty_project)
- project.update_attributes(runners_token: token)
+ project.create_drone_ci_service(active: true)
+ project.drone_ci_service.update(token: 'token')
ip = 'ip'
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'gitlab-ci-token')
- expect(gl_auth.find_for_git_client('gitlab-ci-token', token, project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, :ci))
+ expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'drone-ci-token')
+ expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities))
end
it 'recognizes master passwords' do
@@ -20,7 +58,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
- expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :gitlab_or_ldap))
+ expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
end
it 'recognizes OAuth tokens' do
@@ -30,7 +68,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2')
- expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :oauth))
+ expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities))
end
it 'returns double nil for invalid credentials' do
@@ -92,4 +130,30 @@ describe Gitlab::Auth, lib: true do
end
end
end
+
+ private
+
+ def build_authentication_abilities
+ [
+ :read_project,
+ :build_download_code,
+ :build_read_container_image,
+ :build_create_container_image
+ ]
+ end
+
+ def read_authentication_abilities
+ [
+ :read_project,
+ :download_code,
+ :read_container_image
+ ]
+ end
+
+ def full_authentication_abilities
+ read_authentication_abilities + [
+ :push_code,
+ :create_container_image
+ ]
+ end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index f12c9a370f7..ed43646330f 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1,10 +1,17 @@
require 'spec_helper'
describe Gitlab::GitAccess, lib: true do
- let(:access) { Gitlab::GitAccess.new(actor, project, 'web') }
+ let(:access) { Gitlab::GitAccess.new(actor, project, 'web', authentication_abilities: authentication_abilities) }
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:actor) { user }
+ let(:authentication_abilities) do
+ [
+ :read_project,
+ :download_code,
+ :push_code
+ ]
+ end
describe '#check with single protocols allowed' do
def disable_protocol(protocol)
@@ -15,7 +22,7 @@ describe Gitlab::GitAccess, lib: true do
context 'ssh disabled' do
before do
disable_protocol('ssh')
- @acc = Gitlab::GitAccess.new(actor, project, 'ssh')
+ @acc = Gitlab::GitAccess.new(actor, project, 'ssh', authentication_abilities: authentication_abilities)
end
it 'blocks ssh git push' do
@@ -30,7 +37,7 @@ describe Gitlab::GitAccess, lib: true do
context 'http disabled' do
before do
disable_protocol('http')
- @acc = Gitlab::GitAccess.new(actor, project, 'http')
+ @acc = Gitlab::GitAccess.new(actor, project, 'http', authentication_abilities: authentication_abilities)
end
it 'blocks http push' do
@@ -111,6 +118,36 @@ describe Gitlab::GitAccess, lib: true do
end
end
end
+
+ describe 'build authentication_abilities permissions' do
+ let(:authentication_abilities) { build_authentication_abilities }
+
+ describe 'reporter user' do
+ before { project.team << [user, :reporter] }
+
+ context 'pull code' do
+ it { expect(subject).to be_allowed }
+ end
+ end
+
+ describe 'admin user' do
+ let(:user) { create(:admin) }
+
+ context 'when member of the project' do
+ before { project.team << [user, :reporter] }
+
+ context 'pull code' do
+ it { expect(subject).to be_allowed }
+ end
+ end
+
+ context 'when is not member of the project' do
+ context 'pull code' do
+ it { expect(subject).not_to be_allowed }
+ end
+ end
+ end
+ end
end
describe 'push_access_check' do
@@ -283,38 +320,71 @@ describe Gitlab::GitAccess, lib: true do
end
end
- describe 'deploy key permissions' do
- let(:key) { create(:deploy_key) }
- let(:actor) { key }
+ shared_examples 'can not push code' do
+ subject { access.check('git-receive-pack', '_any') }
+
+ context 'when project is authorized' do
+ before { authorize }
- context 'push code' do
- subject { access.check('git-receive-pack', '_any') }
+ it { expect(subject).not_to be_allowed }
+ end
- context 'when project is authorized' do
- before { key.projects << project }
+ context 'when unauthorized' do
+ context 'to public project' do
+ let(:project) { create(:project, :public) }
it { expect(subject).not_to be_allowed }
end
- context 'when unauthorized' do
- context 'to public project' do
- let(:project) { create(:project, :public) }
+ context 'to internal project' do
+ let(:project) { create(:project, :internal) }
- it { expect(subject).not_to be_allowed }
- end
+ it { expect(subject).not_to be_allowed }
+ end
- context 'to internal project' do
- let(:project) { create(:project, :internal) }
+ context 'to private project' do
+ let(:project) { create(:project, :internal) }
- it { expect(subject).not_to be_allowed }
- end
+ it { expect(subject).not_to be_allowed }
+ end
+ end
+ end
- context 'to private project' do
- let(:project) { create(:project, :internal) }
+ describe 'build authentication abilities' do
+ let(:authentication_abilities) { build_authentication_abilities }
- it { expect(subject).not_to be_allowed }
- end
+ it_behaves_like 'can not push code' do
+ def authorize
+ project.team << [user, :reporter]
end
end
end
+
+ describe 'deploy key permissions' do
+ let(:key) { create(:deploy_key) }
+ let(:actor) { key }
+
+ it_behaves_like 'can not push code' do
+ def authorize
+ key.projects << project
+ end
+ end
+ end
+
+ private
+
+ def build_authentication_abilities
+ [
+ :read_project,
+ :build_download_code
+ ]
+ end
+
+ def full_authentication_abilities
+ [
+ :read_project,
+ :download_code,
+ :push_code
+ ]
+ end
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 4244b807d41..576cda595bb 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -1,9 +1,16 @@
require 'spec_helper'
describe Gitlab::GitAccessWiki, lib: true do
- let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web') }
+ let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities) }
let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:authentication_abilities) do
+ [
+ :read_project,
+ :download_code,
+ :push_code
+ ]
+ end
describe 'push_allowed?' do
before do
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 8eab4281bc7..e7864b7ad33 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -88,9 +88,7 @@ describe Ci::Build, models: true do
end
describe '#trace' do
- subject { build.trace_html }
-
- it { is_expected.to be_empty }
+ it { expect(build.trace).to be_nil }
context 'when build.trace contains text' do
let(:text) { 'example output' }
@@ -98,16 +96,80 @@ describe Ci::Build, models: true do
build.trace = text
end
- it { is_expected.to include(text) }
- it { expect(subject.length).to be >= text.length }
+ it { expect(build.trace).to eq(text) }
+ end
+
+ context 'when build.trace hides runners token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.update(trace: token)
+ build.project.update(runners_token: token)
+ end
+
+ it { expect(build.trace).not_to include(token) }
+ it { expect(build.raw_trace).to include(token) }
+ end
+
+ context 'when build.trace hides build token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.update(trace: token)
+ build.update(token: token)
+ end
+
+ it { expect(build.trace).not_to include(token) }
+ it { expect(build.raw_trace).to include(token) }
+ end
+ end
+
+ describe '#raw_trace' do
+ subject { build.raw_trace }
+
+ context 'when build.trace hides runners token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.project.update(runners_token: token)
+ build.update(trace: token)
+ end
+
+ it { is_expected.not_to include(token) }
+ end
+
+ context 'when build.trace hides build token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.update(token: token)
+ build.update(trace: token)
+ end
+
+ it { is_expected.not_to include(token) }
+ end
+ end
+
+ context '#append_trace' do
+ subject { build.trace_html }
+
+ context 'when build.trace hides runners token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.project.update(runners_token: token)
+ build.append_trace(token, 0)
+ end
+
+ it { is_expected.not_to include(token) }
end
- context 'when build.trace hides token' do
+ context 'when build.trace hides build token' do
let(:token) { 'my_secret_token' }
before do
- build.project.update_attributes(runners_token: token)
- build.update_attributes(trace: token)
+ build.update(token: token)
+ build.append_trace(token, 0)
end
it { is_expected.not_to include(token) }
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index bce18b4e99e..a37a00f461a 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -8,7 +8,7 @@ describe Ci::Build, models: true do
it 'obfuscates project runners token' do
allow(build).to receive(:raw_trace).and_return("Test: #{build.project.runners_token}")
- expect(build.trace).to eq("Test: xxxxxx")
+ expect(build.trace).to eq("Test: xxxxxxxxxxxxxxxxxxxx")
end
it 'empty project runners token' do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 780bd7f2859..df97f1bf7b6 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -254,7 +254,8 @@ describe Ci::API::API do
let(:get_url) { ci_api("/builds/#{build.id}/artifacts") }
let(:jwt_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:headers) { { "GitLab-Workhorse" => "1.0", Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt_token } }
- let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) }
+ let(:token) { build.token }
+ let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => token) }
before { build.run! }
@@ -262,6 +263,7 @@ describe Ci::API::API do
context "should authorize posting artifact to running build" do
it "using token as parameter" do
post authorize_url, { token: build.token }, headers
+
expect(response).to have_http_status(200)
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response["TempPath"]).not_to be_nil
@@ -269,6 +271,15 @@ describe Ci::API::API do
it "using token as header" do
post authorize_url, {}, headers_with_token
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response["TempPath"]).not_to be_nil
+ end
+
+ it "using runners token" do
+ post authorize_url, { token: build.project.runners_token }, headers
+
expect(response).to have_http_status(200)
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response["TempPath"]).not_to be_nil
@@ -276,7 +287,9 @@ describe Ci::API::API do
it "reject requests that did not go through gitlab-workhorse" do
headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
+
post authorize_url, { token: build.token }, headers
+
expect(response).to have_http_status(500)
end
end
@@ -284,13 +297,17 @@ describe Ci::API::API do
context "should fail to post too large artifact" do
it "using token as parameter" do
stub_application_setting(max_artifacts_size: 0)
+
post authorize_url, { token: build.token, filesize: 100 }, headers
+
expect(response).to have_http_status(413)
end
it "using token as header" do
stub_application_setting(max_artifacts_size: 0)
+
post authorize_url, { filesize: 100 }, headers_with_token
+
expect(response).to have_http_status(413)
end
end
@@ -358,6 +375,16 @@ describe Ci::API::API do
it_behaves_like 'successful artifacts upload'
end
+
+ context 'when using runners token' do
+ let(:token) { build.project.runners_token }
+
+ before do
+ upload_artifacts(file_upload, headers_with_token)
+ end
+
+ it_behaves_like 'successful artifacts upload'
+ end
end
context 'posts artifacts file and metadata file' do
@@ -497,19 +524,40 @@ describe Ci::API::API do
before do
delete delete_url, token: build.token
- build.reload
end
- it 'removes build artifacts' do
- expect(response).to have_http_status(200)
- expect(build.artifacts_file.exists?).to be_falsy
- expect(build.artifacts_metadata.exists?).to be_falsy
- expect(build.artifacts_size).to be_nil
+ shared_examples 'having removable artifacts' do
+ it 'removes build artifacts' do
+ build.reload
+
+ expect(response).to have_http_status(200)
+ expect(build.artifacts_file.exists?).to be_falsy
+ expect(build.artifacts_metadata.exists?).to be_falsy
+ expect(build.artifacts_size).to be_nil
+ end
+ end
+
+ context 'when using build token' do
+ before do
+ delete delete_url, token: build.token
+ end
+
+ it_behaves_like 'having removable artifacts'
+ end
+
+ context 'when using runnners token' do
+ before do
+ delete delete_url, token: build.project.runners_token
+ end
+
+ it_behaves_like 'having removable artifacts'
end
end
describe 'GET /builds/:id/artifacts' do
- before { get get_url, token: build.token }
+ before do
+ get get_url, token: token
+ end
context 'build has artifacts' do
let(:build) { create(:ci_build, :artifacts) }
@@ -518,13 +566,29 @@ describe Ci::API::API do
'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
- it 'downloads artifact' do
- expect(response).to have_http_status(200)
- expect(response.headers).to include download_headers
+ shared_examples 'having downloadable artifacts' do
+ it 'download artifacts' do
+ expect(response).to have_http_status(200)
+ expect(response.headers).to include download_headers
+ end
+ end
+
+ context 'when using build token' do
+ let(:token) { build.token }
+
+ it_behaves_like 'having downloadable artifacts'
+ end
+
+ context 'when using runnners token' do
+ let(:token) { build.project.runners_token }
+
+ it_behaves_like 'having downloadable artifacts'
end
end
context 'build does not has artifacts' do
+ let(:token) { build.token }
+
it 'responds with not found' do
expect(response).to have_http_status(404)
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index b7001fede40..e3922bec689 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -300,25 +300,79 @@ describe 'Git HTTP requests', lib: true do
end
context "when a gitlab ci token is provided" do
- let(:token) { 123 }
- let(:project) { FactoryGirl.create :empty_project }
+ let(:build) { create(:ci_build, :running) }
+ let(:project) { build.project }
+ let(:other_project) { create(:empty_project) }
before do
- project.update_attributes(runners_token: token)
project.project_feature.update_attributes(builds_access_level: ProjectFeature::ENABLED)
end
- it "downloads get status 200" do
- clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
+ context 'when build created by system is authenticated' do
+ it "downloads get status 200" do
+ clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
- expect(response).to have_http_status(200)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ end
+
+ it "uploads get status 401 (no project existence information leak)" do
+ push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(401)
+ end
+
+ it "downloads from other project get status 404" do
+ clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(404)
+ end
end
- it "uploads get status 401 (no project existence information leak)" do
- push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
+ context 'and build created by' do
+ before do
+ build.update(user: user)
+ project.team << [user, :reporter]
+ end
- expect(response).to have_http_status(401)
+ shared_examples 'can download code only from own projects' do
+ it 'downloads get status 200' do
+ clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ end
+
+ it 'uploads get status 403' do
+ push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'administrator' do
+ let(:user) { create(:admin) }
+
+ it_behaves_like 'can download code only from own projects'
+
+ it 'downloads from other project get status 403' do
+ clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'regular user' do
+ let(:user) { create(:user) }
+
+ it_behaves_like 'can download code only from own projects'
+
+ it 'downloads from other project get status 404' do
+ clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(404)
+ end
+ end
end
end
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index fc42b534dca..6b956e63004 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -22,11 +22,13 @@ describe JwtController do
context 'when using authorized request' do
context 'using CI token' do
- let(:project) { create(:empty_project, runners_token: 'token') }
- let(:headers) { { authorization: credentials('gitlab-ci-token', project.runners_token) } }
+ let(:build) { create(:ci_build, :running) }
+ let(:project) { build.project }
+ let(:headers) { { authorization: credentials('gitlab-ci-token', build.token) } }
context 'project with enabled CI' do
subject! { get '/jwt/auth', parameters, headers }
+
it { expect(service_class).to have_received(:new).with(project, nil, parameters) }
end
@@ -43,13 +45,31 @@ describe JwtController do
context 'using User login' do
let(:user) { create(:user) }
- let(:headers) { { authorization: credentials('user', 'password') } }
-
- before { expect(Gitlab::Auth).to receive(:find_with_user_password).with('user', 'password').and_return(user) }
+ let(:headers) { { authorization: credentials(user.username, user.password) } }
subject! { get '/jwt/auth', parameters, headers }
it { expect(service_class).to have_received(:new).with(nil, user, parameters) }
+
+ context 'when user has 2FA enabled' do
+ let(:user) { create(:user, :two_factor) }
+
+ context 'without personal token' do
+ it 'rejects the authorization attempt' do
+ expect(response).to have_http_status(401)
+ expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP')
+ end
+ end
+
+ context 'with personal token' do
+ let(:access_token) { create(:personal_access_token, user: user) }
+ let(:headers) { { authorization: credentials(user.username, access_token.token) } }
+
+ it 'rejects the authorization attempt' do
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
end
context 'using invalid login' do
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 6e551bb65fa..b58d410b7a3 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -14,6 +14,7 @@ describe 'Git LFS API and storage' do
end
let(:authorization) { }
let(:sendfile) { }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:sample_oid) { lfs_object.oid }
let(:sample_size) { lfs_object.size }
@@ -244,14 +245,63 @@ describe 'Git LFS API and storage' do
end
end
- context 'when CI is authorized' do
+ context 'when build is authorized as' do
let(:authorization) { authorize_ci_project }
- let(:update_permissions) do
- project.lfs_objects << lfs_object
+ shared_examples 'can download LFS only from own projects' do
+ context 'for own project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+
+ let(:update_permissions) do
+ project.team << [user, :reporter]
+ project.lfs_objects << lfs_object
+ end
+
+ it_behaves_like 'responds with a file'
+ end
+
+ context 'for other project' do
+ let(:other_project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+
+ let(:update_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'rejects downloading code' do
+ expect(response).to have_http_status(other_project_status)
+ end
+ end
+ end
+
+ context 'administrator' do
+ let(:user) { create(:admin) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it_behaves_like 'can download LFS only from own projects' do
+ # We render 403, because administrator does have normally access
+ let(:other_project_status) { 403 }
+ end
+ end
+
+ context 'regular user' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it_behaves_like 'can download LFS only from own projects' do
+ # We render 404, to prevent data leakage about existence of the project
+ let(:other_project_status) { 404 }
+ end
end
- it_behaves_like 'responds with a file'
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it_behaves_like 'can download LFS only from own projects' do
+ # We render 404, to prevent data leakage about existence of the project
+ let(:other_project_status) { 404 }
+ end
+ end
end
end
@@ -431,10 +481,62 @@ describe 'Git LFS API and storage' do
end
end
- context 'when CI is authorized' do
+ context 'when build is authorized as' do
let(:authorization) { authorize_ci_project }
- it_behaves_like 'an authorized requests'
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ shared_examples 'can download LFS only from own projects' do
+ context 'for own project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+
+ let(:update_user_permissions) do
+ project.team << [user, :reporter]
+ end
+
+ it_behaves_like 'an authorized requests'
+ end
+
+ context 'for other project' do
+ let(:other_project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+
+ it 'rejects downloading code' do
+ expect(response).to have_http_status(other_project_status)
+ end
+ end
+ end
+
+ context 'administrator' do
+ let(:user) { create(:admin) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it_behaves_like 'can download LFS only from own projects' do
+ # We render 403, because administrator does have normally access
+ let(:other_project_status) { 403 }
+ end
+ end
+
+ context 'regular user' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it_behaves_like 'can download LFS only from own projects' do
+ # We render 404, to prevent data leakage about existence of the project
+ let(:other_project_status) { 404 }
+ end
+ end
+
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it_behaves_like 'can download LFS only from own projects' do
+ # We render 404, to prevent data leakage about existence of the project
+ let(:other_project_status) { 404 }
+ end
+ end
end
context 'when user is not authenticated' do
@@ -583,11 +685,37 @@ describe 'Git LFS API and storage' do
end
end
- context 'when CI is authorized' do
+ context 'when build is authorized' do
let(:authorization) { authorize_ci_project }
- it 'responds with 401' do
- expect(response).to have_http_status(401)
+ context 'build has an user' do
+ let(:user) { create(:user) }
+
+ context 'tries to push to own project' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'tries to push to other project' do
+ let(:other_project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
end
end
end
@@ -609,14 +737,6 @@ describe 'Git LFS API and storage' do
end
end
end
-
- context 'when CI is authorized' do
- let(:authorization) { authorize_ci_project }
-
- it 'responds with status 403' do
- expect(response).to have_http_status(401)
- end
- end
end
describe 'unsupported' do
@@ -779,10 +899,51 @@ describe 'Git LFS API and storage' do
end
end
- context 'when CI is authenticated' do
+ context 'when build is authorized' do
let(:authorization) { authorize_ci_project }
- it_behaves_like 'unauthorized'
+ context 'build has an user' do
+ let(:user) { create(:user) }
+
+ context 'tries to push to own project' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ before do
+ project.team << [user, :developer]
+ put_authorize
+ end
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'tries to push to other project' do
+ let(:other_project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ before do
+ put_authorize
+ end
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+
+ before do
+ put_authorize
+ end
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
end
context 'for unauthenticated' do
@@ -839,10 +1000,42 @@ describe 'Git LFS API and storage' do
end
end
- context 'when CI is authenticated' do
+ context 'when build is authorized' do
let(:authorization) { authorize_ci_project }
- it_behaves_like 'unauthorized'
+ before do
+ put_authorize
+ end
+
+ context 'build has an user' do
+ let(:user) { create(:user) }
+
+ context 'tries to push to own project' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'tries to push to other project' do
+ let(:other_project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
end
context 'for unauthenticated' do
@@ -897,7 +1090,7 @@ describe 'Git LFS API and storage' do
end
def authorize_ci_project
- ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', project.runners_token)
+ ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', build.token)
end
def authorize_user
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 7cc71f706ce..c64df4979b0 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -6,8 +6,14 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
let(:current_params) { {} }
let(:rsa_key) { OpenSSL::PKey::RSA.generate(512) }
let(:payload) { JWT.decode(subject[:token], rsa_key).first }
+ let(:authentication_abilities) do
+ [
+ :read_container_image,
+ :create_container_image
+ ]
+ end
- subject { described_class.new(current_project, current_user, current_params).execute }
+ subject { described_class.new(current_project, current_user, current_params).execute(authentication_abilities: authentication_abilities) }
before do
allow(Gitlab.config.registry).to receive_messages(enabled: true, issuer: 'rspec', key: nil)
@@ -189,13 +195,22 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
end
- context 'project authorization' do
+ context 'build authorized as user' do
let(:current_project) { create(:empty_project) }
+ let(:current_user) { create(:user) }
+ let(:authentication_abilities) do
+ [
+ :build_read_container_image,
+ :build_create_container_image
+ ]
+ end
- context 'allow to use scope-less authentication' do
- it_behaves_like 'a valid token'
+ before do
+ current_project.team << [current_user, :developer]
end
+ it_behaves_like 'a valid token'
+
context 'allow to pull and push images' do
let(:current_params) do
{ scope: "repository:#{current_project.path_with_namespace}:pull,push" }
@@ -214,12 +229,44 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
context 'allow for public' do
let(:project) { create(:empty_project, :public) }
+
it_behaves_like 'a pullable'
end
- context 'disallow for private' do
+ shared_examples 'pullable for being team member' do
+ context 'when you are not member' do
+ it_behaves_like 'an inaccessible'
+ end
+
+ context 'when you are member' do
+ before do
+ project.team << [current_user, :developer]
+ end
+
+ it_behaves_like 'a pullable'
+ end
+ end
+
+ context 'for private' do
let(:project) { create(:empty_project, :private) }
- it_behaves_like 'an inaccessible'
+
+ it_behaves_like 'pullable for being team member'
+
+ context 'when you are admin' do
+ let(:current_user) { create(:admin) }
+
+ context 'when you are not member' do
+ it_behaves_like 'an inaccessible'
+ end
+
+ context 'when you are member' do
+ before do
+ project.team << [current_user, :developer]
+ end
+
+ it_behaves_like 'a pullable'
+ end
+ end
end
end
@@ -230,6 +277,11 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
context 'disallow for all' do
let(:project) { create(:empty_project, :public) }
+
+ before do
+ project.team << [current_user, :developer]
+ end
+
it_behaves_like 'an inaccessible'
end
end