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