Commit cb5a5eb8 authored by Robin Bobbitt's avatar Robin Bobbitt

Instruct user to use a personal access token for Git over HTTP

If internal auth is disabled and LDAP is not configured on the instance,
present the user with a message to create a personal access token if his
Git over HTTP auth attempt fails.
parent 359a8f1f
...@@ -39,7 +39,7 @@ class JwtController < ApplicationController ...@@ -39,7 +39,7 @@ class JwtController < ApplicationController
errors: [ errors: [
{ code: 'UNAUTHORIZED', { code: 'UNAUTHORIZED',
message: "HTTP Basic: Access denied\n" \ message: "HTTP Basic: Access denied\n" \
"You have 2FA enabled, please use a personal access token for Git over HTTP.\n" \ "You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}" } "You can generate one at #{profile_personal_access_tokens_url}" }
] ]
}, status: 401 }, status: 401
......
...@@ -104,7 +104,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController ...@@ -104,7 +104,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
def render_missing_personal_token def render_missing_personal_token
render plain: "HTTP Basic: Access denied\n" \ render plain: "HTTP Basic: Access denied\n" \
"You have 2FA enabled, please use a personal access token for Git over HTTP.\n" \ "You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}", "You can generate one at #{profile_personal_access_tokens_url}",
status: 401 status: 401
end end
......
---
title: Instruct user to use personal access token for Git over HTTP
merge_request: 11986
author: Robin Bobbitt
...@@ -37,7 +37,11 @@ module Gitlab ...@@ -37,7 +37,11 @@ module Gitlab
rate_limit!(ip, success: result.success?, login: login) rate_limit!(ip, success: result.success?, login: login)
Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor) Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor)
result return result if result.success? || current_application_settings.signin_enabled? || Gitlab::LDAP::Config.enabled?
# If sign-in is disabled and LDAP is not configured, recommend a
# personal access token on failed auth attempts
raise Gitlab::Auth::MissingPersonalTokenError
end end
def find_with_user_password(login, password) def find_with_user_password(login, password)
......
...@@ -204,6 +204,12 @@ describe Gitlab::Auth, lib: true do ...@@ -204,6 +204,12 @@ describe Gitlab::Auth, lib: true do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new) expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new)
end end
it 'throws an error suggesting user create a PAT when internal auth is disabled' do
allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
expect { gl_auth.find_for_git_client('foo', 'bar', project: nil, ip: 'ip') }.to raise_error(Gitlab::Auth::MissingPersonalTokenError)
end
end end
describe 'find_with_user_password' do describe 'find_with_user_password' do
......
...@@ -418,17 +418,17 @@ describe 'Git HTTP requests', lib: true do ...@@ -418,17 +418,17 @@ describe 'Git HTTP requests', lib: true do
end end
context 'when username and password are provided' do context 'when username and password are provided' do
it 'rejects pulls with 2FA error message' do it 'rejects pulls with personal access token error message' do
download(path, user: user.username, password: user.password) do |response| download(path, user: user.username, password: user.password) do |response|
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:unauthorized)
expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP') expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end end
end end
it 'rejects the push attempt' do it 'rejects the push attempt with personal access token error message' do
upload(path, user: user.username, password: user.password) do |response| upload(path, user: user.username, password: user.password) do |response|
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:unauthorized)
expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP') expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end end
end end
end end
...@@ -441,6 +441,41 @@ describe 'Git HTTP requests', lib: true do ...@@ -441,6 +441,41 @@ describe 'Git HTTP requests', lib: true do
end end
end end
context 'when internal auth is disabled' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
end
it 'rejects pulls with personal access token error message' do
download(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_http_status(:unauthorized)
expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
it 'rejects pushes with personal access token error message' do
upload(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_http_status(:unauthorized)
expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
context 'when LDAP is configured' do
before do
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
allow_any_instance_of(Gitlab::LDAP::Authentication).
to receive(:login).and_return(nil)
end
it 'does not display the personal access token error message' do
upload(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_http_status(:unauthorized)
expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
end
end
context "when blank password attempts follow a valid login" do context "when blank password attempts follow a valid login" do
def attempt_login(include_password) def attempt_login(include_password)
password = include_password ? user.password : "" password = include_password ? user.password : ""
......
...@@ -70,7 +70,7 @@ describe JwtController do ...@@ -70,7 +70,7 @@ describe JwtController do
context 'without personal token' do context 'without personal token' do
it 'rejects the authorization attempt' do it 'rejects the authorization attempt' do
expect(response).to have_http_status(401) 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') expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end end
end end
...@@ -88,9 +88,24 @@ describe JwtController do ...@@ -88,9 +88,24 @@ describe JwtController do
context 'using invalid login' do context 'using invalid login' do
let(:headers) { { authorization: credentials('invalid', 'password') } } let(:headers) { { authorization: credentials('invalid', 'password') } }
subject! { get '/jwt/auth', parameters, headers } context 'when internal auth is enabled' do
it 'rejects the authorization attempt' do
get '/jwt/auth', parameters, headers
expect(response).to have_http_status(401)
expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
it { expect(response).to have_http_status(401) } context 'when internal auth is disabled' do
it 'rejects the authorization attempt with personal access token message' do
allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
get '/jwt/auth', parameters, headers
expect(response).to have_http_status(401)
expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment