##// END OF EJS Templates
git+hg middleware do repo verification at earliest possible state, giving 404 as fast as possible. If repo is not found.
marcink -
r2122:c137b8a8 beta
parent child Browse files
Show More
@@ -1,251 +1,251 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplegit
3 rhodecode.lib.middleware.simplegit
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 It's implemented with basic auth function
7 It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import re
28 import re
29 import logging
29 import logging
30 import traceback
30 import traceback
31
31
32 from dulwich import server as dulserver
32 from dulwich import server as dulserver
33
33
34
34
35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36
36
37 def handle(self):
37 def handle(self):
38 write = lambda x: self.proto.write_sideband(1, x)
38 write = lambda x: self.proto.write_sideband(1, x)
39
39
40 graph_walker = dulserver.ProtocolGraphWalker(self,
40 graph_walker = dulserver.ProtocolGraphWalker(self,
41 self.repo.object_store,
41 self.repo.object_store,
42 self.repo.get_peeled)
42 self.repo.get_peeled)
43 objects_iter = self.repo.fetch_objects(
43 objects_iter = self.repo.fetch_objects(
44 graph_walker.determine_wants, graph_walker, self.progress,
44 graph_walker.determine_wants, graph_walker, self.progress,
45 get_tagged=self.get_tagged)
45 get_tagged=self.get_tagged)
46
46
47 # Do they want any objects?
47 # Do they want any objects?
48 if objects_iter is None or len(objects_iter) == 0:
48 if objects_iter is None or len(objects_iter) == 0:
49 return
49 return
50
50
51 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 self.progress("counting objects: %d, done.\n" % len(objects_iter))
52 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
53 objects_iter, len(objects_iter))
53 objects_iter, len(objects_iter))
54 messages = []
54 messages = []
55 messages.append('thank you for using rhodecode')
55 messages.append('thank you for using rhodecode')
56
56
57 for msg in messages:
57 for msg in messages:
58 self.progress(msg + "\n")
58 self.progress(msg + "\n")
59 # we are done
59 # we are done
60 self.proto.write("0000")
60 self.proto.write("0000")
61
61
62 dulserver.DEFAULT_HANDLERS = {
62 dulserver.DEFAULT_HANDLERS = {
63 'git-upload-pack': SimpleGitUploadPackHandler,
63 'git-upload-pack': SimpleGitUploadPackHandler,
64 'git-receive-pack': dulserver.ReceivePackHandler,
64 'git-receive-pack': dulserver.ReceivePackHandler,
65 }
65 }
66
66
67 from dulwich.repo import Repo
67 from dulwich.repo import Repo
68 from dulwich.web import HTTPGitApplication
68 from dulwich.web import HTTPGitApplication
69
69
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
71
71
72 from rhodecode.lib.utils2 import safe_str
72 from rhodecode.lib.utils2 import safe_str
73 from rhodecode.lib.base import BaseVCSController
73 from rhodecode.lib.base import BaseVCSController
74 from rhodecode.lib.auth import get_container_username
74 from rhodecode.lib.auth import get_container_username
75 from rhodecode.lib.utils import is_valid_repo
75 from rhodecode.lib.utils import is_valid_repo
76 from rhodecode.model.db import User
76 from rhodecode.model.db import User
77
77
78 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
78 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
79
79
80 log = logging.getLogger(__name__)
80 log = logging.getLogger(__name__)
81
81
82
82
83 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
83 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
84
84
85
85
86 def is_git(environ):
86 def is_git(environ):
87 path_info = environ['PATH_INFO']
87 path_info = environ['PATH_INFO']
88 isgit_path = GIT_PROTO_PAT.match(path_info)
88 isgit_path = GIT_PROTO_PAT.match(path_info)
89 log.debug('pathinfo: %s detected as GIT %s' % (
89 log.debug('pathinfo: %s detected as GIT %s' % (
90 path_info, isgit_path != None)
90 path_info, isgit_path != None)
91 )
91 )
92 return isgit_path
92 return isgit_path
93
93
94
94
95 class SimpleGit(BaseVCSController):
95 class SimpleGit(BaseVCSController):
96
96
97 def _handle_request(self, environ, start_response):
97 def _handle_request(self, environ, start_response):
98
98
99 if not is_git(environ):
99 if not is_git(environ):
100 return self.application(environ, start_response)
100 return self.application(environ, start_response)
101
101
102 proxy_key = 'HTTP_X_REAL_IP'
102 proxy_key = 'HTTP_X_REAL_IP'
103 def_key = 'REMOTE_ADDR'
103 def_key = 'REMOTE_ADDR'
104 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
104 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
105 username = None
105 username = None
106 # skip passing error to error controller
106 # skip passing error to error controller
107 environ['pylons.status_code_redirect'] = True
107 environ['pylons.status_code_redirect'] = True
108
108
109 #======================================================================
109 #======================================================================
110 # EXTRACT REPOSITORY NAME FROM ENV
110 # EXTRACT REPOSITORY NAME FROM ENV
111 #======================================================================
111 #======================================================================
112 try:
112 try:
113 repo_name = self.__get_repository(environ)
113 repo_name = self.__get_repository(environ)
114 log.debug('Extracted repo name is %s' % repo_name)
114 log.debug('Extracted repo name is %s' % repo_name)
115 except:
115 except:
116 return HTTPInternalServerError()(environ, start_response)
116 return HTTPInternalServerError()(environ, start_response)
117
117
118 # quick check if that dir exists...
119 if is_valid_repo(repo_name, self.basepath) is False:
120 return HTTPNotFound()(environ, start_response)
121
118 #======================================================================
122 #======================================================================
119 # GET ACTION PULL or PUSH
123 # GET ACTION PULL or PUSH
120 #======================================================================
124 #======================================================================
121 action = self.__get_action(environ)
125 action = self.__get_action(environ)
122
126
123 #======================================================================
127 #======================================================================
124 # CHECK ANONYMOUS PERMISSION
128 # CHECK ANONYMOUS PERMISSION
125 #======================================================================
129 #======================================================================
126 if action in ['pull', 'push']:
130 if action in ['pull', 'push']:
127 anonymous_user = self.__get_user('default')
131 anonymous_user = self.__get_user('default')
128 username = anonymous_user.username
132 username = anonymous_user.username
129 anonymous_perm = self._check_permission(action, anonymous_user,
133 anonymous_perm = self._check_permission(action, anonymous_user,
130 repo_name)
134 repo_name)
131
135
132 if anonymous_perm is not True or anonymous_user.active is False:
136 if anonymous_perm is not True or anonymous_user.active is False:
133 if anonymous_perm is not True:
137 if anonymous_perm is not True:
134 log.debug('Not enough credentials to access this '
138 log.debug('Not enough credentials to access this '
135 'repository as anonymous user')
139 'repository as anonymous user')
136 if anonymous_user.active is False:
140 if anonymous_user.active is False:
137 log.debug('Anonymous access is disabled, running '
141 log.debug('Anonymous access is disabled, running '
138 'authentication')
142 'authentication')
139 #==============================================================
143 #==============================================================
140 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
144 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
141 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
145 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
142 #==============================================================
146 #==============================================================
143
147
144 # Attempting to retrieve username from the container
148 # Attempting to retrieve username from the container
145 username = get_container_username(environ, self.config)
149 username = get_container_username(environ, self.config)
146
150
147 # If not authenticated by the container, running basic auth
151 # If not authenticated by the container, running basic auth
148 if not username:
152 if not username:
149 self.authenticate.realm = \
153 self.authenticate.realm = \
150 safe_str(self.config['rhodecode_realm'])
154 safe_str(self.config['rhodecode_realm'])
151 result = self.authenticate(environ)
155 result = self.authenticate(environ)
152 if isinstance(result, str):
156 if isinstance(result, str):
153 AUTH_TYPE.update(environ, 'basic')
157 AUTH_TYPE.update(environ, 'basic')
154 REMOTE_USER.update(environ, result)
158 REMOTE_USER.update(environ, result)
155 username = result
159 username = result
156 else:
160 else:
157 return result.wsgi_application(environ, start_response)
161 return result.wsgi_application(environ, start_response)
158
162
159 #==============================================================
163 #==============================================================
160 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
164 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
161 #==============================================================
165 #==============================================================
162 if action in ['pull', 'push']:
166 if action in ['pull', 'push']:
163 try:
167 try:
164 user = self.__get_user(username)
168 user = self.__get_user(username)
165 if user is None or not user.active:
169 if user is None or not user.active:
166 return HTTPForbidden()(environ, start_response)
170 return HTTPForbidden()(environ, start_response)
167 username = user.username
171 username = user.username
168 except:
172 except:
169 log.error(traceback.format_exc())
173 log.error(traceback.format_exc())
170 return HTTPInternalServerError()(environ,
174 return HTTPInternalServerError()(environ,
171 start_response)
175 start_response)
172
176
173 #check permissions for this repository
177 #check permissions for this repository
174 perm = self._check_permission(action, user, repo_name)
178 perm = self._check_permission(action, user, repo_name)
175 if perm is not True:
179 if perm is not True:
176 return HTTPForbidden()(environ, start_response)
180 return HTTPForbidden()(environ, start_response)
177
181
178 #===================================================================
182 #===================================================================
179 # GIT REQUEST HANDLING
183 # GIT REQUEST HANDLING
180 #===================================================================
184 #===================================================================
181 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
185 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
182 log.debug('Repository path is %s' % repo_path)
186 log.debug('Repository path is %s' % repo_path)
183
187
184 # quick check if that dir exists...
185 if is_valid_repo(repo_name, self.basepath) is False:
186 return HTTPNotFound()(environ, start_response)
187
188 try:
188 try:
189 #invalidate cache on push
189 #invalidate cache on push
190 if action == 'push':
190 if action == 'push':
191 self._invalidate_cache(repo_name)
191 self._invalidate_cache(repo_name)
192 log.info('%s action on GIT repo "%s"' % (action, repo_name))
192 log.info('%s action on GIT repo "%s"' % (action, repo_name))
193 app = self.__make_app(repo_name, repo_path)
193 app = self.__make_app(repo_name, repo_path)
194 return app(environ, start_response)
194 return app(environ, start_response)
195 except Exception:
195 except Exception:
196 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
197 return HTTPInternalServerError()(environ, start_response)
197 return HTTPInternalServerError()(environ, start_response)
198
198
199 def __make_app(self, repo_name, repo_path):
199 def __make_app(self, repo_name, repo_path):
200 """
200 """
201 Make an wsgi application using dulserver
201 Make an wsgi application using dulserver
202
202
203 :param repo_name: name of the repository
203 :param repo_name: name of the repository
204 :param repo_path: full path to the repository
204 :param repo_path: full path to the repository
205 """
205 """
206 _d = {'/' + repo_name: Repo(repo_path)}
206 _d = {'/' + repo_name: Repo(repo_path)}
207 backend = dulserver.DictBackend(_d)
207 backend = dulserver.DictBackend(_d)
208 gitserve = HTTPGitApplication(backend)
208 gitserve = HTTPGitApplication(backend)
209
209
210 return gitserve
210 return gitserve
211
211
212 def __get_repository(self, environ):
212 def __get_repository(self, environ):
213 """
213 """
214 Get's repository name out of PATH_INFO header
214 Get's repository name out of PATH_INFO header
215
215
216 :param environ: environ where PATH_INFO is stored
216 :param environ: environ where PATH_INFO is stored
217 """
217 """
218 try:
218 try:
219 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
219 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
220 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
220 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
221 except:
221 except:
222 log.error(traceback.format_exc())
222 log.error(traceback.format_exc())
223 raise
223 raise
224
224
225 return repo_name
225 return repo_name
226
226
227 def __get_user(self, username):
227 def __get_user(self, username):
228 return User.get_by_username(username)
228 return User.get_by_username(username)
229
229
230 def __get_action(self, environ):
230 def __get_action(self, environ):
231 """
231 """
232 Maps git request commands into a pull or push command.
232 Maps git request commands into a pull or push command.
233
233
234 :param environ:
234 :param environ:
235 """
235 """
236 service = environ['QUERY_STRING'].split('=')
236 service = environ['QUERY_STRING'].split('=')
237
237
238 if len(service) > 1:
238 if len(service) > 1:
239 service_cmd = service[1]
239 service_cmd = service[1]
240 mapping = {
240 mapping = {
241 'git-receive-pack': 'push',
241 'git-receive-pack': 'push',
242 'git-upload-pack': 'pull',
242 'git-upload-pack': 'pull',
243 }
243 }
244 op = mapping[service_cmd]
244 op = mapping[service_cmd]
245 self._git_stored_op = op
245 self._git_stored_op = op
246 return op
246 return op
247 else:
247 else:
248 # try to fallback to stored variable as we don't know if the last
248 # try to fallback to stored variable as we don't know if the last
249 # operation is pull/push
249 # operation is pull/push
250 op = getattr(self, '_git_stored_op', 'pull')
250 op = getattr(self, '_git_stored_op', 'pull')
251 return op
251 return op
@@ -1,258 +1,258 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplehg
3 rhodecode.lib.middleware.simplehg
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleHG middleware for handling mercurial protocol request
6 SimpleHG middleware for handling mercurial protocol request
7 (push/clone etc.). It's implemented with basic auth function
7 (push/clone etc.). It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import urllib
30 import urllib
31
31
32 from mercurial.error import RepoError
32 from mercurial.error import RepoError
33 from mercurial.hgweb import hgweb_mod
33 from mercurial.hgweb import hgweb_mod
34
34
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36
36
37 from rhodecode.lib.utils2 import safe_str
37 from rhodecode.lib.utils2 import safe_str
38 from rhodecode.lib.base import BaseVCSController
38 from rhodecode.lib.base import BaseVCSController
39 from rhodecode.lib.auth import get_container_username
39 from rhodecode.lib.auth import get_container_username
40 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
40 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
41 from rhodecode.model.db import User
41 from rhodecode.model.db import User
42
42
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 def is_mercurial(environ):
48 def is_mercurial(environ):
49 """
49 """
50 Returns True if request's target is mercurial server - header
50 Returns True if request's target is mercurial server - header
51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 """
52 """
53 http_accept = environ.get('HTTP_ACCEPT')
53 http_accept = environ.get('HTTP_ACCEPT')
54 path_info = environ['PATH_INFO']
54 path_info = environ['PATH_INFO']
55 if http_accept and http_accept.startswith('application/mercurial'):
55 if http_accept and http_accept.startswith('application/mercurial'):
56 ishg_path = True
56 ishg_path = True
57 else:
57 else:
58 ishg_path = False
58 ishg_path = False
59
59
60 log.debug('pathinfo: %s detected as HG %s' % (
60 log.debug('pathinfo: %s detected as HG %s' % (
61 path_info, ishg_path)
61 path_info, ishg_path)
62 )
62 )
63 return ishg_path
63 return ishg_path
64
64
65
65
66 class SimpleHg(BaseVCSController):
66 class SimpleHg(BaseVCSController):
67
67
68 def _handle_request(self, environ, start_response):
68 def _handle_request(self, environ, start_response):
69 if not is_mercurial(environ):
69 if not is_mercurial(environ):
70 return self.application(environ, start_response)
70 return self.application(environ, start_response)
71
71
72 proxy_key = 'HTTP_X_REAL_IP'
72 proxy_key = 'HTTP_X_REAL_IP'
73 def_key = 'REMOTE_ADDR'
73 def_key = 'REMOTE_ADDR'
74 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
74 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
75
75
76 # skip passing error to error controller
76 # skip passing error to error controller
77 environ['pylons.status_code_redirect'] = True
77 environ['pylons.status_code_redirect'] = True
78
78
79 #======================================================================
79 #======================================================================
80 # EXTRACT REPOSITORY NAME FROM ENV
80 # EXTRACT REPOSITORY NAME FROM ENV
81 #======================================================================
81 #======================================================================
82 try:
82 try:
83 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
83 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
84 log.debug('Extracted repo name is %s' % repo_name)
84 log.debug('Extracted repo name is %s' % repo_name)
85 except:
85 except:
86 return HTTPInternalServerError()(environ, start_response)
86 return HTTPInternalServerError()(environ, start_response)
87
87
88 # quick check if that dir exists...
89 if is_valid_repo(repo_name, self.basepath) is False:
90 return HTTPNotFound()(environ, start_response)
91
88 #======================================================================
92 #======================================================================
89 # GET ACTION PULL or PUSH
93 # GET ACTION PULL or PUSH
90 #======================================================================
94 #======================================================================
91 action = self.__get_action(environ)
95 action = self.__get_action(environ)
92
96
93 #======================================================================
97 #======================================================================
94 # CHECK ANONYMOUS PERMISSION
98 # CHECK ANONYMOUS PERMISSION
95 #======================================================================
99 #======================================================================
96 if action in ['pull', 'push']:
100 if action in ['pull', 'push']:
97 anonymous_user = self.__get_user('default')
101 anonymous_user = self.__get_user('default')
98 username = anonymous_user.username
102 username = anonymous_user.username
99 anonymous_perm = self._check_permission(action, anonymous_user,
103 anonymous_perm = self._check_permission(action, anonymous_user,
100 repo_name)
104 repo_name)
101
105
102 if anonymous_perm is not True or anonymous_user.active is False:
106 if anonymous_perm is not True or anonymous_user.active is False:
103 if anonymous_perm is not True:
107 if anonymous_perm is not True:
104 log.debug('Not enough credentials to access this '
108 log.debug('Not enough credentials to access this '
105 'repository as anonymous user')
109 'repository as anonymous user')
106 if anonymous_user.active is False:
110 if anonymous_user.active is False:
107 log.debug('Anonymous access is disabled, running '
111 log.debug('Anonymous access is disabled, running '
108 'authentication')
112 'authentication')
109 #==============================================================
113 #==============================================================
110 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
114 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
111 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
115 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
112 #==============================================================
116 #==============================================================
113
117
114 # Attempting to retrieve username from the container
118 # Attempting to retrieve username from the container
115 username = get_container_username(environ, self.config)
119 username = get_container_username(environ, self.config)
116
120
117 # If not authenticated by the container, running basic auth
121 # If not authenticated by the container, running basic auth
118 if not username:
122 if not username:
119 self.authenticate.realm = \
123 self.authenticate.realm = \
120 safe_str(self.config['rhodecode_realm'])
124 safe_str(self.config['rhodecode_realm'])
121 result = self.authenticate(environ)
125 result = self.authenticate(environ)
122 if isinstance(result, str):
126 if isinstance(result, str):
123 AUTH_TYPE.update(environ, 'basic')
127 AUTH_TYPE.update(environ, 'basic')
124 REMOTE_USER.update(environ, result)
128 REMOTE_USER.update(environ, result)
125 username = result
129 username = result
126 else:
130 else:
127 return result.wsgi_application(environ, start_response)
131 return result.wsgi_application(environ, start_response)
128
132
129 #==============================================================
133 #==============================================================
130 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
134 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
131 #==============================================================
135 #==============================================================
132 if action in ['pull', 'push']:
136 if action in ['pull', 'push']:
133 try:
137 try:
134 user = self.__get_user(username)
138 user = self.__get_user(username)
135 if user is None or not user.active:
139 if user is None or not user.active:
136 return HTTPForbidden()(environ, start_response)
140 return HTTPForbidden()(environ, start_response)
137 username = user.username
141 username = user.username
138 except:
142 except:
139 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
140 return HTTPInternalServerError()(environ,
144 return HTTPInternalServerError()(environ,
141 start_response)
145 start_response)
142
146
143 #check permissions for this repository
147 #check permissions for this repository
144 perm = self._check_permission(action, user, repo_name)
148 perm = self._check_permission(action, user, repo_name)
145 if perm is not True:
149 if perm is not True:
146 return HTTPForbidden()(environ, start_response)
150 return HTTPForbidden()(environ, start_response)
147
151
148 # extras are injected into mercurial UI object and later available
152 # extras are injected into mercurial UI object and later available
149 # in hg hooks executed by rhodecode
153 # in hg hooks executed by rhodecode
150 extras = {
154 extras = {
151 'ip': ipaddr,
155 'ip': ipaddr,
152 'username': username,
156 'username': username,
153 'action': action,
157 'action': action,
154 'repository': repo_name
158 'repository': repo_name
155 }
159 }
156
160
157 #======================================================================
161 #======================================================================
158 # MERCURIAL REQUEST HANDLING
162 # MERCURIAL REQUEST HANDLING
159 #======================================================================
163 #======================================================================
160 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
164 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
161 log.debug('Repository path is %s' % repo_path)
165 log.debug('Repository path is %s' % repo_path)
162
166
163 baseui = make_ui('db')
167 baseui = make_ui('db')
164 self.__inject_extras(repo_path, baseui, extras)
168 self.__inject_extras(repo_path, baseui, extras)
165
169
166 # quick check if that dir exists...
167 if is_valid_repo(repo_name, self.basepath) is False:
168 return HTTPNotFound()(environ, start_response)
169
170 try:
170 try:
171 # invalidate cache on push
171 # invalidate cache on push
172 if action == 'push':
172 if action == 'push':
173 self._invalidate_cache(repo_name)
173 self._invalidate_cache(repo_name)
174 log.info('%s action on HG repo "%s"' % (action, repo_name))
174 log.info('%s action on HG repo "%s"' % (action, repo_name))
175 app = self.__make_app(repo_path, baseui, extras)
175 app = self.__make_app(repo_path, baseui, extras)
176 return app(environ, start_response)
176 return app(environ, start_response)
177 except RepoError, e:
177 except RepoError, e:
178 if str(e).find('not found') != -1:
178 if str(e).find('not found') != -1:
179 return HTTPNotFound()(environ, start_response)
179 return HTTPNotFound()(environ, start_response)
180 except Exception:
180 except Exception:
181 log.error(traceback.format_exc())
181 log.error(traceback.format_exc())
182 return HTTPInternalServerError()(environ, start_response)
182 return HTTPInternalServerError()(environ, start_response)
183
183
184 def __make_app(self, repo_name, baseui, extras):
184 def __make_app(self, repo_name, baseui, extras):
185 """
185 """
186 Make an wsgi application using hgweb, and inject generated baseui
186 Make an wsgi application using hgweb, and inject generated baseui
187 instance, additionally inject some extras into ui object
187 instance, additionally inject some extras into ui object
188 """
188 """
189 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
189 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
190
190
191 def __get_repository(self, environ):
191 def __get_repository(self, environ):
192 """
192 """
193 Get's repository name out of PATH_INFO header
193 Get's repository name out of PATH_INFO header
194
194
195 :param environ: environ where PATH_INFO is stored
195 :param environ: environ where PATH_INFO is stored
196 """
196 """
197 try:
197 try:
198 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
198 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
199 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
199 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
200 if repo_name.endswith('/'):
200 if repo_name.endswith('/'):
201 repo_name = repo_name.rstrip('/')
201 repo_name = repo_name.rstrip('/')
202 except:
202 except:
203 log.error(traceback.format_exc())
203 log.error(traceback.format_exc())
204 raise
204 raise
205
205
206 return repo_name
206 return repo_name
207
207
208 def __get_user(self, username):
208 def __get_user(self, username):
209 return User.get_by_username(username)
209 return User.get_by_username(username)
210
210
211 def __get_action(self, environ):
211 def __get_action(self, environ):
212 """
212 """
213 Maps mercurial request commands into a clone,pull or push command.
213 Maps mercurial request commands into a clone,pull or push command.
214 This should always return a valid command string
214 This should always return a valid command string
215
215
216 :param environ:
216 :param environ:
217 """
217 """
218 mapping = {'changegroup': 'pull',
218 mapping = {'changegroup': 'pull',
219 'changegroupsubset': 'pull',
219 'changegroupsubset': 'pull',
220 'stream_out': 'pull',
220 'stream_out': 'pull',
221 'listkeys': 'pull',
221 'listkeys': 'pull',
222 'unbundle': 'push',
222 'unbundle': 'push',
223 'pushkey': 'push', }
223 'pushkey': 'push', }
224 for qry in environ['QUERY_STRING'].split('&'):
224 for qry in environ['QUERY_STRING'].split('&'):
225 if qry.startswith('cmd'):
225 if qry.startswith('cmd'):
226 cmd = qry.split('=')[-1]
226 cmd = qry.split('=')[-1]
227 if cmd in mapping:
227 if cmd in mapping:
228 return mapping[cmd]
228 return mapping[cmd]
229 else:
229 else:
230 return 'pull'
230 return 'pull'
231
231
232 def __inject_extras(self, repo_path, baseui, extras={}):
232 def __inject_extras(self, repo_path, baseui, extras={}):
233 """
233 """
234 Injects some extra params into baseui instance
234 Injects some extra params into baseui instance
235
235
236 also overwrites global settings with those takes from local hgrc file
236 also overwrites global settings with those takes from local hgrc file
237
237
238 :param baseui: baseui instance
238 :param baseui: baseui instance
239 :param extras: dict with extra params to put into baseui
239 :param extras: dict with extra params to put into baseui
240 """
240 """
241
241
242 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
242 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
243
243
244 # make our hgweb quiet so it doesn't print output
244 # make our hgweb quiet so it doesn't print output
245 baseui.setconfig('ui', 'quiet', 'true')
245 baseui.setconfig('ui', 'quiet', 'true')
246
246
247 #inject some additional parameters that will be available in ui
247 #inject some additional parameters that will be available in ui
248 #for hooks
248 #for hooks
249 for k, v in extras.items():
249 for k, v in extras.items():
250 baseui.setconfig('rhodecode_extras', k, v)
250 baseui.setconfig('rhodecode_extras', k, v)
251
251
252 repoui = make_ui('file', hgrc, False)
252 repoui = make_ui('file', hgrc, False)
253
253
254 if repoui:
254 if repoui:
255 #overwrite our ui instance with the section from hgrc file
255 #overwrite our ui instance with the section from hgrc file
256 for section in ui_sections:
256 for section in ui_sections:
257 for k, v in repoui.configitems(section):
257 for k, v in repoui.configitems(section):
258 baseui.setconfig(section, k, v)
258 baseui.setconfig(section, k, v)
General Comments 0
You need to be logged in to leave comments. Login now