##// END OF EJS Templates
fixed issue #181, and small fix in gitmiddleware
marcink -
r1293:ae5b07e7 beta
parent child Browse files
Show More
@@ -1,318 +1,318 b''
1 1 .. _changelog:
2 2
3 3 Changelog
4 4 =========
5 5
6 6 1.2.0 (**2011-XX-XX**)
7 7 ======================
8 8
9 9 :status: in-progress
10 10 :branch: beta
11 11
12 12 news
13 13 ----
14 14
15 15 - implemented #89 Can setup google analytics code from settings menu
16 16 - implemented #91 added nicer looking archive urls with more download options
17 17 like tags, branches
18 18 - implemented #44 into file browsing, and added follow branch option
19 19 - implemented #84 downloads can be enabled/disabled for each repository
20 20 - anonymous repository can be cloned without having to pass default:default
21 21 into clone url
22 22 - fixed #90 whoosh indexer can index chooses repositories passed in command
23 23 line
24 24 - extended journal with day aggregates and paging
25 25 - implemented #107 source code lines highlight ranges
26 26 - implemented #93 customizable changelog on combined revision ranges -
27 27 equivalent of githubs compare view
28 28 - implemented #108 extended and more powerful LDAP configuration
29 29 - implemented #56 users groups
30 30 - major code rewrites optimized codes for speed and memory usage
31 31 - raw and diff downloads are now in git format
32 32 - setup command checks for write access to given path
33 33 - fixed many issues with international characters and unicode. It uses utf8
34 34 decode with replace to provide less errors even with non utf8 encoded strings
35 35 - #125 added API KEY access to feeds
36 36 - #109 Repository can be created from external Mercurial link (aka. remote
37 37 repository, and manually updated (via pull) from admin panel
38 38 - beta git support - push/pull server + basic view for git repos
39 39 - added followers page
40 40
41 41 fixes
42 42 -----
43 43
44 44 - fixed file browser bug, when switching into given form revision the url was
45 45 not changing
46 46 - fixed propagation to error controller on simplehg and simplegit middlewares
47 47 - fixed error when trying to make a download on empty repository
48 48 - fixed problem with '[' chars in commit messages in journal
49 49 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
50 50 - journal fork fixes
51 51 - removed issue with space inside renamed repository after deletion
52 52 - fixed strange issue on formencode imports
53 53 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
54 54 - #150 fixes for errors on repositories mapped in db but corrupted in
55 55 filesystem
56
56 - fixed problem with ascendant characters in realm #181
57 57
58 58 1.1.8 (**2011-04-12**)
59 59 ======================
60 60
61 61 news
62 62 ----
63 63
64 64 - improved windows support
65 65
66 66 fixes
67 67 -----
68 68
69 69 - fixed #140 freeze of python dateutil library, since new version is python2.x
70 70 incompatible
71 71 - setup-app will check for write permission in given path
72 72 - cleaned up license info issue #149
73 73 - fixes for issues #137,#116 and problems with unicode and accented characters.
74 74 - fixes crashes on gravatar, when passed in email as unicode
75 75 - fixed tooltip flickering problems
76 76 - fixed came_from redirection on windows
77 77 - fixed logging modules, and sql formatters
78 78 - windows fixes for os.kill issue #133
79 79 - fixes path splitting for windows issues #148
80 80 - fixed issue #143 wrong import on migration to 1.1.X
81 81 - fixed problems with displaying binary files, thanks to Thomas Waldmann
82 82 - removed name from archive files since it's breaking ui for long repo names
83 83 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
84 84 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
85 85 Thomas Waldmann
86 86 - fixed issue #166 summary pager was skipping 10 revisions on second page
87 87
88 88
89 89 1.1.7 (**2011-03-23**)
90 90 ======================
91 91
92 92 news
93 93 ----
94 94
95 95 fixes
96 96 -----
97 97
98 98 - fixed (again) #136 installation support for FreeBSD
99 99
100 100
101 101 1.1.6 (**2011-03-21**)
102 102 ======================
103 103
104 104 news
105 105 ----
106 106
107 107 fixes
108 108 -----
109 109
110 110 - fixed #136 installation support for FreeBSD
111 111 - RhodeCode will check for python version during installation
112 112
113 113 1.1.5 (**2011-03-17**)
114 114 ======================
115 115
116 116 news
117 117 ----
118 118
119 119 - basic windows support, by exchanging pybcrypt into sha256 for windows only
120 120 highly inspired by idea of mantis406
121 121
122 122 fixes
123 123 -----
124 124
125 125 - fixed sorting by author in main page
126 126 - fixed crashes with diffs on binary files
127 127 - fixed #131 problem with boolean values for LDAP
128 128 - fixed #122 mysql problems thanks to striker69
129 129 - fixed problem with errors on calling raw/raw_files/annotate functions
130 130 with unknown revisions
131 131 - fixed returned rawfiles attachment names with international character
132 132 - cleaned out docs, big thanks to Jason Harris
133 133
134 134 1.1.4 (**2011-02-19**)
135 135 ======================
136 136
137 137 news
138 138 ----
139 139
140 140 fixes
141 141 -----
142 142
143 143 - fixed formencode import problem on settings page, that caused server crash
144 144 when that page was accessed as first after server start
145 145 - journal fixes
146 146 - fixed option to access repository just by entering http://server/<repo_name>
147 147
148 148 1.1.3 (**2011-02-16**)
149 149 ======================
150 150
151 151 news
152 152 ----
153 153
154 154 - implemented #102 allowing the '.' character in username
155 155 - added option to access repository just by entering http://server/<repo_name>
156 156 - celery task ignores result for better performance
157 157
158 158 fixes
159 159 -----
160 160
161 161 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
162 162 apollo13 and Johan Walles
163 163 - small fixes in journal
164 164 - fixed problems with getting setting for celery from .ini files
165 165 - registration, password reset and login boxes share the same title as main
166 166 application now
167 167 - fixed #113: to high permissions to fork repository
168 168 - fixed problem with '[' chars in commit messages in journal
169 169 - removed issue with space inside renamed repository after deletion
170 170 - db transaction fixes when filesystem repository creation failed
171 171 - fixed #106 relation issues on databases different than sqlite
172 172 - fixed static files paths links to use of url() method
173 173
174 174 1.1.2 (**2011-01-12**)
175 175 ======================
176 176
177 177 news
178 178 ----
179 179
180 180
181 181 fixes
182 182 -----
183 183
184 184 - fixes #98 protection against float division of percentage stats
185 185 - fixed graph bug
186 186 - forced webhelpers version since it was making troubles during installation
187 187
188 188 1.1.1 (**2011-01-06**)
189 189 ======================
190 190
191 191 news
192 192 ----
193 193
194 194 - added force https option into ini files for easier https usage (no need to
195 195 set server headers with this options)
196 196 - small css updates
197 197
198 198 fixes
199 199 -----
200 200
201 201 - fixed #96 redirect loop on files view on repositories without changesets
202 202 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
203 203 and server crashed with errors
204 204 - fixed large tooltips problems on main page
205 205 - fixed #92 whoosh indexer is more error proof
206 206
207 207 1.1.0 (**2010-12-18**)
208 208 ======================
209 209
210 210 news
211 211 ----
212 212
213 213 - rewrite of internals for vcs >=0.1.10
214 214 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
215 215 with older clients
216 216 - anonymous access, authentication via ldap
217 217 - performance upgrade for cached repos list - each repository has it's own
218 218 cache that's invalidated when needed.
219 219 - performance upgrades on repositories with large amount of commits (20K+)
220 220 - main page quick filter for filtering repositories
221 221 - user dashboards with ability to follow chosen repositories actions
222 222 - sends email to admin on new user registration
223 223 - added cache/statistics reset options into repository settings
224 224 - more detailed action logger (based on hooks) with pushed changesets lists
225 225 and options to disable those hooks from admin panel
226 226 - introduced new enhanced changelog for merges that shows more accurate results
227 227 - new improved and faster code stats (based on pygments lexers mapping tables,
228 228 showing up to 10 trending sources for each repository. Additionally stats
229 229 can be disabled in repository settings.
230 230 - gui optimizations, fixed application width to 1024px
231 231 - added cut off (for large files/changesets) limit into config files
232 232 - whoosh, celeryd, upgrade moved to paster command
233 233 - other than sqlite database backends can be used
234 234
235 235 fixes
236 236 -----
237 237
238 238 - fixes #61 forked repo was showing only after cache expired
239 239 - fixes #76 no confirmation on user deletes
240 240 - fixes #66 Name field misspelled
241 241 - fixes #72 block user removal when he owns repositories
242 242 - fixes #69 added password confirmation fields
243 243 - fixes #87 RhodeCode crashes occasionally on updating repository owner
244 244 - fixes #82 broken annotations on files with more than 1 blank line at the end
245 245 - a lot of fixes and tweaks for file browser
246 246 - fixed detached session issues
247 247 - fixed when user had no repos he would see all repos listed in my account
248 248 - fixed ui() instance bug when global hgrc settings was loaded for server
249 249 instance and all hgrc options were merged with our db ui() object
250 250 - numerous small bugfixes
251 251
252 252 (special thanks for TkSoh for detailed feedback)
253 253
254 254
255 255 1.0.2 (**2010-11-12**)
256 256 ======================
257 257
258 258 news
259 259 ----
260 260
261 261 - tested under python2.7
262 262 - bumped sqlalchemy and celery versions
263 263
264 264 fixes
265 265 -----
266 266
267 267 - fixed #59 missing graph.js
268 268 - fixed repo_size crash when repository had broken symlinks
269 269 - fixed python2.5 crashes.
270 270
271 271
272 272 1.0.1 (**2010-11-10**)
273 273 ======================
274 274
275 275 news
276 276 ----
277 277
278 278 - small css updated
279 279
280 280 fixes
281 281 -----
282 282
283 283 - fixed #53 python2.5 incompatible enumerate calls
284 284 - fixed #52 disable mercurial extension for web
285 285 - fixed #51 deleting repositories don't delete it's dependent objects
286 286
287 287
288 288 1.0.0 (**2010-11-02**)
289 289 ======================
290 290
291 291 - security bugfix simplehg wasn't checking for permissions on commands
292 292 other than pull or push.
293 293 - fixed doubled messages after push or pull in admin journal
294 294 - templating and css corrections, fixed repo switcher on chrome, updated titles
295 295 - admin menu accessible from options menu on repository view
296 296 - permissions cached queries
297 297
298 298 1.0.0rc4 (**2010-10-12**)
299 299 ==========================
300 300
301 301 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
302 302 - removed cache_manager settings from sqlalchemy meta
303 303 - added sqlalchemy cache settings to ini files
304 304 - validated password length and added second try of failure on paster setup-app
305 305 - fixed setup database destroy prompt even when there was no db
306 306
307 307
308 308 1.0.0rc3 (**2010-10-11**)
309 309 =========================
310 310
311 311 - fixed i18n during installation.
312 312
313 313 1.0.0rc2 (**2010-10-11**)
314 314 =========================
315 315
316 316 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
317 317 occure. After vcs is fixed it'll be put back again.
318 318 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,275 +1,275 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) 2009-2010 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
31 31 from dulwich import server as dulserver
32 32
33 33
34 34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35 35
36 36 def handle(self):
37 37 write = lambda x: self.proto.write_sideband(1, x)
38 38
39 39 graph_walker = dulserver.ProtocolGraphWalker(self,
40 40 self.repo.object_store,
41 41 self.repo.get_peeled)
42 42 objects_iter = self.repo.fetch_objects(
43 43 graph_walker.determine_wants, graph_walker, self.progress,
44 44 get_tagged=self.get_tagged)
45 45
46 46 # Do they want any objects?
47 47 if len(objects_iter) == 0:
48 48 return
49 49
50 50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 51 dulserver.write_pack_data(dulserver.ProtocolFile(None, write),
52 52 objects_iter, len(objects_iter))
53 53 messages = []
54 54 messages.append('thank you for using rhodecode')
55 55
56 56 for msg in messages:
57 57 self.progress(msg + "\n")
58 58 # we are done
59 59 self.proto.write("0000")
60 60
61 61 dulserver.DEFAULT_HANDLERS = {
62 62 'git-upload-pack': SimpleGitUploadPackHandler,
63 63 'git-receive-pack': dulserver.ReceivePackHandler,
64 64 }
65 65
66 66 from dulwich.repo import Repo
67 67 from dulwich.web import HTTPGitApplication
68 68
69 69 from paste.auth.basic import AuthBasicAuthenticator
70 70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
71 71
72 72 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
73 73 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
74 74 from rhodecode.model.user import UserModel
75 75
76 76 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
77 77
78 78 log = logging.getLogger(__name__)
79 79
80 80
81 81 def is_git(environ):
82 82 """Returns True if request's target is git server.
83 83 ``HTTP_USER_AGENT`` would then have git client version given.
84 84
85 85 :param environ:
86 86 """
87 87 http_user_agent = environ.get('HTTP_USER_AGENT')
88 88 if http_user_agent and http_user_agent.startswith('git'):
89 89 return True
90 90 return False
91 91
92 92
93 93 class SimpleGit(object):
94 94
95 95 def __init__(self, application, config):
96 96 self.application = application
97 97 self.config = config
98 98 #authenticate this git request using
99 99 self.authenticate = AuthBasicAuthenticator('', authfunc)
100 100 self.ipaddr = '0.0.0.0'
101 self.repository = None
101 self.repo_name = None
102 102 self.username = None
103 103 self.action = None
104 104
105 105 def __call__(self, environ, start_response):
106 106 if not is_git(environ):
107 107 return self.application(environ, start_response)
108 108
109 109 proxy_key = 'HTTP_X_REAL_IP'
110 110 def_key = 'REMOTE_ADDR'
111 111 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
112 112 # skip passing error to error controller
113 113 environ['pylons.status_code_redirect'] = True
114 114
115 115 #======================================================================
116 116 # GET ACTION PULL or PUSH
117 117 #======================================================================
118 118 self.action = self.__get_action(environ)
119 119 try:
120 120 #==================================================================
121 121 # GET REPOSITORY NAME
122 122 #==================================================================
123 123 self.repo_name = self.__get_repository(environ)
124 124 except:
125 125 return HTTPInternalServerError()(environ, start_response)
126 126
127 127 #======================================================================
128 128 # CHECK ANONYMOUS PERMISSION
129 129 #======================================================================
130 130 if self.action in ['pull', 'push']:
131 131 anonymous_user = self.__get_user('default')
132 132 self.username = anonymous_user.username
133 133 anonymous_perm = self.__check_permission(self.action,
134 134 anonymous_user,
135 135 self.repo_name)
136 136
137 137 if anonymous_perm is not True or anonymous_user.active is False:
138 138 if anonymous_perm is not True:
139 139 log.debug('Not enough credentials to access this '
140 140 'repository as anonymous user')
141 141 if anonymous_user.active is False:
142 142 log.debug('Anonymous access is disabled, running '
143 143 'authentication')
144 144 #==============================================================
145 145 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
146 146 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
147 147 #==============================================================
148 148
149 149 if not REMOTE_USER(environ):
150 self.authenticate.realm = str(
151 self.config['rhodecode_realm'])
150 self.authenticate.realm = self.config['rhodecode_realm'].\
151 encode('utf8', 'replace')
152 152 result = self.authenticate(environ)
153 153 if isinstance(result, str):
154 154 AUTH_TYPE.update(environ, 'basic')
155 155 REMOTE_USER.update(environ, result)
156 156 else:
157 157 return result.wsgi_application(environ, start_response)
158 158
159 159 #==============================================================
160 160 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
161 161 # BASIC AUTH
162 162 #==============================================================
163 163
164 164 if self.action in ['pull', 'push']:
165 165 username = REMOTE_USER(environ)
166 166 try:
167 167 user = self.__get_user(username)
168 168 self.username = user.username
169 169 except:
170 170 log.error(traceback.format_exc())
171 171 return HTTPInternalServerError()(environ,
172 172 start_response)
173 173
174 174 #check permissions for this repository
175 175 perm = self.__check_permission(self.action, user,
176 176 self.repo_name)
177 177 if perm is not True:
178 178 return HTTPForbidden()(environ, start_response)
179 179
180 180 self.extras = {'ip': self.ipaddr,
181 181 'username': self.username,
182 182 'action': self.action,
183 183 'repository': self.repo_name}
184 184
185 185 #===================================================================
186 186 # GIT REQUEST HANDLING
187 187 #===================================================================
188 188 self.basepath = self.config['base_path']
189 189 self.repo_path = os.path.join(self.basepath, self.repo_name)
190 190 #quick check if that dir exists...
191 191 if check_repo_fast(self.repo_name, self.basepath):
192 192 return HTTPNotFound()(environ, start_response)
193 193 try:
194 194 app = self.__make_app()
195 195 except:
196 196 log.error(traceback.format_exc())
197 197 return HTTPInternalServerError()(environ, start_response)
198 198
199 199 #invalidate cache on push
200 200 if self.action == 'push':
201 201 self.__invalidate_cache(self.repo_name)
202 202
203 203 return app(environ, start_response)
204 204
205 205 def __make_app(self):
206 206 _d = {'/' + self.repo_name: Repo(self.repo_path)}
207 207 backend = dulserver.DictBackend(_d)
208 208 gitserve = HTTPGitApplication(backend)
209 209
210 210 return gitserve
211 211
212 212 def __check_permission(self, action, user, repo_name):
213 213 """Checks permissions using action (push/pull) user and repository
214 214 name
215 215
216 216 :param action: push or pull action
217 217 :param user: user instance
218 218 :param repo_name: repository name
219 219 """
220 220 if action == 'push':
221 221 if not HasPermissionAnyMiddleware('repository.write',
222 222 'repository.admin')(user,
223 223 repo_name):
224 224 return False
225 225
226 226 else:
227 227 #any other action need at least read permission
228 228 if not HasPermissionAnyMiddleware('repository.read',
229 229 'repository.write',
230 230 'repository.admin')(user,
231 231 repo_name):
232 232 return False
233 233
234 234 return True
235 235
236 236 def __get_repository(self, environ):
237 237 """Get's repository name out of PATH_INFO header
238 238
239 239 :param environ: environ where PATH_INFO is stored
240 240 """
241 241 try:
242 242 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
243 243 if repo_name.endswith('/'):
244 244 repo_name = repo_name.rstrip('/')
245 245 except:
246 246 log.error(traceback.format_exc())
247 247 raise
248 248 repo_name = repo_name.split('/')[0]
249 249 return repo_name
250 250
251 251 def __get_user(self, username):
252 252 return UserModel().get_by_username(username, cache=True)
253 253
254 254 def __get_action(self, environ):
255 255 """Maps git request commands into a pull or push command.
256 256
257 257 :param environ:
258 258 """
259 259 service = environ['QUERY_STRING'].split('=')
260 260 if len(service) > 1:
261 261 service_cmd = service[1]
262 262 mapping = {'git-receive-pack': 'push',
263 263 'git-upload-pack': 'pull',
264 264 }
265 265
266 266 return mapping.get(service_cmd,
267 267 service_cmd if service_cmd else 'other')
268 268 else:
269 269 return 'other'
270 270
271 271 def __invalidate_cache(self, repo_name):
272 272 """we know that some change was made to repositories and we should
273 273 invalidate the cache to see the changes right away but only for
274 274 push requests"""
275 275 invalidate_cache('get_repo_cached_%s' % repo_name)
@@ -1,271 +1,271 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) 2009-2010 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
31 31 from mercurial.error import RepoError
32 32 from mercurial.hgweb import hgweb
33 33 from mercurial.hgweb.request import wsgiapplication
34 34
35 35 from paste.auth.basic import AuthBasicAuthenticator
36 36 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
37 37
38 38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
39 39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
40 40 check_repo_fast, ui_sections
41 41 from rhodecode.model.user import UserModel
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 """Returns True if request's target is mercurial server - header
50 50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
51 51 """
52 52 http_accept = environ.get('HTTP_ACCEPT')
53 53 if http_accept and http_accept.startswith('application/mercurial'):
54 54 return True
55 55 return False
56 56
57 57
58 58 class SimpleHg(object):
59 59
60 60 def __init__(self, application, config):
61 61 self.application = application
62 62 self.config = config
63 63 #authenticate this mercurial request using authfunc
64 64 self.authenticate = AuthBasicAuthenticator('', authfunc)
65 65 self.ipaddr = '0.0.0.0'
66 66 self.repo_name = None
67 67 self.username = None
68 68 self.action = None
69 69
70 70 def __call__(self, environ, start_response):
71 71 if not is_mercurial(environ):
72 72 return self.application(environ, start_response)
73 73
74 74 proxy_key = 'HTTP_X_REAL_IP'
75 75 def_key = 'REMOTE_ADDR'
76 76 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
77 77 # skip passing error to error controller
78 78 environ['pylons.status_code_redirect'] = True
79 79
80 80 #======================================================================
81 81 # GET ACTION PULL or PUSH
82 82 #======================================================================
83 83 self.action = self.__get_action(environ)
84 84 try:
85 85 #==================================================================
86 86 # GET REPOSITORY NAME
87 87 #==================================================================
88 88 self.repo_name = self.__get_repository(environ)
89 89 except:
90 90 return HTTPInternalServerError()(environ, start_response)
91 91
92 92 #======================================================================
93 93 # CHECK ANONYMOUS PERMISSION
94 94 #======================================================================
95 95 if self.action in ['pull', 'push']:
96 96 anonymous_user = self.__get_user('default')
97 97 self.username = anonymous_user.username
98 98 anonymous_perm = self.__check_permission(self.action,
99 99 anonymous_user,
100 100 self.repo_name)
101 101
102 102 if anonymous_perm is not True or anonymous_user.active is False:
103 103 if anonymous_perm is not True:
104 104 log.debug('Not enough credentials to access this '
105 105 'repository as anonymous user')
106 106 if anonymous_user.active is False:
107 107 log.debug('Anonymous access is disabled, running '
108 108 'authentication')
109 109 #==============================================================
110 110 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
111 111 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
112 112 #==============================================================
113 113
114 114 if not REMOTE_USER(environ):
115 self.authenticate.realm = str(
116 self.config['rhodecode_realm'])
115 self.authenticate.realm = self.config['rhodecode_realm'].\
116 encode('utf8', 'replace')
117 117 result = self.authenticate(environ)
118 118 if isinstance(result, str):
119 119 AUTH_TYPE.update(environ, 'basic')
120 120 REMOTE_USER.update(environ, result)
121 121 else:
122 122 return result.wsgi_application(environ, start_response)
123 123
124 124 #==============================================================
125 125 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
126 126 # BASIC AUTH
127 127 #==============================================================
128 128
129 129 if self.action in ['pull', 'push']:
130 130 username = REMOTE_USER(environ)
131 131 try:
132 132 user = self.__get_user(username)
133 133 self.username = user.username
134 134 except:
135 135 log.error(traceback.format_exc())
136 136 return HTTPInternalServerError()(environ,
137 137 start_response)
138 138
139 139 #check permissions for this repository
140 140 perm = self.__check_permission(self.action, user,
141 141 self.repo_name)
142 142 if perm is not True:
143 143 return HTTPForbidden()(environ, start_response)
144 144
145 145 self.extras = {'ip': self.ipaddr,
146 146 'username': self.username,
147 147 'action': self.action,
148 148 'repository': self.repo_name}
149 149
150 150 #======================================================================
151 151 # MERCURIAL REQUEST HANDLING
152 152 #======================================================================
153 153 environ['PATH_INFO'] = '/' # since we wrap into hgweb, reset the path
154 154 self.baseui = make_ui('db')
155 155 self.basepath = self.config['base_path']
156 156 self.repo_path = os.path.join(self.basepath, self.repo_name)
157 157
158 158 #quick check if that dir exists...
159 159 if check_repo_fast(self.repo_name, self.basepath):
160 160 return HTTPNotFound()(environ, start_response)
161 161 try:
162 162 app = wsgiapplication(self.__make_app)
163 163 except RepoError, e:
164 164 if str(e).find('not found') != -1:
165 165 return HTTPNotFound()(environ, start_response)
166 166 except Exception:
167 167 log.error(traceback.format_exc())
168 168 return HTTPInternalServerError()(environ, start_response)
169 169
170 170 #invalidate cache on push
171 171 if self.action == 'push':
172 172 self.__invalidate_cache(self.repo_name)
173 173
174 174 return app(environ, start_response)
175 175
176 176 def __make_app(self):
177 177 """
178 178 Make an wsgi application using hgweb, and inject generated baseui
179 179 instance, additionally inject some extras into ui object
180 180 """
181 181 self.__inject_extras(self.baseui, self.extras)
182 182 return hgweb(str(self.repo_path), baseui=self.baseui)
183 183
184 184
185 185 def __check_permission(self, action, user, repo_name):
186 186 """
187 187 Checks permissions using action (push/pull) user and repository
188 188 name
189 189
190 190 :param action: push or pull action
191 191 :param user: user instance
192 192 :param repo_name: repository name
193 193 """
194 194 if action == 'push':
195 195 if not HasPermissionAnyMiddleware('repository.write',
196 196 'repository.admin')(user,
197 197 repo_name):
198 198 return False
199 199
200 200 else:
201 201 #any other action need at least read permission
202 202 if not HasPermissionAnyMiddleware('repository.read',
203 203 'repository.write',
204 204 'repository.admin')(user,
205 205 repo_name):
206 206 return False
207 207
208 208 return True
209 209
210 210 def __get_repository(self, environ):
211 211 """
212 212 Get's repository name out of PATH_INFO header
213 213
214 214 :param environ: environ where PATH_INFO is stored
215 215 """
216 216 try:
217 217 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
218 218 if repo_name.endswith('/'):
219 219 repo_name = repo_name.rstrip('/')
220 220 except:
221 221 log.error(traceback.format_exc())
222 222 raise
223 223
224 224 return repo_name
225 225
226 226 def __get_user(self, username):
227 227 return UserModel().get_by_username(username, cache=True)
228 228
229 229 def __get_action(self, environ):
230 230 """
231 231 Maps mercurial request commands into a clone,pull or push command.
232 232 This should always return a valid command string
233 233
234 234 :param environ:
235 235 """
236 236 mapping = {'changegroup': 'pull',
237 237 'changegroupsubset': 'pull',
238 238 'stream_out': 'pull',
239 239 'listkeys': 'pull',
240 240 'unbundle': 'push',
241 241 'pushkey': 'push', }
242 242 for qry in environ['QUERY_STRING'].split('&'):
243 243 if qry.startswith('cmd'):
244 244 cmd = qry.split('=')[-1]
245 245 if cmd in mapping:
246 246 return mapping[cmd]
247 247 else:
248 248 return 'pull'
249 249
250 250 def __invalidate_cache(self, repo_name):
251 251 """we know that some change was made to repositories and we should
252 252 invalidate the cache to see the changes right away but only for
253 253 push requests"""
254 254 invalidate_cache('get_repo_cached_%s' % repo_name)
255 255
256 256 def __inject_extras(self, baseui, extras={}):
257 257
258 258 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
259 259
260 260 #inject some additional parameters that will be available in ui
261 261 #for hooks
262 262 for k, v in extras.items():
263 263 baseui.setconfig('rhodecode_extras', k, v)
264 264
265 265 repoui = make_ui('file', hgrc, False)
266 266
267 267 if repoui:
268 268 #overwrite our ui instance with the section from hgrc file
269 269 for section in ui_sections:
270 270 for k, v in repoui.configitems(section):
271 271 baseui.repo.ui.setconfig(section, k, v)
General Comments 0
You need to be logged in to leave comments. Login now