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