##// END OF EJS Templates
better logging
marcink -
r2021:a04844d9 beta
parent child Browse files
Show More
@@ -1,249 +1,249 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
160 log.info('%s action on GIT repo "%s"' % (action, repo_name))
161 if action in ['pull', 'push']:
161 if action in ['pull', 'push']:
162 try:
162 try:
163 user = self.__get_user(username)
163 user = self.__get_user(username)
164 if user is None or not user.active:
164 if user is None or not user.active:
165 return HTTPForbidden()(environ, start_response)
165 return HTTPForbidden()(environ, start_response)
166 username = user.username
166 username = user.username
167 except:
167 except:
168 log.error(traceback.format_exc())
168 log.error(traceback.format_exc())
169 return HTTPInternalServerError()(environ,
169 return HTTPInternalServerError()(environ,
170 start_response)
170 start_response)
171
171
172 #check permissions for this repository
172 #check permissions for this repository
173 perm = self._check_permission(action, user,
173 perm = self._check_permission(action, user,
174 repo_name)
174 repo_name)
175 if perm is not True:
175 if perm is not True:
176 return HTTPForbidden()(environ, start_response)
176 return HTTPForbidden()(environ, start_response)
177
177
178 #===================================================================
178 #===================================================================
179 # GIT REQUEST HANDLING
179 # GIT REQUEST HANDLING
180 #===================================================================
180 #===================================================================
181
181
182 repo_path = safe_str(os.path.join(self.basepath, repo_name))
182 repo_path = safe_str(os.path.join(self.basepath, repo_name))
183 log.debug('Repository path is %s' % repo_path)
183 log.debug('Repository path is %s' % repo_path)
184
184
185 # quick check if that dir exists...
185 # quick check if that dir exists...
186 if is_valid_repo(repo_name, self.basepath) is False:
186 if is_valid_repo(repo_name, self.basepath) is False:
187 return HTTPNotFound()(environ, start_response)
187 return HTTPNotFound()(environ, start_response)
188
188
189 try:
189 try:
190 #invalidate cache on push
190 #invalidate cache on push
191 if action == 'push':
191 if action == 'push':
192 self._invalidate_cache(repo_name)
192 self._invalidate_cache(repo_name)
193
193
194 app = self.__make_app(repo_name, repo_path)
194 app = self.__make_app(repo_name, repo_path)
195 return app(environ, start_response)
195 return app(environ, start_response)
196 except Exception:
196 except Exception:
197 log.error(traceback.format_exc())
197 log.error(traceback.format_exc())
198 return HTTPInternalServerError()(environ, start_response)
198 return HTTPInternalServerError()(environ, start_response)
199
199
200 def __make_app(self, repo_name, repo_path):
200 def __make_app(self, repo_name, repo_path):
201 """
201 """
202 Make an wsgi application using dulserver
202 Make an wsgi application using dulserver
203
203
204 :param repo_name: name of the repository
204 :param repo_name: name of the repository
205 :param repo_path: full path to the repository
205 :param repo_path: full path to the repository
206 """
206 """
207
207
208 _d = {'/' + repo_name: Repo(repo_path)}
208 _d = {'/' + repo_name: Repo(repo_path)}
209 backend = dulserver.DictBackend(_d)
209 backend = dulserver.DictBackend(_d)
210 gitserve = HTTPGitApplication(backend)
210 gitserve = HTTPGitApplication(backend)
211
211
212 return gitserve
212 return gitserve
213
213
214 def __get_repository(self, environ):
214 def __get_repository(self, environ):
215 """
215 """
216 Get's repository name out of PATH_INFO header
216 Get's repository name out of PATH_INFO header
217
217
218 :param environ: environ where PATH_INFO is stored
218 :param environ: environ where PATH_INFO is stored
219 """
219 """
220 try:
220 try:
221 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
221 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
222 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
222 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
223 if repo_name.endswith('/'):
223 if repo_name.endswith('/'):
224 repo_name = repo_name.rstrip('/')
224 repo_name = repo_name.rstrip('/')
225 except:
225 except:
226 log.error(traceback.format_exc())
226 log.error(traceback.format_exc())
227 raise
227 raise
228 repo_name = repo_name.split('/')[0]
228 repo_name = repo_name.split('/')[0]
229 return repo_name
229 return repo_name
230
230
231 def __get_user(self, username):
231 def __get_user(self, username):
232 return User.get_by_username(username)
232 return User.get_by_username(username)
233
233
234 def __get_action(self, environ):
234 def __get_action(self, environ):
235 """Maps git request commands into a pull or push command.
235 """Maps git request commands into a pull or push command.
236
236
237 :param environ:
237 :param environ:
238 """
238 """
239 service = environ['QUERY_STRING'].split('=')
239 service = environ['QUERY_STRING'].split('=')
240 if len(service) > 1:
240 if len(service) > 1:
241 service_cmd = service[1]
241 service_cmd = service[1]
242 mapping = {'git-receive-pack': 'push',
242 mapping = {'git-receive-pack': 'push',
243 'git-upload-pack': 'pull',
243 'git-upload-pack': 'pull',
244 }
244 }
245
245
246 return mapping.get(service_cmd,
246 return mapping.get(service_cmd,
247 service_cmd if service_cmd else 'other')
247 service_cmd if service_cmd else 'other')
248 else:
248 else:
249 return 'other'
249 return 'other'
@@ -1,249 +1,248 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
84 #======================================================================
83 #======================================================================
85 # CHECK ANONYMOUS PERMISSION
84 # CHECK ANONYMOUS PERMISSION
86 #======================================================================
85 #======================================================================
87 if action in ['pull', 'push']:
86 if action in ['pull', 'push']:
88 anonymous_user = self.__get_user('default')
87 anonymous_user = self.__get_user('default')
89
88
90 username = anonymous_user.username
89 username = anonymous_user.username
91 anonymous_perm = self._check_permission(action,anonymous_user,
90 anonymous_perm = self._check_permission(action, anonymous_user,
92 repo_name)
91 repo_name)
93
92
94 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:
95 if anonymous_perm is not True:
94 if anonymous_perm is not True:
96 log.debug('Not enough credentials to access this '
95 log.debug('Not enough credentials to access this '
97 'repository as anonymous user')
96 'repository as anonymous user')
98 if anonymous_user.active is False:
97 if anonymous_user.active is False:
99 log.debug('Anonymous access is disabled, running '
98 log.debug('Anonymous access is disabled, running '
100 'authentication')
99 'authentication')
101 #==============================================================
100 #==============================================================
102 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
101 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
103 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
102 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
104 #==============================================================
103 #==============================================================
105
104
106 # Attempting to retrieve username from the container
105 # Attempting to retrieve username from the container
107 username = get_container_username(environ, self.config)
106 username = get_container_username(environ, self.config)
108
107
109 # If not authenticated by the container, running basic auth
108 # If not authenticated by the container, running basic auth
110 if not username:
109 if not username:
111 self.authenticate.realm = \
110 self.authenticate.realm = \
112 safe_str(self.config['rhodecode_realm'])
111 safe_str(self.config['rhodecode_realm'])
113 result = self.authenticate(environ)
112 result = self.authenticate(environ)
114 if isinstance(result, str):
113 if isinstance(result, str):
115 AUTH_TYPE.update(environ, 'basic')
114 AUTH_TYPE.update(environ, 'basic')
116 REMOTE_USER.update(environ, result)
115 REMOTE_USER.update(environ, result)
117 username = result
116 username = result
118 else:
117 else:
119 return result.wsgi_application(environ, start_response)
118 return result.wsgi_application(environ, start_response)
120
119
121 #==============================================================
120 #==============================================================
122 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
121 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
123 #==============================================================
122 #==============================================================
124
123 log.info('%s action on HG repo "%s"' % (action, repo_name))
125 if action in ['pull', 'push']:
124 if action in ['pull', 'push']:
126 try:
125 try:
127 user = self.__get_user(username)
126 user = self.__get_user(username)
128 if user is None or not user.active:
127 if user is None or not user.active:
129 return HTTPForbidden()(environ, start_response)
128 return HTTPForbidden()(environ, start_response)
130 username = user.username
129 username = user.username
131 except:
130 except:
132 log.error(traceback.format_exc())
131 log.error(traceback.format_exc())
133 return HTTPInternalServerError()(environ,
132 return HTTPInternalServerError()(environ,
134 start_response)
133 start_response)
135
134
136 #check permissions for this repository
135 #check permissions for this repository
137 perm = self._check_permission(action, user,
136 perm = self._check_permission(action, user,
138 repo_name)
137 repo_name)
139 if perm is not True:
138 if perm is not True:
140 return HTTPForbidden()(environ, start_response)
139 return HTTPForbidden()(environ, start_response)
141
140
142 extras = {'ip': ipaddr,
141 extras = {'ip': ipaddr,
143 'username': username,
142 'username': username,
144 'action': action,
143 'action': action,
145 'repository': repo_name}
144 'repository': repo_name}
146
145
147 #======================================================================
146 #======================================================================
148 # MERCURIAL REQUEST HANDLING
147 # MERCURIAL REQUEST HANDLING
149 #======================================================================
148 #======================================================================
150
149
151 repo_path = safe_str(os.path.join(self.basepath, repo_name))
150 repo_path = safe_str(os.path.join(self.basepath, repo_name))
152 log.debug('Repository path is %s' % repo_path)
151 log.debug('Repository path is %s' % repo_path)
153
152
154 baseui = make_ui('db')
153 baseui = make_ui('db')
155 self.__inject_extras(repo_path, baseui, extras)
154 self.__inject_extras(repo_path, baseui, extras)
156
155
157 # quick check if that dir exists...
156 # quick check if that dir exists...
158 if is_valid_repo(repo_name, self.basepath) is False:
157 if is_valid_repo(repo_name, self.basepath) is False:
159 return HTTPNotFound()(environ, start_response)
158 return HTTPNotFound()(environ, start_response)
160
159
161 try:
160 try:
162 # invalidate cache on push
161 # invalidate cache on push
163 if action == 'push':
162 if action == 'push':
164 self._invalidate_cache(repo_name)
163 self._invalidate_cache(repo_name)
165
164
166 app = self.__make_app(repo_path, baseui, extras)
165 app = self.__make_app(repo_path, baseui, extras)
167 return app(environ, start_response)
166 return app(environ, start_response)
168 except RepoError, e:
167 except RepoError, e:
169 if str(e).find('not found') != -1:
168 if str(e).find('not found') != -1:
170 return HTTPNotFound()(environ, start_response)
169 return HTTPNotFound()(environ, start_response)
171 except Exception:
170 except Exception:
172 log.error(traceback.format_exc())
171 log.error(traceback.format_exc())
173 return HTTPInternalServerError()(environ, start_response)
172 return HTTPInternalServerError()(environ, start_response)
174
173
175 def __make_app(self, repo_name, baseui, extras):
174 def __make_app(self, repo_name, baseui, extras):
176 """
175 """
177 Make an wsgi application using hgweb, and inject generated baseui
176 Make an wsgi application using hgweb, and inject generated baseui
178 instance, additionally inject some extras into ui object
177 instance, additionally inject some extras into ui object
179 """
178 """
180 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
179 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
181
180
182 def __get_repository(self, environ):
181 def __get_repository(self, environ):
183 """
182 """
184 Get's repository name out of PATH_INFO header
183 Get's repository name out of PATH_INFO header
185
184
186 :param environ: environ where PATH_INFO is stored
185 :param environ: environ where PATH_INFO is stored
187 """
186 """
188 try:
187 try:
189 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
188 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
190 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
189 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
191 if repo_name.endswith('/'):
190 if repo_name.endswith('/'):
192 repo_name = repo_name.rstrip('/')
191 repo_name = repo_name.rstrip('/')
193 except:
192 except:
194 log.error(traceback.format_exc())
193 log.error(traceback.format_exc())
195 raise
194 raise
196
195
197 return repo_name
196 return repo_name
198
197
199 def __get_user(self, username):
198 def __get_user(self, username):
200 return User.get_by_username(username)
199 return User.get_by_username(username)
201
200
202 def __get_action(self, environ):
201 def __get_action(self, environ):
203 """
202 """
204 Maps mercurial request commands into a clone,pull or push command.
203 Maps mercurial request commands into a clone,pull or push command.
205 This should always return a valid command string
204 This should always return a valid command string
206
205
207 :param environ:
206 :param environ:
208 """
207 """
209 mapping = {'changegroup': 'pull',
208 mapping = {'changegroup': 'pull',
210 'changegroupsubset': 'pull',
209 'changegroupsubset': 'pull',
211 'stream_out': 'pull',
210 'stream_out': 'pull',
212 'listkeys': 'pull',
211 'listkeys': 'pull',
213 'unbundle': 'push',
212 'unbundle': 'push',
214 'pushkey': 'push', }
213 'pushkey': 'push', }
215 for qry in environ['QUERY_STRING'].split('&'):
214 for qry in environ['QUERY_STRING'].split('&'):
216 if qry.startswith('cmd'):
215 if qry.startswith('cmd'):
217 cmd = qry.split('=')[-1]
216 cmd = qry.split('=')[-1]
218 if cmd in mapping:
217 if cmd in mapping:
219 return mapping[cmd]
218 return mapping[cmd]
220 else:
219 else:
221 return 'pull'
220 return 'pull'
222
221
223 def __inject_extras(self, repo_path, baseui, extras={}):
222 def __inject_extras(self, repo_path, baseui, extras={}):
224 """
223 """
225 Injects some extra params into baseui instance
224 Injects some extra params into baseui instance
226
225
227 also overwrites global settings with those takes from local hgrc file
226 also overwrites global settings with those takes from local hgrc file
228
227
229 :param baseui: baseui instance
228 :param baseui: baseui instance
230 :param extras: dict with extra params to put into baseui
229 :param extras: dict with extra params to put into baseui
231 """
230 """
232
231
233 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
232 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
234
233
235 # make our hgweb quiet so it doesn't print output
234 # make our hgweb quiet so it doesn't print output
236 baseui.setconfig('ui', 'quiet', 'true')
235 baseui.setconfig('ui', 'quiet', 'true')
237
236
238 #inject some additional parameters that will be available in ui
237 #inject some additional parameters that will be available in ui
239 #for hooks
238 #for hooks
240 for k, v in extras.items():
239 for k, v in extras.items():
241 baseui.setconfig('rhodecode_extras', k, v)
240 baseui.setconfig('rhodecode_extras', k, v)
242
241
243 repoui = make_ui('file', hgrc, False)
242 repoui = make_ui('file', hgrc, False)
244
243
245 if repoui:
244 if repoui:
246 #overwrite our ui instance with the section from hgrc file
245 #overwrite our ui instance with the section from hgrc file
247 for section in ui_sections:
246 for section in ui_sections:
248 for k, v in repoui.configitems(section):
247 for k, v in repoui.configitems(section):
249 baseui.setconfig(section, k, v)
248 baseui.setconfig(section, k, v)
General Comments 0
You need to be logged in to leave comments. Login now