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