2fdda74458
This makes sure that we always have a repository type when trying to parse a repository from a path. This is needed because sometimes we want to perform access checks as if the project already existed, for example when creating a project on push. Before this we were only doing that when accessing git over http, this makes sure it also works correctly when accessing git over SSH
115 lines
3.2 KiB
Ruby
115 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# This file should be identical in GitLab Community Edition and Enterprise Edition
|
|
|
|
class Projects::GitHttpClientController < Projects::ApplicationController
|
|
include ActionController::HttpAuthentication::Basic
|
|
include KerberosSpnegoHelper
|
|
|
|
attr_reader :authentication_result, :redirected_path
|
|
|
|
delegate :actor, :authentication_abilities, to: :authentication_result, allow_nil: true
|
|
delegate :type, to: :authentication_result, allow_nil: true, prefix: :auth_result
|
|
|
|
alias_method :user, :actor
|
|
alias_method :authenticated_user, :actor
|
|
|
|
# Git clients will not know what authenticity token to send along
|
|
skip_before_action :verify_authenticity_token
|
|
skip_before_action :repository
|
|
before_action :authenticate_user
|
|
|
|
private
|
|
|
|
def download_request?
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def upload_request?
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def authenticate_user
|
|
@authentication_result = Gitlab::Auth::Result.new
|
|
|
|
if allow_basic_auth? && basic_auth_provided?
|
|
login, password = user_name_and_password(request)
|
|
|
|
if handle_basic_authentication(login, password)
|
|
return # Allow access
|
|
end
|
|
elsif allow_kerberos_spnego_auth? && spnego_provided?
|
|
kerberos_user = find_kerberos_user
|
|
|
|
if kerberos_user
|
|
@authentication_result = Gitlab::Auth::Result.new(
|
|
kerberos_user, nil, :kerberos, Gitlab::Auth.full_authentication_abilities)
|
|
|
|
send_final_spnego_response
|
|
return # Allow access
|
|
end
|
|
elsif project && download_request? && Guest.can?(:download_code, project)
|
|
@authentication_result = Gitlab::Auth::Result.new(nil, project, :none, [:download_code])
|
|
|
|
return # Allow access
|
|
end
|
|
|
|
send_challenges
|
|
render plain: "HTTP Basic: Access denied\n", status: :unauthorized
|
|
rescue Gitlab::Auth::MissingPersonalAccessTokenError
|
|
render_missing_personal_access_token
|
|
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
|
|
|
|
def project
|
|
parse_repo_path unless defined?(@project)
|
|
|
|
@project
|
|
end
|
|
|
|
def parse_repo_path
|
|
@project, @repo_type, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
|
|
end
|
|
|
|
def render_missing_personal_access_token
|
|
render plain: "HTTP Basic: Access denied\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}",
|
|
status: :unauthorized
|
|
end
|
|
|
|
def repository
|
|
repo_type.repository_for(project)
|
|
end
|
|
|
|
def wiki?
|
|
repo_type.wiki?
|
|
end
|
|
|
|
def repo_type
|
|
parse_repo_path unless defined?(@repo_type)
|
|
|
|
@repo_type
|
|
end
|
|
|
|
def handle_basic_authentication(login, password)
|
|
@authentication_result = Gitlab::Auth.find_for_git_client(
|
|
login, password, project: project, ip: request.ip)
|
|
|
|
@authentication_result.success?
|
|
end
|
|
|
|
def ci?
|
|
authentication_result.ci?(project)
|
|
end
|
|
end
|