Commit f2d02bf4 authored by Aakriti Gupta's avatar Aakriti Gupta Committed by Michael Kozono

Check maintenance mode when checking for git access

- return 401 and false for success so that
consumers like gitlab-shell can interpret it
correctly

- also, re-factor base_spec. Move helper methods
into a helper class and re-use in ee/.../base_spec
parent 9f1089fd
...@@ -10,6 +10,7 @@ module EE ...@@ -10,6 +10,7 @@ module EE
override :check override :check
def check(cmd, changes) def check(cmd, changes)
check_maintenance_mode!(cmd)
check_geo_license! check_geo_license!
check_smartcard_access! check_smartcard_access!
...@@ -91,6 +92,17 @@ module EE ...@@ -91,6 +92,17 @@ module EE
end end
end end
def check_maintenance_mode!(cmd)
return unless cmd == 'git-receive-pack'
return unless maintenance_mode?
raise ::Gitlab::GitAccess::ForbiddenError, 'Git push is not allowed because this GitLab instance is currently in (read-only) maintenance mode.'
end
def maintenance_mode?
::Gitlab::CurrentSettings.current_application_settings.maintenance_mode
end
def can_access_without_new_smartcard_login? def can_access_without_new_smartcard_login?
return true unless user return true unless user
......
...@@ -729,6 +729,40 @@ RSpec.describe Gitlab::GitAccess do ...@@ -729,6 +729,40 @@ RSpec.describe Gitlab::GitAccess do
end end
end end
describe '#check_maintenance_mode!' do
let(:changes) { Gitlab::GitAccess::ANY }
before do
project.add_maintainer(user)
end
def push_access_check
access.check('git-receive-pack', changes)
end
context 'when maintenance mode is enabled' do
before do
stub_application_setting(maintenance_mode: true)
end
it 'blocks git push' do
aggregate_failures do
expect { push_access_check }.to raise_forbidden('Git push is not allowed because this GitLab instance is currently in (read-only) maintenance mode.')
end
end
end
context 'when maintenance mode is disabled' do
before do
stub_application_setting(maintenance_mode: false)
end
it 'allows git push' do
expect { push_access_check }.not_to raise_error
end
end
end
private private
def access def access
......
...@@ -3,14 +3,15 @@ require 'spec_helper' ...@@ -3,14 +3,15 @@ require 'spec_helper'
RSpec.describe API::Internal::Base do RSpec.describe API::Internal::Base do
include EE::GeoHelpers include EE::GeoHelpers
include APIInternalBaseHelpers
let_it_be(:primary_url) { 'http://primary.example.com' } let_it_be(:primary_url) { 'http://primary.example.com' }
let_it_be(:secondary_url) { 'http://secondary.example.com' } let_it_be(:secondary_url) { 'http://secondary.example.com' }
let_it_be(:primary_node, reload: true) { create(:geo_node, :primary, url: primary_url) } let_it_be(:primary_node, reload: true) { create(:geo_node, :primary, url: primary_url) }
let_it_be(:secondary_node, reload: true) { create(:geo_node, url: secondary_url) } let_it_be(:secondary_node, reload: true) { create(:geo_node, url: secondary_url) }
let_it_be(:user) { create(:user) }
describe 'POST /internal/post_receive', :geo do describe 'POST /internal/post_receive', :geo do
let_it_be(:user) { create(:user) }
let(:key) { create(:key, user: user) } let(:key) { create(:key, user: user) }
let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) } let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) }
let(:secret_token) { Gitlab::Shell.secret_token } let(:secret_token) { Gitlab::Shell.secret_token }
...@@ -72,7 +73,6 @@ RSpec.describe API::Internal::Base do ...@@ -72,7 +73,6 @@ RSpec.describe API::Internal::Base do
end end
describe "POST /internal/allowed" do describe "POST /internal/allowed" do
let_it_be(:user) { create(:user) }
let_it_be(:key) { create(:key, user: user) } let_it_be(:key) { create(:key, user: user) }
let(:secret_token) { Gitlab::Shell.secret_token } let(:secret_token) { Gitlab::Shell.secret_token }
...@@ -243,10 +243,41 @@ RSpec.describe API::Internal::Base do ...@@ -243,10 +243,41 @@ RSpec.describe API::Internal::Base do
end end
end end
end end
context 'maintenance mode enabled' do
let_it_be(:project) { create(:project, :repository) }
before do
stub_application_setting(maintenance_mode: true)
project.add_developer(user)
end
context 'when action is git push' do
it 'returns forbidden' do
push(key, project)
expect(response).to have_gitlab_http_status(:unauthorized)
expect(json_response["status"]).to be_falsey
expect(json_response["message"]).to eq(
'Git push is not allowed because this GitLab instance is currently in (read-only) maintenance mode.'
)
expect(user.reload.last_activity_on).to be_nil
end
end
context 'when action is not git push' do
it 'returns success' do
pull(key, project)
expect(response).to have_gitlab_http_status(:success)
expect(json_response["status"]).to be_truthy
end
end
end
end end
describe "POST /internal/lfs_authenticate", :geo do describe "POST /internal/lfs_authenticate", :geo do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:secret_token) { Gitlab::Shell.secret_token } let(:secret_token) { Gitlab::Shell.secret_token }
...@@ -279,7 +310,6 @@ RSpec.describe API::Internal::Base do ...@@ -279,7 +310,6 @@ RSpec.describe API::Internal::Base do
end end
describe 'POST /internal/personal_access_token' do describe 'POST /internal/personal_access_token' do
let_it_be(:user) { create(:user) }
let_it_be(:key) { create(:key, user: user) } let_it_be(:key) { create(:key, user: user) }
let(:instance_level_max_personal_access_token_lifetime) { nil } let(:instance_level_max_personal_access_token_lifetime) { nil }
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe API::Internal::Base do RSpec.describe API::Internal::Base do
include APIInternalBaseHelpers
let_it_be(:user, reload: true) { create(:user) } let_it_be(:user, reload: true) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) } let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) }
let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) }
...@@ -1207,88 +1209,6 @@ RSpec.describe API::Internal::Base do ...@@ -1207,88 +1209,6 @@ RSpec.describe API::Internal::Base do
end end
end end
def gl_repository_for(container)
case container
when ProjectWiki
Gitlab::GlRepository::WIKI.identifier_for_container(container.project)
when Project
Gitlab::GlRepository::PROJECT.identifier_for_container(container)
when Snippet
Gitlab::GlRepository::SNIPPET.identifier_for_container(container)
else
nil
end
end
def full_path_for(container)
case container
when PersonalSnippet
"snippets/#{container.id}"
when ProjectSnippet
"#{container.project.full_path}/snippets/#{container.id}"
else
container.full_path
end
end
def pull(key, container, protocol = 'ssh')
post(
api("/internal/allowed"),
params: {
key_id: key.id,
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-pack',
secret_token: secret_token,
protocol: protocol
}
)
end
def push(key, container, protocol = 'ssh', env: nil, changes: nil)
push_with_path(key,
full_path: full_path_for(container),
gl_repository: gl_repository_for(container),
protocol: protocol,
env: env,
changes: changes)
end
def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: nil, changes: nil)
changes ||= 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master'
params = {
changes: changes,
key_id: key.id,
project: full_path,
action: 'git-receive-pack',
secret_token: secret_token,
protocol: protocol,
env: env
}
params[:gl_repository] = gl_repository if gl_repository
post(
api("/internal/allowed"),
params: params
)
end
def archive(key, container)
post(
api("/internal/allowed"),
params: {
ref: 'master',
key_id: key.id,
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-archive',
secret_token: secret_token,
protocol: 'ssh'
}
)
end
def lfs_auth_project(project) def lfs_auth_project(project)
post( post(
api("/internal/lfs_authenticate"), api("/internal/lfs_authenticate"),
......
# frozen_string_literal: true
module APIInternalBaseHelpers
def gl_repository_for(container)
case container
when ProjectWiki
Gitlab::GlRepository::WIKI.identifier_for_container(container.project)
when Project
Gitlab::GlRepository::PROJECT.identifier_for_container(container)
when Snippet
Gitlab::GlRepository::SNIPPET.identifier_for_container(container)
else
nil
end
end
def full_path_for(container)
case container
when PersonalSnippet
"snippets/#{container.id}"
when ProjectSnippet
"#{container.project.full_path}/snippets/#{container.id}"
else
container.full_path
end
end
def pull(key, container, protocol = 'ssh')
post(
api("/internal/allowed"),
params: {
key_id: key.id,
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-pack',
secret_token: secret_token,
protocol: protocol
}
)
end
def push(key, container, protocol = 'ssh', env: nil, changes: nil)
push_with_path(key,
full_path: full_path_for(container),
gl_repository: gl_repository_for(container),
protocol: protocol,
env: env,
changes: changes)
end
def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: nil, changes: nil)
changes ||= 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master'
params = {
changes: changes,
key_id: key.id,
project: full_path,
action: 'git-receive-pack',
secret_token: secret_token,
protocol: protocol,
env: env
}
params[:gl_repository] = gl_repository if gl_repository
post(
api("/internal/allowed"),
params: params
)
end
def archive(key, container)
post(
api("/internal/allowed"),
params: {
ref: 'master',
key_id: key.id,
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-archive',
secret_token: secret_token,
protocol: 'ssh'
}
)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment