Commit 4bcad1cb authored by Jacob Vosmaer's avatar Jacob Vosmaer

Groundwork for Kerberos SPNEGO (EE feature)

parent 2efee5f6
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpController < Projects::ApplicationController class Projects::GitHttpController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
attr_reader :user attr_reader :user
# Git clients will not know what authenticity token to send along # Git clients will not know what authenticity token to send along
...@@ -40,9 +45,12 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -40,9 +45,12 @@ class Projects::GitHttpController < Projects::ApplicationController
private private
def authenticate_user def authenticate_user
return if project && project.public? && upload_pack? if project && project.public? && upload_pack?
return # Allow access
end
authenticate_or_request_with_http_basic do |login, password| if allow_basic_auth? && basic_auth_provided?
login, password = user_name_and_password(request)
auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip) auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
if auth_result.type == :ci && upload_pack? if auth_result.type == :ci && upload_pack?
...@@ -53,8 +61,31 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -53,8 +61,31 @@ class Projects::GitHttpController < Projects::ApplicationController
@user = auth_result.user @user = auth_result.user
end end
ci? || user if ci? || user
return # Allow access
end
elsif allow_kerberos_spnego_auth? && spnego_provided?
@user = find_kerberos_user
if user
send_final_spnego_response
return # Allow access
end
end
send_challenges
render plain: "HTTP Basic: Access denied\n", status: 401
end end
def basic_auth_provided?
has_basic_credentials?(request)
end
def send_challenges
challenges = []
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
challenges << spnego_challenge if allow_kerberos_spnego_auth?
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end end
def ensure_project_found! def ensure_project_found!
...@@ -120,7 +151,7 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -120,7 +151,7 @@ class Projects::GitHttpController < Projects::ApplicationController
end end
def render_not_found def render_not_found
render text: 'Not Found', status: :not_found render plain: 'Not Found', status: :not_found
end end
def ci? def ci?
......
module KerberosSpnegoHelper
def allow_basic_auth?
true # different behavior in GitLab Enterprise Edition
end
def allow_kerberos_spnego_auth?
false # different behavior in GitLab Enterprise Edition
end
end
...@@ -350,23 +350,23 @@ describe 'Git HTTP requests', lib: true do ...@@ -350,23 +350,23 @@ describe 'Git HTTP requests', lib: true do
end end
def clone_get(project, options={}) def clone_get(project, options={})
get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password)) get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def clone_post(project, options={}) def clone_post(project, options={})
post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password)) post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def push_get(project, options={}) def push_get(project, options={})
get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password)) get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def push_post(project, options={}) def push_post(project, options={})
post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password)) post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def download(project, user: nil, password: nil) def download(project, user: nil, password: nil, spnego_request_token: nil)
args = [project, { user: user, password: password }] args = [project, { user: user, password: password, spnego_request_token: spnego_request_token }]
clone_get(*args) clone_get(*args)
yield response yield response
...@@ -375,8 +375,8 @@ describe 'Git HTTP requests', lib: true do ...@@ -375,8 +375,8 @@ describe 'Git HTTP requests', lib: true do
yield response yield response
end end
def upload(project, user: nil, password: nil) def upload(project, user: nil, password: nil, spnego_request_token: nil)
args = [project, { user: user, password: password }] args = [project, { user: user, password: password, spnego_request_token: spnego_request_token }]
push_get(*args) push_get(*args)
yield response yield response
...@@ -385,11 +385,14 @@ describe 'Git HTTP requests', lib: true do ...@@ -385,11 +385,14 @@ describe 'Git HTTP requests', lib: true do
yield response yield response
end end
def auth_env(user, password) def auth_env(user, password, spnego_request_token)
env = {}
if user && password if user && password
{ 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password) } env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user, password)
else elsif spnego_request_token
{} env['HTTP_AUTHORIZATION'] = "Negotiate #{::Base64.strict_encode64('opaque_request_token')}"
end end
env
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