OSDN Git Service

diff view on commit with parallel diff view
[wvm/gitlab.git] / lib / gitlab / backend / grack_auth.rb
1 require_relative 'shell_env'
2 require_relative 'grack_helpers'
3
4 module Grack
5   class Auth < Rack::Auth::Basic
6     include Helpers
7
8     attr_accessor :user, :project, :ref, :env
9
10     def call(env)
11       @env = env
12       @request = Rack::Request.new(env)
13       @auth = Request.new(env)
14
15       # Need this patch due to the rails mount
16
17       # Need this if under RELATIVE_URL_ROOT
18       unless Gitlab.config.gitlab.relative_url_root.empty?
19         # If website is mounted using relative_url_root need to remove it first
20         @env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root,'')
21       else
22         @env['PATH_INFO'] = @request.path
23       end
24
25       @env['SCRIPT_NAME'] = ""
26
27       auth!
28     end
29
30     private
31
32     def auth!
33       return render_not_found unless project
34
35       if @auth.provided?
36         return bad_request unless @auth.basic?
37
38         # Authentication with username and password
39         login, password = @auth.credentials
40
41         # Allow authentication for GitLab CI service
42         # if valid token passed
43         if login == "gitlab-ci-token" && project.gitlab_ci?
44           token = project.gitlab_ci_service.token
45
46           if token.present? && token == password && service_name == 'git-upload-pack'
47             return @app.call(env)
48           end
49         end
50
51         @user = authenticate_user(login, password)
52
53         if @user
54           Gitlab::ShellEnv.set_env(@user)
55           @env['REMOTE_USER'] = @auth.username
56         else
57           return unauthorized
58         end
59
60       else
61         return unauthorized unless project.public
62       end
63
64       if authorized_git_request?
65         @app.call(env)
66       else
67         unauthorized
68       end
69     end
70
71     def authorized_git_request?
72       authorize_request(service_name)
73     end
74
75     def authenticate_user(login, password)
76       auth = Gitlab::Auth.new
77       auth.find(login, password)
78     end
79
80     def authorize_request(service)
81       case service
82       when 'git-upload-pack'
83         project.public || can?(user, :download_code, project)
84       when'git-receive-pack'
85         action = if project.protected_branch?(ref)
86                    :push_code_to_protected_branches
87                  else
88                    :push_code
89                  end
90
91         can?(user, action, project)
92       else
93         false
94       end
95     end
96
97     def service_name
98       if @request.get?
99         @request.params['service']
100       elsif @request.post?
101         File.basename(@request.path)
102       else
103         nil
104       end
105     end
106
107     def project
108       @project ||= project_by_path(@request.path_info)
109     end
110
111     def ref
112       @ref ||= parse_ref
113     end
114
115     def parse_ref
116       input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
117                 Zlib::GzipReader.new(@request.body).read
118               else
119                 @request.body.read
120               end
121
122       # Need to reset seek point
123       @request.body.rewind
124       /refs\/heads\/([\/\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
125     end
126   end
127 end