Commit b4e8fea274ff18717391e1c220342d82b965a22f

Authored by Dmitriy Zaporozhets
1 parent a8bcb9a5

Refactor grack auth module. Add git over http wiki support

lib/gitlab/backend/grack_auth.rb
1 require_relative 'shell_env' 1 require_relative 'shell_env'
2 -require 'omniauth-ldap' 2 +require_relative 'grack_ldap'
  3 +require_relative 'grack_helpers'
3 4
4 module Grack 5 module Grack
5 class Auth < Rack::Auth::Basic 6 class Auth < Rack::Auth::Basic
6 - attr_accessor :user, :project 7 + include LDAP
  8 + include Helpers
  9 +
  10 + attr_accessor :user, :project, :ref, :env
7 11
8 def call(env) 12 def call(env)
9 @env = env 13 @env = env
@@ -14,42 +18,52 @@ module Grack @@ -14,42 +18,52 @@ module Grack
14 @env['PATH_INFO'] = @request.path 18 @env['PATH_INFO'] = @request.path
15 @env['SCRIPT_NAME'] = "" 19 @env['SCRIPT_NAME'] = ""
16 20
17 - return render_not_found unless project  
18 - return unauthorized unless project.public || @auth.provided?  
19 - return bad_request if @auth.provided? && !@auth.basic?  
20 -  
21 - if valid?  
22 - if @auth.provided?  
23 - @env['REMOTE_USER'] = @auth.username  
24 - end  
25 - return @app.call(env)  
26 - else  
27 - unauthorized  
28 - end 21 + auth!
29 end 22 end
30 23
31 - def valid? 24 + private
  25 +
  26 + def auth!
  27 + return render_not_found unless project
  28 +
32 if @auth.provided? 29 if @auth.provided?
  30 + return bad_request unless @auth.basic?
  31 +
33 # Authentication with username and password 32 # Authentication with username and password
34 login, password = @auth.credentials 33 login, password = @auth.credentials
35 34
36 - @user = authenticate(login, password)  
37 - return false unless @user 35 + @user = authenticate_user(login, password)
38 36
39 - Gitlab::ShellEnv.set_env(@user) 37 + if @user
  38 + Gitlab::ShellEnv.set_env(@user)
  39 + @env['REMOTE_USER'] = @auth.username
  40 + else
  41 + return unauthorized
  42 + end
  43 +
  44 + else
  45 + return unauthorized unless project.public
40 end 46 end
41 47
  48 + if authorized_git_request?
  49 + @app.call(env)
  50 + else
  51 + unauthorized
  52 + end
  53 + end
  54 +
  55 + def authorized_git_request?
42 # Git upload and receive 56 # Git upload and receive
43 if @request.get? 57 if @request.get?
44 - validate_get_request 58 + authorize_request(@request.params['service'])
45 elsif @request.post? 59 elsif @request.post?
46 - validate_post_request 60 + authorize_request(File.basename(@request.path))
47 else 61 else
48 false 62 false
49 end 63 end
50 end 64 end
51 65
52 - def authenticate(login, password) 66 + def authenticate_user(login, password)
53 user = User.find_by_email(login) || User.find_by_username(login) 67 user = User.find_by_email(login) || User.find_by_username(login)
54 68
55 # If the provided login was not a known email or username 69 # If the provided login was not a known email or username
@@ -65,34 +79,12 @@ module Grack @@ -65,34 +79,12 @@ module Grack
65 end 79 end
66 end 80 end
67 81
68 - def ldap_auth(login, password)  
69 - # Check user against LDAP backend if user is not authenticated  
70 - # Only check with valid login and password to prevent anonymous bind results  
71 - return nil unless ldap_conf.enabled && !login.blank? && !password.blank?  
72 -  
73 - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)  
74 - ldap_user = ldap.bind_as(  
75 - filter: Net::LDAP::Filter.eq(ldap.uid, login),  
76 - size: 1,  
77 - password: password  
78 - )  
79 -  
80 - User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user  
81 - end  
82 -  
83 - def validate_get_request  
84 - validate_request(@request.params['service'])  
85 - end  
86 -  
87 - def validate_post_request  
88 - validate_request(File.basename(@request.path))  
89 - end  
90 -  
91 - def validate_request(service)  
92 - if service == 'git-upload-pack' 82 + def authorize_request(service)
  83 + case service
  84 + when 'git-upload-pack'
93 project.public || can?(user, :download_code, project) 85 project.public || can?(user, :download_code, project)
94 - elsif service == 'git-receive-pack'  
95 - action = if project.protected_branch?(current_ref) 86 + when'git-receive-pack'
  87 + action = if project.protected_branch?(ref)
96 :push_code_to_protected_branches 88 :push_code_to_protected_branches
97 else 89 else
98 :push_code 90 :push_code
@@ -104,49 +96,24 @@ module Grack @@ -104,49 +96,24 @@ module Grack
104 end 96 end
105 end 97 end
106 98
107 - def can?(object, action, subject)  
108 - abilities.allowed?(object, action, subject)  
109 - end  
110 -  
111 - def current_ref  
112 - if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/  
113 - input = Zlib::GzipReader.new(@request.body).read  
114 - else  
115 - input = @request.body.read  
116 - end  
117 - # Need to reset seek point  
118 - @request.body.rewind  
119 - /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last  
120 - end  
121 -  
122 def project 99 def project
123 - unless instance_variable_defined? :@project  
124 - # Find project by PATH_INFO from env  
125 - if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a  
126 - @project = Project.find_with_namespace(m.last)  
127 - end  
128 - end  
129 - return @project 100 + @project ||= project_by_path(@request.path_info)
130 end 101 end
131 102
132 - PLAIN_TYPE = {"Content-Type" => "text/plain"}  
133 -  
134 - def render_not_found  
135 - [404, PLAIN_TYPE, ["Not Found"]] 103 + def ref
  104 + @ref ||= parse_ref
136 end 105 end
137 106
138 - protected 107 + def parse_ref
  108 + input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
  109 + Zlib::GzipReader.new(@request.body).read
  110 + else
  111 + @request.body.read
  112 + end
139 113
140 - def abilities  
141 - @abilities ||= begin  
142 - abilities = Six.new  
143 - abilities << Ability  
144 - abilities  
145 - end  
146 - end  
147 -  
148 - def ldap_conf  
149 - @ldap_conf ||= Gitlab.config.ldap 114 + # Need to reset seek point
  115 + @request.body.rewind
  116 + /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
150 end 117 end
151 - end# Auth  
152 -end# Grack 118 + end
  119 +end
lib/gitlab/backend/grack_helpers.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +module Grack
  2 + module Helpers
  3 + def project_by_path(path)
  4 + if m = /^\/([\w\.\/-]+)\.git/.match(path).to_a
  5 + path_with_namespace = m.last
  6 + path_with_namespace.gsub!(/.wiki$/, '')
  7 +
  8 + Project.find_with_namespace(path_with_namespace)
  9 + end
  10 + end
  11 +
  12 + def render_not_found
  13 + [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
  14 + end
  15 +
  16 + def can?(object, action, subject)
  17 + abilities.allowed?(object, action, subject)
  18 + end
  19 +
  20 + def abilities
  21 + @abilities ||= begin
  22 + abilities = Six.new
  23 + abilities << Ability
  24 + abilities
  25 + end
  26 + end
  27 + end
  28 +end
lib/gitlab/backend/grack_ldap.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +require 'omniauth-ldap'
  2 +
  3 +module Grack
  4 + module LDAP
  5 + def ldap_auth(login, password)
  6 + # Check user against LDAP backend if user is not authenticated
  7 + # Only check with valid login and password to prevent anonymous bind results
  8 + return nil unless ldap_conf.enabled && !login.blank? && !password.blank?
  9 +
  10 + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
  11 + ldap_user = ldap.bind_as(
  12 + filter: Net::LDAP::Filter.eq(ldap.uid, login),
  13 + size: 1,
  14 + password: password
  15 + )
  16 +
  17 + User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user
  18 + end
  19 +
  20 + def ldap_conf
  21 + @ldap_conf ||= Gitlab.config.ldap
  22 + end
  23 + end
  24 +end