##// END OF EJS Templates
improved logging in git/hg middlewares
marcink -
r2026:c6e288dc beta
parent child Browse files
Show More
@@ -1,249 +1,248 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 logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from dulwich import server as dulserver
31 from dulwich import server as dulserver
32
32
33
33
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35
35
36 def handle(self):
36 def handle(self):
37 write = lambda x: self.proto.write_sideband(1, x)
37 write = lambda x: self.proto.write_sideband(1, x)
38
38
39 graph_walker = dulserver.ProtocolGraphWalker(self,
39 graph_walker = dulserver.ProtocolGraphWalker(self,
40 self.repo.object_store,
40 self.repo.object_store,
41 self.repo.get_peeled)
41 self.repo.get_peeled)
42 objects_iter = self.repo.fetch_objects(
42 objects_iter = self.repo.fetch_objects(
43 graph_walker.determine_wants, graph_walker, self.progress,
43 graph_walker.determine_wants, graph_walker, self.progress,
44 get_tagged=self.get_tagged)
44 get_tagged=self.get_tagged)
45
45
46 # Do they want any objects?
46 # Do they want any objects?
47 if objects_iter is None or len(objects_iter) == 0:
47 if objects_iter is None or len(objects_iter) == 0:
48 return
48 return
49
49
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 objects_iter, len(objects_iter))
52 objects_iter, len(objects_iter))
53 messages = []
53 messages = []
54 messages.append('thank you for using rhodecode')
54 messages.append('thank you for using rhodecode')
55
55
56 for msg in messages:
56 for msg in messages:
57 self.progress(msg + "\n")
57 self.progress(msg + "\n")
58 # we are done
58 # we are done
59 self.proto.write("0000")
59 self.proto.write("0000")
60
60
61 dulserver.DEFAULT_HANDLERS = {
61 dulserver.DEFAULT_HANDLERS = {
62 'git-upload-pack': SimpleGitUploadPackHandler,
62 'git-upload-pack': SimpleGitUploadPackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
64 }
64 }
65
65
66 from dulwich.repo import Repo
66 from dulwich.repo import Repo
67 from dulwich.web import HTTPGitApplication
67 from dulwich.web import HTTPGitApplication
68
68
69 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
69 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
70
70
71 from rhodecode.lib import safe_str
71 from rhodecode.lib import safe_str
72 from rhodecode.lib.base import BaseVCSController
72 from rhodecode.lib.base import BaseVCSController
73 from rhodecode.lib.auth import get_container_username
73 from rhodecode.lib.auth import get_container_username
74 from rhodecode.lib.utils import is_valid_repo
74 from rhodecode.lib.utils import is_valid_repo
75 from rhodecode.model.db import User
75 from rhodecode.model.db import User
76
76
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
78
78
79 log = logging.getLogger(__name__)
79 log = logging.getLogger(__name__)
80
80
81
81
82 def is_git(environ):
82 def is_git(environ):
83 """Returns True if request's target is git server.
83 """Returns True if request's target is git server.
84 ``HTTP_USER_AGENT`` would then have git client version given.
84 ``HTTP_USER_AGENT`` would then have git client version given.
85
85
86 :param environ:
86 :param environ:
87 """
87 """
88 http_user_agent = environ.get('HTTP_USER_AGENT')
88 http_user_agent = environ.get('HTTP_USER_AGENT')
89 if http_user_agent and http_user_agent.startswith('git'):
89 if http_user_agent and http_user_agent.startswith('git'):
90 return True
90 return True
91 return False
91 return False
92
92
93
93
94 class SimpleGit(BaseVCSController):
94 class SimpleGit(BaseVCSController):
95
95
96 def _handle_request(self, environ, start_response):
96 def _handle_request(self, environ, start_response):
97 if not is_git(environ):
97 if not is_git(environ):
98 return self.application(environ, start_response)
98 return self.application(environ, start_response)
99
99
100 proxy_key = 'HTTP_X_REAL_IP'
100 proxy_key = 'HTTP_X_REAL_IP'
101 def_key = 'REMOTE_ADDR'
101 def_key = 'REMOTE_ADDR'
102 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
102 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
103 username = None
103 username = None
104 # skip passing error to error controller
104 # skip passing error to error controller
105 environ['pylons.status_code_redirect'] = True
105 environ['pylons.status_code_redirect'] = True
106
106
107 #======================================================================
107 #======================================================================
108 # EXTRACT REPOSITORY NAME FROM ENV
108 # EXTRACT REPOSITORY NAME FROM ENV
109 #======================================================================
109 #======================================================================
110 try:
110 try:
111 repo_name = self.__get_repository(environ)
111 repo_name = self.__get_repository(environ)
112 log.debug('Extracted repo name is %s' % repo_name)
112 log.debug('Extracted repo name is %s' % repo_name)
113 except:
113 except:
114 return HTTPInternalServerError()(environ, start_response)
114 return HTTPInternalServerError()(environ, start_response)
115
115
116 #======================================================================
116 #======================================================================
117 # GET ACTION PULL or PUSH
117 # GET ACTION PULL or PUSH
118 #======================================================================
118 #======================================================================
119 action = self.__get_action(environ)
119 action = self.__get_action(environ)
120
120
121 #======================================================================
121 #======================================================================
122 # CHECK ANONYMOUS PERMISSION
122 # CHECK ANONYMOUS PERMISSION
123 #======================================================================
123 #======================================================================
124 if action in ['pull', 'push']:
124 if action in ['pull', 'push']:
125 anonymous_user = self.__get_user('default')
125 anonymous_user = self.__get_user('default')
126 username = anonymous_user.username
126 username = anonymous_user.username
127 anonymous_perm = self._check_permission(action,anonymous_user,
127 anonymous_perm = self._check_permission(action, anonymous_user,
128 repo_name)
128 repo_name)
129
129
130 if anonymous_perm is not True or anonymous_user.active is False:
130 if anonymous_perm is not True or anonymous_user.active is False:
131 if anonymous_perm is not True:
131 if anonymous_perm is not True:
132 log.debug('Not enough credentials to access this '
132 log.debug('Not enough credentials to access this '
133 'repository as anonymous user')
133 'repository as anonymous user')
134 if anonymous_user.active is False:
134 if anonymous_user.active is False:
135 log.debug('Anonymous access is disabled, running '
135 log.debug('Anonymous access is disabled, running '
136 'authentication')
136 'authentication')
137 #==============================================================
137 #==============================================================
138 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
138 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
139 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
139 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
140 #==============================================================
140 #==============================================================
141
141
142 # Attempting to retrieve username from the container
142 # Attempting to retrieve username from the container
143 username = get_container_username(environ, self.config)
143 username = get_container_username(environ, self.config)
144
144
145 # If not authenticated by the container, running basic auth
145 # If not authenticated by the container, running basic auth
146 if not username:
146 if not username:
147 self.authenticate.realm = \
147 self.authenticate.realm = \
148 safe_str(self.config['rhodecode_realm'])
148 safe_str(self.config['rhodecode_realm'])
149 result = self.authenticate(environ)
149 result = self.authenticate(environ)
150 if isinstance(result, str):
150 if isinstance(result, str):
151 AUTH_TYPE.update(environ, 'basic')
151 AUTH_TYPE.update(environ, 'basic')
152 REMOTE_USER.update(environ, result)
152 REMOTE_USER.update(environ, result)
153 username = result
153 username = result
154 else:
154 else:
155 return result.wsgi_application(environ, start_response)
155 return result.wsgi_application(environ, start_response)
156
156
157 #==============================================================
157 #==============================================================
158 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
158 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
159 #==============================================================
159 #==============================================================
160 log.info('%s action on GIT repo "%s"' % (action, repo_name))
161 if action in ['pull', 'push']:
160 if action in ['pull', 'push']:
162 try:
161 try:
163 user = self.__get_user(username)
162 user = self.__get_user(username)
164 if user is None or not user.active:
163 if user is None or not user.active:
165 return HTTPForbidden()(environ, start_response)
164 return HTTPForbidden()(environ, start_response)
166 username = user.username
165 username = user.username
167 except:
166 except:
168 log.error(traceback.format_exc())
167 log.error(traceback.format_exc())
169 return HTTPInternalServerError()(environ,
168 return HTTPInternalServerError()(environ,
170 start_response)
169 start_response)
171
170
172 #check permissions for this repository
171 #check permissions for this repository
173 perm = self._check_permission(action, user,
172 perm = self._check_permission(action, user,
174 repo_name)
173 repo_name)
175 if perm is not True:
174 if perm is not True:
176 return HTTPForbidden()(environ, start_response)
175 return HTTPForbidden()(environ, start_response)
177
176
178 #===================================================================
177 #===================================================================
179 # GIT REQUEST HANDLING
178 # GIT REQUEST HANDLING
180 #===================================================================
179 #===================================================================
181
180
182 repo_path = safe_str(os.path.join(self.basepath, repo_name))
181 repo_path = safe_str(os.path.join(self.basepath, repo_name))
183 log.debug('Repository path is %s' % repo_path)
182 log.debug('Repository path is %s' % repo_path)
184
183
185 # quick check if that dir exists...
184 # quick check if that dir exists...
186 if is_valid_repo(repo_name, self.basepath) is False:
185 if is_valid_repo(repo_name, self.basepath) is False:
187 return HTTPNotFound()(environ, start_response)
186 return HTTPNotFound()(environ, start_response)
188
187
189 try:
188 try:
190 #invalidate cache on push
189 #invalidate cache on push
191 if action == 'push':
190 if action == 'push':
192 self._invalidate_cache(repo_name)
191 self._invalidate_cache(repo_name)
193
192 log.info('%s action on GIT repo "%s"' % (action, repo_name))
194 app = self.__make_app(repo_name, repo_path)
193 app = self.__make_app(repo_name, repo_path)
195 return app(environ, start_response)
194 return app(environ, start_response)
196 except Exception:
195 except Exception:
197 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
198 return HTTPInternalServerError()(environ, start_response)
197 return HTTPInternalServerError()(environ, start_response)
199
198
200 def __make_app(self, repo_name, repo_path):
199 def __make_app(self, repo_name, repo_path):
201 """
200 """
202 Make an wsgi application using dulserver
201 Make an wsgi application using dulserver
203
202
204 :param repo_name: name of the repository
203 :param repo_name: name of the repository
205 :param repo_path: full path to the repository
204 :param repo_path: full path to the repository
206 """
205 """
207
206
208 _d = {'/' + repo_name: Repo(repo_path)}
207 _d = {'/' + repo_name: Repo(repo_path)}
209 backend = dulserver.DictBackend(_d)
208 backend = dulserver.DictBackend(_d)
210 gitserve = HTTPGitApplication(backend)
209 gitserve = HTTPGitApplication(backend)
211
210
212 return gitserve
211 return gitserve
213
212
214 def __get_repository(self, environ):
213 def __get_repository(self, environ):
215 """
214 """
216 Get's repository name out of PATH_INFO header
215 Get's repository name out of PATH_INFO header
217
216
218 :param environ: environ where PATH_INFO is stored
217 :param environ: environ where PATH_INFO is stored
219 """
218 """
220 try:
219 try:
221 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
220 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
222 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
221 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
223 if repo_name.endswith('/'):
222 if repo_name.endswith('/'):
224 repo_name = repo_name.rstrip('/')
223 repo_name = repo_name.rstrip('/')
225 except:
224 except:
226 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
227 raise
226 raise
228 repo_name = repo_name.split('/')[0]
227 repo_name = repo_name.split('/')[0]
229 return repo_name
228 return repo_name
230
229
231 def __get_user(self, username):
230 def __get_user(self, username):
232 return User.get_by_username(username)
231 return User.get_by_username(username)
233
232
234 def __get_action(self, environ):
233 def __get_action(self, environ):
235 """Maps git request commands into a pull or push command.
234 """Maps git request commands into a pull or push command.
236
235
237 :param environ:
236 :param environ:
238 """
237 """
239 service = environ['QUERY_STRING'].split('=')
238 service = environ['QUERY_STRING'].split('=')
240 if len(service) > 1:
239 if len(service) > 1:
241 service_cmd = service[1]
240 service_cmd = service[1]
242 mapping = {'git-receive-pack': 'push',
241 mapping = {'git-receive-pack': 'push',
243 'git-upload-pack': 'pull',
242 'git-upload-pack': 'pull',
244 }
243 }
245
244
246 return mapping.get(service_cmd,
245 return mapping.get(service_cmd,
247 service_cmd if service_cmd else 'other')
246 service_cmd if service_cmd else 'other')
248 else:
247 else:
249 return 'other'
248 return 'other'
@@ -1,248 +1,247 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
30
31 from mercurial.error import RepoError
31 from mercurial.error import RepoError
32 from mercurial.hgweb import hgweb_mod
32 from mercurial.hgweb import hgweb_mod
33
33
34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35
35
36 from rhodecode.lib import safe_str
36 from rhodecode.lib import safe_str
37 from rhodecode.lib.base import BaseVCSController
37 from rhodecode.lib.base import BaseVCSController
38 from rhodecode.lib.auth import get_container_username
38 from rhodecode.lib.auth import get_container_username
39 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
39 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
40 from rhodecode.model.db import User
40 from rhodecode.model.db import User
41
41
42 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
42 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 def is_mercurial(environ):
47 def is_mercurial(environ):
48 """Returns True if request's target is mercurial server - header
48 """Returns True if request's target is mercurial server - header
49 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
49 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
50 """
50 """
51 http_accept = environ.get('HTTP_ACCEPT')
51 http_accept = environ.get('HTTP_ACCEPT')
52 if http_accept and http_accept.startswith('application/mercurial'):
52 if http_accept and http_accept.startswith('application/mercurial'):
53 return True
53 return True
54 return False
54 return False
55
55
56
56
57 class SimpleHg(BaseVCSController):
57 class SimpleHg(BaseVCSController):
58
58
59 def _handle_request(self, environ, start_response):
59 def _handle_request(self, environ, start_response):
60 if not is_mercurial(environ):
60 if not is_mercurial(environ):
61 return self.application(environ, start_response)
61 return self.application(environ, start_response)
62
62
63 proxy_key = 'HTTP_X_REAL_IP'
63 proxy_key = 'HTTP_X_REAL_IP'
64 def_key = 'REMOTE_ADDR'
64 def_key = 'REMOTE_ADDR'
65 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
65 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
66
66
67 # skip passing error to error controller
67 # skip passing error to error controller
68 environ['pylons.status_code_redirect'] = True
68 environ['pylons.status_code_redirect'] = True
69
69
70 #======================================================================
70 #======================================================================
71 # EXTRACT REPOSITORY NAME FROM ENV
71 # EXTRACT REPOSITORY NAME FROM ENV
72 #======================================================================
72 #======================================================================
73 try:
73 try:
74 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
74 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
75 log.debug('Extracted repo name is %s' % repo_name)
75 log.debug('Extracted repo name is %s' % repo_name)
76 except:
76 except:
77 return HTTPInternalServerError()(environ, start_response)
77 return HTTPInternalServerError()(environ, start_response)
78
78
79 #======================================================================
79 #======================================================================
80 # GET ACTION PULL or PUSH
80 # GET ACTION PULL or PUSH
81 #======================================================================
81 #======================================================================
82 action = self.__get_action(environ)
82 action = self.__get_action(environ)
83 #======================================================================
83 #======================================================================
84 # CHECK ANONYMOUS PERMISSION
84 # CHECK ANONYMOUS PERMISSION
85 #======================================================================
85 #======================================================================
86 if action in ['pull', 'push']:
86 if action in ['pull', 'push']:
87 anonymous_user = self.__get_user('default')
87 anonymous_user = self.__get_user('default')
88
88
89 username = anonymous_user.username
89 username = anonymous_user.username
90 anonymous_perm = self._check_permission(action, anonymous_user,
90 anonymous_perm = self._check_permission(action, anonymous_user,
91 repo_name)
91 repo_name)
92
92
93 if anonymous_perm is not True or anonymous_user.active is False:
93 if anonymous_perm is not True or anonymous_user.active is False:
94 if anonymous_perm is not True:
94 if anonymous_perm is not True:
95 log.debug('Not enough credentials to access this '
95 log.debug('Not enough credentials to access this '
96 'repository as anonymous user')
96 'repository as anonymous user')
97 if anonymous_user.active is False:
97 if anonymous_user.active is False:
98 log.debug('Anonymous access is disabled, running '
98 log.debug('Anonymous access is disabled, running '
99 'authentication')
99 'authentication')
100 #==============================================================
100 #==============================================================
101 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
101 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
102 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
102 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
103 #==============================================================
103 #==============================================================
104
104
105 # Attempting to retrieve username from the container
105 # Attempting to retrieve username from the container
106 username = get_container_username(environ, self.config)
106 username = get_container_username(environ, self.config)
107
107
108 # If not authenticated by the container, running basic auth
108 # If not authenticated by the container, running basic auth
109 if not username:
109 if not username:
110 self.authenticate.realm = \
110 self.authenticate.realm = \
111 safe_str(self.config['rhodecode_realm'])
111 safe_str(self.config['rhodecode_realm'])
112 result = self.authenticate(environ)
112 result = self.authenticate(environ)
113 if isinstance(result, str):
113 if isinstance(result, str):
114 AUTH_TYPE.update(environ, 'basic')
114 AUTH_TYPE.update(environ, 'basic')
115 REMOTE_USER.update(environ, result)
115 REMOTE_USER.update(environ, result)
116 username = result
116 username = result
117 else:
117 else:
118 return result.wsgi_application(environ, start_response)
118 return result.wsgi_application(environ, start_response)
119
119
120 #==============================================================
120 #==============================================================
121 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
121 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
122 #==============================================================
122 #==============================================================
123 log.info('%s action on HG repo "%s"' % (action, repo_name))
124 if action in ['pull', 'push']:
123 if action in ['pull', 'push']:
125 try:
124 try:
126 user = self.__get_user(username)
125 user = self.__get_user(username)
127 if user is None or not user.active:
126 if user is None or not user.active:
128 return HTTPForbidden()(environ, start_response)
127 return HTTPForbidden()(environ, start_response)
129 username = user.username
128 username = user.username
130 except:
129 except:
131 log.error(traceback.format_exc())
130 log.error(traceback.format_exc())
132 return HTTPInternalServerError()(environ,
131 return HTTPInternalServerError()(environ,
133 start_response)
132 start_response)
134
133
135 #check permissions for this repository
134 #check permissions for this repository
136 perm = self._check_permission(action, user,
135 perm = self._check_permission(action, user,
137 repo_name)
136 repo_name)
138 if perm is not True:
137 if perm is not True:
139 return HTTPForbidden()(environ, start_response)
138 return HTTPForbidden()(environ, start_response)
140
139
141 extras = {'ip': ipaddr,
140 extras = {'ip': ipaddr,
142 'username': username,
141 'username': username,
143 'action': action,
142 'action': action,
144 'repository': repo_name}
143 'repository': repo_name}
145
144
146 #======================================================================
145 #======================================================================
147 # MERCURIAL REQUEST HANDLING
146 # MERCURIAL REQUEST HANDLING
148 #======================================================================
147 #======================================================================
149
148
150 repo_path = safe_str(os.path.join(self.basepath, repo_name))
149 repo_path = safe_str(os.path.join(self.basepath, repo_name))
151 log.debug('Repository path is %s' % repo_path)
150 log.debug('Repository path is %s' % repo_path)
152
151
153 baseui = make_ui('db')
152 baseui = make_ui('db')
154 self.__inject_extras(repo_path, baseui, extras)
153 self.__inject_extras(repo_path, baseui, extras)
155
154
156 # quick check if that dir exists...
155 # quick check if that dir exists...
157 if is_valid_repo(repo_name, self.basepath) is False:
156 if is_valid_repo(repo_name, self.basepath) is False:
158 return HTTPNotFound()(environ, start_response)
157 return HTTPNotFound()(environ, start_response)
159
158
160 try:
159 try:
161 # invalidate cache on push
160 # invalidate cache on push
162 if action == 'push':
161 if action == 'push':
163 self._invalidate_cache(repo_name)
162 self._invalidate_cache(repo_name)
164
163 log.info('%s action on HG repo "%s"' % (action, repo_name))
165 app = self.__make_app(repo_path, baseui, extras)
164 app = self.__make_app(repo_path, baseui, extras)
166 return app(environ, start_response)
165 return app(environ, start_response)
167 except RepoError, e:
166 except RepoError, e:
168 if str(e).find('not found') != -1:
167 if str(e).find('not found') != -1:
169 return HTTPNotFound()(environ, start_response)
168 return HTTPNotFound()(environ, start_response)
170 except Exception:
169 except Exception:
171 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
172 return HTTPInternalServerError()(environ, start_response)
171 return HTTPInternalServerError()(environ, start_response)
173
172
174 def __make_app(self, repo_name, baseui, extras):
173 def __make_app(self, repo_name, baseui, extras):
175 """
174 """
176 Make an wsgi application using hgweb, and inject generated baseui
175 Make an wsgi application using hgweb, and inject generated baseui
177 instance, additionally inject some extras into ui object
176 instance, additionally inject some extras into ui object
178 """
177 """
179 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
178 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
180
179
181 def __get_repository(self, environ):
180 def __get_repository(self, environ):
182 """
181 """
183 Get's repository name out of PATH_INFO header
182 Get's repository name out of PATH_INFO header
184
183
185 :param environ: environ where PATH_INFO is stored
184 :param environ: environ where PATH_INFO is stored
186 """
185 """
187 try:
186 try:
188 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
187 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
189 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
188 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
190 if repo_name.endswith('/'):
189 if repo_name.endswith('/'):
191 repo_name = repo_name.rstrip('/')
190 repo_name = repo_name.rstrip('/')
192 except:
191 except:
193 log.error(traceback.format_exc())
192 log.error(traceback.format_exc())
194 raise
193 raise
195
194
196 return repo_name
195 return repo_name
197
196
198 def __get_user(self, username):
197 def __get_user(self, username):
199 return User.get_by_username(username)
198 return User.get_by_username(username)
200
199
201 def __get_action(self, environ):
200 def __get_action(self, environ):
202 """
201 """
203 Maps mercurial request commands into a clone,pull or push command.
202 Maps mercurial request commands into a clone,pull or push command.
204 This should always return a valid command string
203 This should always return a valid command string
205
204
206 :param environ:
205 :param environ:
207 """
206 """
208 mapping = {'changegroup': 'pull',
207 mapping = {'changegroup': 'pull',
209 'changegroupsubset': 'pull',
208 'changegroupsubset': 'pull',
210 'stream_out': 'pull',
209 'stream_out': 'pull',
211 'listkeys': 'pull',
210 'listkeys': 'pull',
212 'unbundle': 'push',
211 'unbundle': 'push',
213 'pushkey': 'push', }
212 'pushkey': 'push', }
214 for qry in environ['QUERY_STRING'].split('&'):
213 for qry in environ['QUERY_STRING'].split('&'):
215 if qry.startswith('cmd'):
214 if qry.startswith('cmd'):
216 cmd = qry.split('=')[-1]
215 cmd = qry.split('=')[-1]
217 if cmd in mapping:
216 if cmd in mapping:
218 return mapping[cmd]
217 return mapping[cmd]
219 else:
218 else:
220 return 'pull'
219 return 'pull'
221
220
222 def __inject_extras(self, repo_path, baseui, extras={}):
221 def __inject_extras(self, repo_path, baseui, extras={}):
223 """
222 """
224 Injects some extra params into baseui instance
223 Injects some extra params into baseui instance
225
224
226 also overwrites global settings with those takes from local hgrc file
225 also overwrites global settings with those takes from local hgrc file
227
226
228 :param baseui: baseui instance
227 :param baseui: baseui instance
229 :param extras: dict with extra params to put into baseui
228 :param extras: dict with extra params to put into baseui
230 """
229 """
231
230
232 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
231 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
233
232
234 # make our hgweb quiet so it doesn't print output
233 # make our hgweb quiet so it doesn't print output
235 baseui.setconfig('ui', 'quiet', 'true')
234 baseui.setconfig('ui', 'quiet', 'true')
236
235
237 #inject some additional parameters that will be available in ui
236 #inject some additional parameters that will be available in ui
238 #for hooks
237 #for hooks
239 for k, v in extras.items():
238 for k, v in extras.items():
240 baseui.setconfig('rhodecode_extras', k, v)
239 baseui.setconfig('rhodecode_extras', k, v)
241
240
242 repoui = make_ui('file', hgrc, False)
241 repoui = make_ui('file', hgrc, False)
243
242
244 if repoui:
243 if repoui:
245 #overwrite our ui instance with the section from hgrc file
244 #overwrite our ui instance with the section from hgrc file
246 for section in ui_sections:
245 for section in ui_sections:
247 for k, v in repoui.configitems(section):
246 for k, v in repoui.configitems(section):
248 baseui.setconfig(section, k, v)
247 baseui.setconfig(section, k, v)
General Comments 0
You need to be logged in to leave comments. Login now