##// END OF EJS Templates
changed dulwich git interface to gitweb + subprocessio
marcink -
r2382:034e4fe1 beta
parent child Browse files
Show More
@@ -0,0 +1,181 b''
1 import os
2 import socket
3 import logging
4 import subprocess
5
6 from webob import Request, Response, exc
7
8 from rhodecode.lib import subprocessio
9
10 log = logging.getLogger(__name__)
11
12
13 class FileWrapper(object):
14
15 def __init__(self, fd, content_length):
16 self.fd = fd
17 self.content_length = content_length
18 self.remain = content_length
19
20 def read(self, size):
21 if size <= self.remain:
22 try:
23 data = self.fd.read(size)
24 except socket.error:
25 raise IOError(self)
26 self.remain -= size
27 elif self.remain:
28 data = self.fd.read(self.remain)
29 self.remain = 0
30 else:
31 data = None
32 return data
33
34 def __repr__(self):
35 return '<FileWrapper %s len: %s, read: %s>' % (
36 self.fd, self.content_length, self.content_length - self.remain
37 )
38
39
40 class GitRepository(object):
41 git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
42 commands = ['git-upload-pack', 'git-receive-pack']
43
44 def __init__(self, repo_name, content_path):
45 files = set([f.lower() for f in os.listdir(content_path)])
46 if not (self.git_folder_signature.intersection(files)
47 == self.git_folder_signature):
48 raise OSError('%s missing git signature' % content_path)
49 self.content_path = content_path
50 self.valid_accepts = ['application/x-%s-result' %
51 c for c in self.commands]
52 self.repo_name = repo_name
53
54 def _get_fixedpath(self, path):
55 """
56 Small fix for repo_path
57
58 :param path:
59 :type path:
60 """
61 return path.split(self.repo_name, 1)[-1].strip('/')
62
63 def inforefs(self, request, environ):
64 """
65 WSGI Response producer for HTTP GET Git Smart
66 HTTP /info/refs request.
67 """
68
69 git_command = request.GET['service']
70 if git_command not in self.commands:
71 log.debug('command %s not allowed' % git_command)
72 return exc.HTTPMethodNotAllowed()
73
74 # note to self:
75 # please, resist the urge to add '\n' to git capture and increment
76 # line count by 1.
77 # The code in Git client not only does NOT need '\n', but actually
78 # blows up if you sprinkle "flush" (0000) as "0001\n".
79 # It reads binary, per number of bytes specified.
80 # if you do add '\n' as part of data, count it.
81 smart_server_advert = '# service=%s' % git_command
82 try:
83 out = subprocessio.SubprocessIOChunker(
84 r'git %s --stateless-rpc --advertise-refs "%s"' % (
85 git_command[4:], self.content_path),
86 starting_values=[
87 str(hex(len(smart_server_advert) + 4)[2:]
88 .rjust(4, '0') + smart_server_advert + '0000')
89 ]
90 )
91 except EnvironmentError, e:
92 log.exception(e)
93 raise exc.HTTPExpectationFailed()
94 resp = Response()
95 resp.content_type = 'application/x-%s-advertisement' % str(git_command)
96 resp.app_iter = out
97 return resp
98
99 def backend(self, request, environ):
100 """
101 WSGI Response producer for HTTP POST Git Smart HTTP requests.
102 Reads commands and data from HTTP POST's body.
103 returns an iterator obj with contents of git command's
104 response to stdout
105 """
106 git_command = self._get_fixedpath(request.path_info)
107 if git_command not in self.commands:
108 log.debug('command %s not allowed' % git_command)
109 return exc.HTTPMethodNotAllowed()
110
111 if 'CONTENT_LENGTH' in environ:
112 inputstream = FileWrapper(environ['wsgi.input'],
113 request.content_length)
114 else:
115 inputstream = environ['wsgi.input']
116
117 try:
118 out = subprocessio.SubprocessIOChunker(
119 r'git %s --stateless-rpc "%s"' % (git_command[4:],
120 self.content_path),
121 inputstream=inputstream
122 )
123 except EnvironmentError, e:
124 log.exception(e)
125 raise exc.HTTPExpectationFailed()
126
127 if git_command in [u'git-receive-pack']:
128 # updating refs manually after each push.
129 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
130 subprocess.call(u'git --git-dir "%s" '
131 'update-server-info' % self.content_path,
132 shell=True)
133
134 resp = Response()
135 resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
136 resp.app_iter = out
137 return resp
138
139 def __call__(self, environ, start_response):
140 request = Request(environ)
141 _path = self._get_fixedpath(request.path_info)
142 if _path.startswith('info/refs'):
143 app = self.inforefs
144 elif [a for a in self.valid_accepts if a in request.accept]:
145 app = self.backend
146 try:
147 resp = app(request, environ)
148 except exc.HTTPException, e:
149 resp = e
150 log.exception(e)
151 except Exception, e:
152 log.exception(e)
153 resp = exc.HTTPInternalServerError()
154 return resp(environ, start_response)
155
156
157 class GitDirectory(object):
158
159 def __init__(self, repo_root, repo_name):
160 repo_location = os.path.join(repo_root, repo_name)
161 if not os.path.isdir(repo_location):
162 raise OSError(repo_location)
163
164 self.content_path = repo_location
165 self.repo_name = repo_name
166 self.repo_location = repo_location
167
168 def __call__(self, environ, start_response):
169 content_path = self.content_path
170 try:
171 app = GitRepository(self.repo_name, content_path)
172 except (AssertionError, OSError):
173 if os.path.isdir(os.path.join(content_path, '.git')):
174 app = GitRepository(os.path.join(content_path, '.git'))
175 else:
176 return exc.HTTPNotFound()(environ, start_response)
177 return app(environ, start_response)
178
179
180 def make_wsgi_app(repo_name, repo_root):
181 return GitDirectory(repo_root, repo_name)
@@ -0,0 +1,401 b''
1 '''
2 Module provides a class allowing to wrap communication over subprocess.Popen
3 input, output, error streams into a meaningfull, non-blocking, concurrent
4 stream processor exposing the output data as an iterator fitting to be a
5 return value passed by a WSGI applicaiton to a WSGI server per PEP 3333.
6
7 Copyright (c) 2011 Daniel Dotsenko <dotsa@hotmail.com>
8
9 This file is part of git_http_backend.py Project.
10
11 git_http_backend.py Project is free software: you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public License as
13 published by the Free Software Foundation, either version 2.1 of the License,
14 or (at your option) any later version.
15
16 git_http_backend.py Project is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Lesser General Public License for more details.
20
21 You should have received a copy of the GNU Lesser General Public License
22 along with git_http_backend.py Project.
23 If not, see <http://www.gnu.org/licenses/>.
24 '''
25 import os
26 import subprocess
27 import threading
28 from collections import deque
29
30
31 class StreamFeeder(threading.Thread):
32 """
33 Normal writing into pipe-like is blocking once the buffer is filled.
34 This thread allows a thread to seep data from a file-like into a pipe
35 without blocking the main thread.
36 We close inpipe once the end of the source stream is reached.
37 """
38 def __init__(self, source):
39 super(StreamFeeder, self).__init__()
40 self.daemon = True
41 filelike = False
42 self.bytes = b''
43 if type(source) in (type(''), bytes, bytearray): # string-like
44 self.bytes = bytes(source)
45 else: # can be either file pointer or file-like
46 if type(source) in (int, long): # file pointer it is
47 ## converting file descriptor (int) stdin into file-like
48 try:
49 source = os.fdopen(source, 'rb', 16384)
50 except:
51 pass
52 # let's see if source is file-like by now
53 try:
54 filelike = source.read
55 except:
56 pass
57 if not filelike and not self.bytes:
58 raise TypeError("StreamFeeder's source object must be a readable file-like, a file descriptor, or a string-like.")
59 self.source = source
60 self.readiface, self.writeiface = os.pipe()
61
62 def run(self):
63 t = self.writeiface
64 if self.bytes:
65 os.write(t, self.bytes)
66 else:
67 s = self.source
68 b = s.read(4096)
69 while b:
70 os.write(t, b)
71 b = s.read(4096)
72 os.close(t)
73
74 @property
75 def output(self):
76 return self.readiface
77
78
79 class InputStreamChunker(threading.Thread):
80 def __init__(self, source, target, buffer_size, chunk_size):
81
82 super(InputStreamChunker, self).__init__()
83
84 self.daemon = True # die die die.
85
86 self.source = source
87 self.target = target
88 self.chunk_count_max = int(buffer_size / chunk_size) + 1
89 self.chunk_size = chunk_size
90
91 self.data_added = threading.Event()
92 self.data_added.clear()
93
94 self.keep_reading = threading.Event()
95 self.keep_reading.set()
96
97 self.EOF = threading.Event()
98 self.EOF.clear()
99
100 self.go = threading.Event()
101 self.go.set()
102
103 def stop(self):
104 self.go.clear()
105 self.EOF.set()
106 try:
107 # this is not proper, but is done to force the reader thread let
108 # go of the input because, if successful, .close() will send EOF
109 # down the pipe.
110 self.source.close()
111 except:
112 pass
113
114 def run(self):
115 s = self.source
116 t = self.target
117 cs = self.chunk_size
118 ccm = self.chunk_count_max
119 kr = self.keep_reading
120 da = self.data_added
121 go = self.go
122 b = s.read(cs)
123 while b and go.is_set():
124 if len(t) > ccm:
125 kr.clear()
126 kr.wait(2)
127 # # this only works on 2.7.x and up
128 # if not kr.wait(10):
129 # raise Exception("Timed out while waiting for input to be read.")
130 # instead we'll use this
131 if len(t) > ccm + 3:
132 raise IOError("Timed out while waiting for input from subprocess.")
133 t.append(b)
134 da.set()
135 b = s.read(cs)
136 self.EOF.set()
137 da.set() # for cases when done but there was no input.
138
139
140 class BufferedGenerator():
141 '''
142 Class behaves as a non-blocking, buffered pipe reader.
143 Reads chunks of data (through a thread)
144 from a blocking pipe, and attaches these to an array (Deque) of chunks.
145 Reading is halted in the thread when max chunks is internally buffered.
146 The .next() may operate in blocking or non-blocking fashion by yielding
147 '' if no data is ready
148 to be sent or by not returning until there is some data to send
149 When we get EOF from underlying source pipe we raise the marker to raise
150 StopIteration after the last chunk of data is yielded.
151 '''
152
153 def __init__(self, source, buffer_size=65536, chunk_size=4096,
154 starting_values=[], bottomless=False):
155
156 if bottomless:
157 maxlen = int(buffer_size / chunk_size)
158 else:
159 maxlen = None
160
161 self.data = deque(starting_values, maxlen)
162
163 self.worker = InputStreamChunker(source, self.data, buffer_size,
164 chunk_size)
165 if starting_values:
166 self.worker.data_added.set()
167 self.worker.start()
168
169 ####################
170 # Generator's methods
171 ####################
172
173 def __iter__(self):
174 return self
175
176 def next(self):
177 while not len(self.data) and not self.worker.EOF.is_set():
178 self.worker.data_added.clear()
179 self.worker.data_added.wait(0.2)
180 if len(self.data):
181 self.worker.keep_reading.set()
182 return bytes(self.data.popleft())
183 elif self.worker.EOF.is_set():
184 raise StopIteration
185
186 def throw(self, type, value=None, traceback=None):
187 if not self.worker.EOF.is_set():
188 raise type(value)
189
190 def start(self):
191 self.worker.start()
192
193 def stop(self):
194 self.worker.stop()
195
196 def close(self):
197 try:
198 self.worker.stop()
199 self.throw(GeneratorExit)
200 except (GeneratorExit, StopIteration):
201 pass
202
203 def __del__(self):
204 self.close()
205
206 ####################
207 # Threaded reader's infrastructure.
208 ####################
209 @property
210 def input(self):
211 return self.worker.w
212
213 @property
214 def data_added_event(self):
215 return self.worker.data_added
216
217 @property
218 def data_added(self):
219 return self.worker.data_added.is_set()
220
221 @property
222 def reading_paused(self):
223 return not self.worker.keep_reading.is_set()
224
225 @property
226 def done_reading_event(self):
227 '''
228 Done_reding does not mean that the iterator's buffer is empty.
229 Iterator might have done reading from underlying source, but the read
230 chunks might still be available for serving through .next() method.
231
232 @return An Event class instance.
233 '''
234 return self.worker.EOF
235
236 @property
237 def done_reading(self):
238 '''
239 Done_reding does not mean that the iterator's buffer is empty.
240 Iterator might have done reading from underlying source, but the read
241 chunks might still be available for serving through .next() method.
242
243 @return An Bool value.
244 '''
245 return self.worker.EOF.is_set()
246
247 @property
248 def length(self):
249 '''
250 returns int.
251
252 This is the lenght of the que of chunks, not the length of
253 the combined contents in those chunks.
254
255 __len__() cannot be meaningfully implemented because this
256 reader is just flying throuh a bottomless pit content and
257 can only know the lenght of what it already saw.
258
259 If __len__() on WSGI server per PEP 3333 returns a value,
260 the responce's length will be set to that. In order not to
261 confuse WSGI PEP3333 servers, we will not implement __len__
262 at all.
263 '''
264 return len(self.data)
265
266 def prepend(self, x):
267 self.data.appendleft(x)
268
269 def append(self, x):
270 self.data.append(x)
271
272 def extend(self, o):
273 self.data.extend(o)
274
275 def __getitem__(self, i):
276 return self.data[i]
277
278
279 class SubprocessIOChunker():
280 '''
281 Processor class wrapping handling of subprocess IO.
282
283 In a way, this is a "communicate()" replacement with a twist.
284
285 - We are multithreaded. Writing in and reading out, err are all sep threads.
286 - We support concurrent (in and out) stream processing.
287 - The output is not a stream. It's a queue of read string (bytes, not unicode)
288 chunks. The object behaves as an iterable. You can "for chunk in obj:" us.
289 - We are non-blocking in more respects than communicate()
290 (reading from subprocess out pauses when internal buffer is full, but
291 does not block the parent calling code. On the flip side, reading from
292 slow-yielding subprocess may block the iteration until data shows up. This
293 does not block the parallel inpipe reading occurring parallel thread.)
294
295 The purpose of the object is to allow us to wrap subprocess interactions into
296 and interable that can be passed to a WSGI server as the application's return
297 value. Because of stream-processing-ability, WSGI does not have to read ALL
298 of the subprocess's output and buffer it, before handing it to WSGI server for
299 HTTP response. Instead, the class initializer reads just a bit of the stream
300 to figure out if error ocurred or likely to occur and if not, just hands the
301 further iteration over subprocess output to the server for completion of HTTP
302 response.
303
304 The real or perceived subprocess error is trapped and raised as one of
305 EnvironmentError family of exceptions
306
307 Example usage:
308 # try:
309 # answer = SubprocessIOChunker(
310 # cmd,
311 # input,
312 # buffer_size = 65536,
313 # chunk_size = 4096
314 # )
315 # except (EnvironmentError) as e:
316 # print str(e)
317 # raise e
318 #
319 # return answer
320
321
322 '''
323 def __init__(self, cmd, inputstream=None, buffer_size=65536,
324 chunk_size=4096, starting_values=[]):
325 '''
326 Initializes SubprocessIOChunker
327
328 @param cmd A Subprocess.Popen style "cmd". Can be string or array of strings
329 @param inputstream (Default: None) A file-like, string, or file pointer.
330 @param buffer_size (Default: 65536) A size of total buffer per stream in bytes.
331 @param chunk_size (Default: 4096) A max size of a chunk. Actual chunk may be smaller.
332 @param starting_values (Default: []) An array of strings to put in front of output que.
333 '''
334
335 if inputstream:
336 input_streamer = StreamFeeder(inputstream)
337 input_streamer.start()
338 inputstream = input_streamer.output
339
340 _p = subprocess.Popen(cmd,
341 bufsize=-1,
342 shell=True,
343 stdin=inputstream,
344 stdout=subprocess.PIPE,
345 stderr=subprocess.PIPE
346 )
347
348 bg_out = BufferedGenerator(_p.stdout, buffer_size, chunk_size, starting_values)
349 bg_err = BufferedGenerator(_p.stderr, 16000, 1, bottomless=True)
350
351 while not bg_out.done_reading and not bg_out.reading_paused and not bg_err.length:
352 # doing this until we reach either end of file, or end of buffer.
353 bg_out.data_added_event.wait(1)
354 bg_out.data_added_event.clear()
355
356 # at this point it's still ambiguous if we are done reading or just full buffer.
357 # Either way, if error (returned by ended process, or implied based on
358 # presence of stuff in stderr output) we error out.
359 # Else, we are happy.
360 _returncode = _p.poll()
361 if _returncode or (_returncode == None and bg_err.length):
362 try:
363 _p.terminate()
364 except:
365 pass
366 bg_out.stop()
367 bg_err.stop()
368 raise EnvironmentError("Subprocess exited due to an error.\n" + "".join(bg_err))
369
370 self.process = _p
371 self.output = bg_out
372 self.error = bg_err
373
374 def __iter__(self):
375 return self
376
377 def next(self):
378 if self.process.poll():
379 raise EnvironmentError("Subprocess exited due to an error:\n" + ''.join(self.error))
380 return self.output.next()
381
382 def throw(self, type, value=None, traceback=None):
383 if self.output.length or not self.output.done_reading:
384 raise type(value)
385
386 def close(self):
387 try:
388 self.process.terminate()
389 except:
390 pass
391 try:
392 self.output.close()
393 except:
394 pass
395 try:
396 self.error.close()
397 except:
398 pass
399
400 def __del__(self):
401 self.close()
@@ -1,688 +1,690 b''
1 1 .. _changelog:
2 2
3 3 =========
4 4 Changelog
5 5 =========
6 6
7 7 1.4.0 (**2012-XX-XX**)
8 8 ----------------------
9 9
10 10 :status: in-progress
11 11 :branch: beta
12 12
13 13 news
14 14 ++++
15 15
16 16 - new codereview system
17 17 - email map, allowing users to have multiple email addresses mapped into
18 18 their accounts
19 19 - changed setup-app into setup-rhodecode and added default options to it.
20 20 - new git repos are created as bare now by default
21 21 - #464 added links to groups in permission box
22 22 - #465 mentions autocomplete inside comments boxes
23 23 - #469 added --update-only option to whoosh to re-index only given list
24 24 of repos in index
25 25 - rhodecode-api CLI client
26 - new git http protocol replaced buggy dulwich implementation.
27 Now based on pygrack & gitweb
26 28
27 29 fixes
28 30 +++++
29 31
30 32 - improved translations
31 33 - fixes issue #455 Creating an archive generates an exception on Windows
32 34 - fixes #448 Download ZIP archive keeps file in /tmp open and results
33 35 in out of disk space
34 36 - fixes issue #454 Search results under Windows include proceeding
35 37 backslash
36 38 - fixed issue #450. Rhodecode no longer will crash when bad revision is
37 39 present in journal data.
38 40 - fix for issue #417, git execution was broken on windows for certain
39 41 commands.
40 42 - fixed #413. Don't disable .git directory for bare repos on deleting
41 43 - fixed issue #459. Changed the way of obtaining logger in reindex task.
42 44
43 45 1.3.6 (**2012-05-17**)
44 46 ----------------------
45 47
46 48 news
47 49 ++++
48 50
49 51 - chinese traditional translation
50 52 - changed setup-app into setup-rhodecode and added arguments for auto-setup
51 53 mode that doesn't need user interaction
52 54
53 55 fixes
54 56 +++++
55 57
56 58 - fixed no scm found warning
57 59 - fixed __future__ import error on rcextensions
58 60 - made simplejson required lib for speedup on JSON encoding
59 61 - fixes #449 bad regex could get more than revisions from parsing history
60 62 - don't clear DB session when CELERY_EAGER is turned ON
61 63
62 64 1.3.5 (**2012-05-10**)
63 65 ----------------------
64 66
65 67 news
66 68 ++++
67 69
68 70 - use ext_json for json module
69 71 - unified annotation view with file source view
70 72 - notification improvements, better inbox + css
71 73 - #419 don't strip passwords for login forms, make rhodecode
72 74 more compatible with LDAP servers
73 75 - Added HTTP_X_FORWARDED_FOR as another method of extracting
74 76 IP for pull/push logs. - moved all to base controller
75 77 - #415: Adding comment to changeset causes reload.
76 78 Comments are now added via ajax and doesn't reload the page
77 79 - #374 LDAP config is discarded when LDAP can't be activated
78 80 - limited push/pull operations are now logged for git in the journal
79 81 - bumped mercurial to 2.2.X series
80 82 - added support for displaying submodules in file-browser
81 83 - #421 added bookmarks in changelog view
82 84
83 85 fixes
84 86 +++++
85 87
86 88 - fixed dev-version marker for stable when served from source codes
87 89 - fixed missing permission checks on show forks page
88 90 - #418 cast to unicode fixes in notification objects
89 91 - #426 fixed mention extracting regex
90 92 - fixed remote-pulling for git remotes remopositories
91 93 - fixed #434: Error when accessing files or changesets of a git repository
92 94 with submodules
93 95 - fixed issue with empty APIKEYS for users after registration ref. #438
94 96 - fixed issue with getting README files from git repositories
95 97
96 98 1.3.4 (**2012-03-28**)
97 99 ----------------------
98 100
99 101 news
100 102 ++++
101 103
102 104 - Whoosh logging is now controlled by the .ini files logging setup
103 105 - added clone-url into edit form on /settings page
104 106 - added help text into repo add/edit forms
105 107 - created rcextensions module with additional mappings (ref #322) and
106 108 post push/pull/create repo hooks callbacks
107 109 - implemented #377 Users view for his own permissions on account page
108 110 - #399 added inheritance of permissions for users group on repos groups
109 111 - #401 repository group is automatically pre-selected when adding repos
110 112 inside a repository group
111 113 - added alternative HTTP 403 response when client failed to authenticate. Helps
112 114 solving issues with Mercurial and LDAP
113 115 - #402 removed group prefix from repository name when listing repositories
114 116 inside a group
115 117 - added gravatars into permission view and permissions autocomplete
116 118 - #347 when running multiple RhodeCode instances, properly invalidates cache
117 119 for all registered servers
118 120
119 121 fixes
120 122 +++++
121 123
122 124 - fixed #390 cache invalidation problems on repos inside group
123 125 - fixed #385 clone by ID url was loosing proxy prefix in URL
124 126 - fixed some unicode problems with waitress
125 127 - fixed issue with escaping < and > in changeset commits
126 128 - fixed error occurring during recursive group creation in API
127 129 create_repo function
128 130 - fixed #393 py2.5 fixes for routes url generator
129 131 - fixed #397 Private repository groups shows up before login
130 132 - fixed #396 fixed problems with revoking users in nested groups
131 133 - fixed mysql unicode issues + specified InnoDB as default engine with
132 134 utf8 charset
133 135 - #406 trim long branch/tag names in changelog to not break UI
134 136
135 137 1.3.3 (**2012-03-02**)
136 138 ----------------------
137 139
138 140 news
139 141 ++++
140 142
141 143
142 144 fixes
143 145 +++++
144 146
145 147 - fixed some python2.5 compatibility issues
146 148 - fixed issues with removed repos was accidentally added as groups, after
147 149 full rescan of paths
148 150 - fixes #376 Cannot edit user (using container auth)
149 151 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
150 152 configuration
151 153 - fixed initial sorting of repos inside repo group
152 154 - fixes issue when user tried to resubmit same permission into user/user_groups
153 155 - bumped beaker version that fixes #375 leap error bug
154 156 - fixed raw_changeset for git. It was generated with hg patch headers
155 157 - fixed vcs issue with last_changeset for filenodes
156 158 - fixed missing commit after hook delete
157 159 - fixed #372 issues with git operation detection that caused a security issue
158 160 for git repos
159 161
160 162 1.3.2 (**2012-02-28**)
161 163 ----------------------
162 164
163 165 news
164 166 ++++
165 167
166 168
167 169 fixes
168 170 +++++
169 171
170 172 - fixed git protocol issues with repos-groups
171 173 - fixed git remote repos validator that prevented from cloning remote git repos
172 174 - fixes #370 ending slashes fixes for repo and groups
173 175 - fixes #368 improved git-protocol detection to handle other clients
174 176 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
175 177 Moved To Root
176 178 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
177 179 - fixed #373 missing cascade drop on user_group_to_perm table
178 180
179 181 1.3.1 (**2012-02-27**)
180 182 ----------------------
181 183
182 184 news
183 185 ++++
184 186
185 187
186 188 fixes
187 189 +++++
188 190
189 191 - redirection loop occurs when remember-me wasn't checked during login
190 192 - fixes issues with git blob history generation
191 193 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
192 194
193 195 1.3.0 (**2012-02-26**)
194 196 ----------------------
195 197
196 198 news
197 199 ++++
198 200
199 201 - code review, inspired by github code-comments
200 202 - #215 rst and markdown README files support
201 203 - #252 Container-based and proxy pass-through authentication support
202 204 - #44 branch browser. Filtering of changelog by branches
203 205 - mercurial bookmarks support
204 206 - new hover top menu, optimized to add maximum size for important views
205 207 - configurable clone url template with possibility to specify protocol like
206 208 ssh:// or http:// and also manually alter other parts of clone_url.
207 209 - enabled largefiles extension by default
208 210 - optimized summary file pages and saved a lot of unused space in them
209 211 - #239 option to manually mark repository as fork
210 212 - #320 mapping of commit authors to RhodeCode users
211 213 - #304 hashes are displayed using monospace font
212 214 - diff configuration, toggle white lines and context lines
213 215 - #307 configurable diffs, whitespace toggle, increasing context lines
214 216 - sorting on branches, tags and bookmarks using YUI datatable
215 217 - improved file filter on files page
216 218 - implements #330 api method for listing nodes ar particular revision
217 219 - #73 added linking issues in commit messages to chosen issue tracker url
218 220 based on user defined regular expression
219 221 - added linking of changesets in commit messages
220 222 - new compact changelog with expandable commit messages
221 223 - firstname and lastname are optional in user creation
222 224 - #348 added post-create repository hook
223 225 - #212 global encoding settings is now configurable from .ini files
224 226 - #227 added repository groups permissions
225 227 - markdown gets codehilite extensions
226 228 - new API methods, delete_repositories, grante/revoke permissions for groups
227 229 and repos
228 230
229 231
230 232 fixes
231 233 +++++
232 234
233 235 - rewrote dbsession management for atomic operations, and better error handling
234 236 - fixed sorting of repo tables
235 237 - #326 escape of special html entities in diffs
236 238 - normalized user_name => username in api attributes
237 239 - fixes #298 ldap created users with mixed case emails created conflicts
238 240 on saving a form
239 241 - fixes issue when owner of a repo couldn't revoke permissions for users
240 242 and groups
241 243 - fixes #271 rare JSON serialization problem with statistics
242 244 - fixes #337 missing validation check for conflicting names of a group with a
243 245 repositories group
244 246 - #340 fixed session problem for mysql and celery tasks
245 247 - fixed #331 RhodeCode mangles repository names if the a repository group
246 248 contains the "full path" to the repositories
247 249 - #355 RhodeCode doesn't store encrypted LDAP passwords
248 250
249 251 1.2.5 (**2012-01-28**)
250 252 ----------------------
251 253
252 254 news
253 255 ++++
254 256
255 257 fixes
256 258 +++++
257 259
258 260 - #340 Celery complains about MySQL server gone away, added session cleanup
259 261 for celery tasks
260 262 - #341 "scanning for repositories in None" log message during Rescan was missing
261 263 a parameter
262 264 - fixed creating archives with subrepos. Some hooks were triggered during that
263 265 operation leading to crash.
264 266 - fixed missing email in account page.
265 267 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
266 268 forking on windows impossible
267 269
268 270 1.2.4 (**2012-01-19**)
269 271 ----------------------
270 272
271 273 news
272 274 ++++
273 275
274 276 - RhodeCode is bundled with mercurial series 2.0.X by default, with
275 277 full support to largefiles extension. Enabled by default in new installations
276 278 - #329 Ability to Add/Remove Groups to/from a Repository via AP
277 279 - added requires.txt file with requirements
278 280
279 281 fixes
280 282 +++++
281 283
282 284 - fixes db session issues with celery when emailing admins
283 285 - #331 RhodeCode mangles repository names if the a repository group
284 286 contains the "full path" to the repositories
285 287 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
286 288 - DB session cleanup after hg protocol operations, fixes issues with
287 289 `mysql has gone away` errors
288 290 - #333 doc fixes for get_repo api function
289 291 - #271 rare JSON serialization problem with statistics enabled
290 292 - #337 Fixes issues with validation of repository name conflicting with
291 293 a group name. A proper message is now displayed.
292 294 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
293 295 doesn't work
294 296 - #316 fixes issues with web description in hgrc files
295 297
296 298 1.2.3 (**2011-11-02**)
297 299 ----------------------
298 300
299 301 news
300 302 ++++
301 303
302 304 - added option to manage repos group for non admin users
303 305 - added following API methods for get_users, create_user, get_users_groups,
304 306 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
305 307 get_repo, create_repo, add_user_to_repo
306 308 - implements #237 added password confirmation for my account
307 309 and admin edit user.
308 310 - implements #291 email notification for global events are now sent to all
309 311 administrator users, and global config email.
310 312
311 313 fixes
312 314 +++++
313 315
314 316 - added option for passing auth method for smtp mailer
315 317 - #276 issue with adding a single user with id>10 to usergroups
316 318 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
317 319 - #288 fixes managing of repos in a group for non admin user
318 320
319 321 1.2.2 (**2011-10-17**)
320 322 ----------------------
321 323
322 324 news
323 325 ++++
324 326
325 327 - #226 repo groups are available by path instead of numerical id
326 328
327 329 fixes
328 330 +++++
329 331
330 332 - #259 Groups with the same name but with different parent group
331 333 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
332 334 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
333 335 - #265 ldap save fails sometimes on converting attributes to booleans,
334 336 added getter and setter into model that will prevent from this on db model level
335 337 - fixed problems with timestamps issues #251 and #213
336 338 - fixes #266 RhodeCode allows to create repo with the same name and in
337 339 the same parent as group
338 340 - fixes #245 Rescan of the repositories on Windows
339 341 - fixes #248 cannot edit repos inside a group on windows
340 342 - fixes #219 forking problems on windows
341 343
342 344 1.2.1 (**2011-10-08**)
343 345 ----------------------
344 346
345 347 news
346 348 ++++
347 349
348 350
349 351 fixes
350 352 +++++
351 353
352 354 - fixed problems with basic auth and push problems
353 355 - gui fixes
354 356 - fixed logger
355 357
356 358 1.2.0 (**2011-10-07**)
357 359 ----------------------
358 360
359 361 news
360 362 ++++
361 363
362 364 - implemented #47 repository groups
363 365 - implemented #89 Can setup google analytics code from settings menu
364 366 - implemented #91 added nicer looking archive urls with more download options
365 367 like tags, branches
366 368 - implemented #44 into file browsing, and added follow branch option
367 369 - implemented #84 downloads can be enabled/disabled for each repository
368 370 - anonymous repository can be cloned without having to pass default:default
369 371 into clone url
370 372 - fixed #90 whoosh indexer can index chooses repositories passed in command
371 373 line
372 374 - extended journal with day aggregates and paging
373 375 - implemented #107 source code lines highlight ranges
374 376 - implemented #93 customizable changelog on combined revision ranges -
375 377 equivalent of githubs compare view
376 378 - implemented #108 extended and more powerful LDAP configuration
377 379 - implemented #56 users groups
378 380 - major code rewrites optimized codes for speed and memory usage
379 381 - raw and diff downloads are now in git format
380 382 - setup command checks for write access to given path
381 383 - fixed many issues with international characters and unicode. It uses utf8
382 384 decode with replace to provide less errors even with non utf8 encoded strings
383 385 - #125 added API KEY access to feeds
384 386 - #109 Repository can be created from external Mercurial link (aka. remote
385 387 repository, and manually updated (via pull) from admin panel
386 388 - beta git support - push/pull server + basic view for git repos
387 389 - added followers page and forks page
388 390 - server side file creation (with binary file upload interface)
389 391 and edition with commits powered by codemirror
390 392 - #111 file browser file finder, quick lookup files on whole file tree
391 393 - added quick login sliding menu into main page
392 394 - changelog uses lazy loading of affected files details, in some scenarios
393 395 this can improve speed of changelog page dramatically especially for
394 396 larger repositories.
395 397 - implements #214 added support for downloading subrepos in download menu.
396 398 - Added basic API for direct operations on rhodecode via JSON
397 399 - Implemented advanced hook management
398 400
399 401 fixes
400 402 +++++
401 403
402 404 - fixed file browser bug, when switching into given form revision the url was
403 405 not changing
404 406 - fixed propagation to error controller on simplehg and simplegit middlewares
405 407 - fixed error when trying to make a download on empty repository
406 408 - fixed problem with '[' chars in commit messages in journal
407 409 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
408 410 - journal fork fixes
409 411 - removed issue with space inside renamed repository after deletion
410 412 - fixed strange issue on formencode imports
411 413 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
412 414 - #150 fixes for errors on repositories mapped in db but corrupted in
413 415 filesystem
414 416 - fixed problem with ascendant characters in realm #181
415 417 - fixed problem with sqlite file based database connection pool
416 418 - whoosh indexer and code stats share the same dynamic extensions map
417 419 - fixes #188 - relationship delete of repo_to_perm entry on user removal
418 420 - fixes issue #189 Trending source files shows "show more" when no more exist
419 421 - fixes issue #197 Relative paths for pidlocks
420 422 - fixes issue #198 password will require only 3 chars now for login form
421 423 - fixes issue #199 wrong redirection for non admin users after creating a repository
422 424 - fixes issues #202, bad db constraint made impossible to attach same group
423 425 more than one time. Affects only mysql/postgres
424 426 - fixes #218 os.kill patch for windows was missing sig param
425 427 - improved rendering of dag (they are not trimmed anymore when number of
426 428 heads exceeds 5)
427 429
428 430 1.1.8 (**2011-04-12**)
429 431 ----------------------
430 432
431 433 news
432 434 ++++
433 435
434 436 - improved windows support
435 437
436 438 fixes
437 439 +++++
438 440
439 441 - fixed #140 freeze of python dateutil library, since new version is python2.x
440 442 incompatible
441 443 - setup-app will check for write permission in given path
442 444 - cleaned up license info issue #149
443 445 - fixes for issues #137,#116 and problems with unicode and accented characters.
444 446 - fixes crashes on gravatar, when passed in email as unicode
445 447 - fixed tooltip flickering problems
446 448 - fixed came_from redirection on windows
447 449 - fixed logging modules, and sql formatters
448 450 - windows fixes for os.kill issue #133
449 451 - fixes path splitting for windows issues #148
450 452 - fixed issue #143 wrong import on migration to 1.1.X
451 453 - fixed problems with displaying binary files, thanks to Thomas Waldmann
452 454 - removed name from archive files since it's breaking ui for long repo names
453 455 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
454 456 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
455 457 Thomas Waldmann
456 458 - fixed issue #166 summary pager was skipping 10 revisions on second page
457 459
458 460
459 461 1.1.7 (**2011-03-23**)
460 462 ----------------------
461 463
462 464 news
463 465 ++++
464 466
465 467 fixes
466 468 +++++
467 469
468 470 - fixed (again) #136 installation support for FreeBSD
469 471
470 472
471 473 1.1.6 (**2011-03-21**)
472 474 ----------------------
473 475
474 476 news
475 477 ++++
476 478
477 479 fixes
478 480 +++++
479 481
480 482 - fixed #136 installation support for FreeBSD
481 483 - RhodeCode will check for python version during installation
482 484
483 485 1.1.5 (**2011-03-17**)
484 486 ----------------------
485 487
486 488 news
487 489 ++++
488 490
489 491 - basic windows support, by exchanging pybcrypt into sha256 for windows only
490 492 highly inspired by idea of mantis406
491 493
492 494 fixes
493 495 +++++
494 496
495 497 - fixed sorting by author in main page
496 498 - fixed crashes with diffs on binary files
497 499 - fixed #131 problem with boolean values for LDAP
498 500 - fixed #122 mysql problems thanks to striker69
499 501 - fixed problem with errors on calling raw/raw_files/annotate functions
500 502 with unknown revisions
501 503 - fixed returned rawfiles attachment names with international character
502 504 - cleaned out docs, big thanks to Jason Harris
503 505
504 506 1.1.4 (**2011-02-19**)
505 507 ----------------------
506 508
507 509 news
508 510 ++++
509 511
510 512 fixes
511 513 +++++
512 514
513 515 - fixed formencode import problem on settings page, that caused server crash
514 516 when that page was accessed as first after server start
515 517 - journal fixes
516 518 - fixed option to access repository just by entering http://server/<repo_name>
517 519
518 520 1.1.3 (**2011-02-16**)
519 521 ----------------------
520 522
521 523 news
522 524 ++++
523 525
524 526 - implemented #102 allowing the '.' character in username
525 527 - added option to access repository just by entering http://server/<repo_name>
526 528 - celery task ignores result for better performance
527 529
528 530 fixes
529 531 +++++
530 532
531 533 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
532 534 apollo13 and Johan Walles
533 535 - small fixes in journal
534 536 - fixed problems with getting setting for celery from .ini files
535 537 - registration, password reset and login boxes share the same title as main
536 538 application now
537 539 - fixed #113: to high permissions to fork repository
538 540 - fixed problem with '[' chars in commit messages in journal
539 541 - removed issue with space inside renamed repository after deletion
540 542 - db transaction fixes when filesystem repository creation failed
541 543 - fixed #106 relation issues on databases different than sqlite
542 544 - fixed static files paths links to use of url() method
543 545
544 546 1.1.2 (**2011-01-12**)
545 547 ----------------------
546 548
547 549 news
548 550 ++++
549 551
550 552
551 553 fixes
552 554 +++++
553 555
554 556 - fixes #98 protection against float division of percentage stats
555 557 - fixed graph bug
556 558 - forced webhelpers version since it was making troubles during installation
557 559
558 560 1.1.1 (**2011-01-06**)
559 561 ----------------------
560 562
561 563 news
562 564 ++++
563 565
564 566 - added force https option into ini files for easier https usage (no need to
565 567 set server headers with this options)
566 568 - small css updates
567 569
568 570 fixes
569 571 +++++
570 572
571 573 - fixed #96 redirect loop on files view on repositories without changesets
572 574 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
573 575 and server crashed with errors
574 576 - fixed large tooltips problems on main page
575 577 - fixed #92 whoosh indexer is more error proof
576 578
577 579 1.1.0 (**2010-12-18**)
578 580 ----------------------
579 581
580 582 news
581 583 ++++
582 584
583 585 - rewrite of internals for vcs >=0.1.10
584 586 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
585 587 with older clients
586 588 - anonymous access, authentication via ldap
587 589 - performance upgrade for cached repos list - each repository has its own
588 590 cache that's invalidated when needed.
589 591 - performance upgrades on repositories with large amount of commits (20K+)
590 592 - main page quick filter for filtering repositories
591 593 - user dashboards with ability to follow chosen repositories actions
592 594 - sends email to admin on new user registration
593 595 - added cache/statistics reset options into repository settings
594 596 - more detailed action logger (based on hooks) with pushed changesets lists
595 597 and options to disable those hooks from admin panel
596 598 - introduced new enhanced changelog for merges that shows more accurate results
597 599 - new improved and faster code stats (based on pygments lexers mapping tables,
598 600 showing up to 10 trending sources for each repository. Additionally stats
599 601 can be disabled in repository settings.
600 602 - gui optimizations, fixed application width to 1024px
601 603 - added cut off (for large files/changesets) limit into config files
602 604 - whoosh, celeryd, upgrade moved to paster command
603 605 - other than sqlite database backends can be used
604 606
605 607 fixes
606 608 +++++
607 609
608 610 - fixes #61 forked repo was showing only after cache expired
609 611 - fixes #76 no confirmation on user deletes
610 612 - fixes #66 Name field misspelled
611 613 - fixes #72 block user removal when he owns repositories
612 614 - fixes #69 added password confirmation fields
613 615 - fixes #87 RhodeCode crashes occasionally on updating repository owner
614 616 - fixes #82 broken annotations on files with more than 1 blank line at the end
615 617 - a lot of fixes and tweaks for file browser
616 618 - fixed detached session issues
617 619 - fixed when user had no repos he would see all repos listed in my account
618 620 - fixed ui() instance bug when global hgrc settings was loaded for server
619 621 instance and all hgrc options were merged with our db ui() object
620 622 - numerous small bugfixes
621 623
622 624 (special thanks for TkSoh for detailed feedback)
623 625
624 626
625 627 1.0.2 (**2010-11-12**)
626 628 ----------------------
627 629
628 630 news
629 631 ++++
630 632
631 633 - tested under python2.7
632 634 - bumped sqlalchemy and celery versions
633 635
634 636 fixes
635 637 +++++
636 638
637 639 - fixed #59 missing graph.js
638 640 - fixed repo_size crash when repository had broken symlinks
639 641 - fixed python2.5 crashes.
640 642
641 643
642 644 1.0.1 (**2010-11-10**)
643 645 ----------------------
644 646
645 647 news
646 648 ++++
647 649
648 650 - small css updated
649 651
650 652 fixes
651 653 +++++
652 654
653 655 - fixed #53 python2.5 incompatible enumerate calls
654 656 - fixed #52 disable mercurial extension for web
655 657 - fixed #51 deleting repositories don't delete it's dependent objects
656 658
657 659
658 660 1.0.0 (**2010-11-02**)
659 661 ----------------------
660 662
661 663 - security bugfix simplehg wasn't checking for permissions on commands
662 664 other than pull or push.
663 665 - fixed doubled messages after push or pull in admin journal
664 666 - templating and css corrections, fixed repo switcher on chrome, updated titles
665 667 - admin menu accessible from options menu on repository view
666 668 - permissions cached queries
667 669
668 670 1.0.0rc4 (**2010-10-12**)
669 671 --------------------------
670 672
671 673 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
672 674 - removed cache_manager settings from sqlalchemy meta
673 675 - added sqlalchemy cache settings to ini files
674 676 - validated password length and added second try of failure on paster setup-app
675 677 - fixed setup database destroy prompt even when there was no db
676 678
677 679
678 680 1.0.0rc3 (**2010-10-11**)
679 681 -------------------------
680 682
681 683 - fixed i18n during installation.
682 684
683 685 1.0.0rc2 (**2010-10-11**)
684 686 -------------------------
685 687
686 688 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
687 689 occure. After vcs is fixed it'll be put back again.
688 690 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,301 +1,303 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplegit
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 7 It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import re
29 29 import logging
30 30 import traceback
31 31
32 32 from dulwich import server as dulserver
33 33
34 34
35 35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36 36
37 37 def handle(self):
38 38 write = lambda x: self.proto.write_sideband(1, x)
39 39
40 40 graph_walker = dulserver.ProtocolGraphWalker(self,
41 41 self.repo.object_store,
42 42 self.repo.get_peeled)
43 43 objects_iter = self.repo.fetch_objects(
44 44 graph_walker.determine_wants, graph_walker, self.progress,
45 45 get_tagged=self.get_tagged)
46 46
47 47 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
48 48 # that the client still expects a 0-object pack in most cases.
49 49 if objects_iter is None:
50 50 return
51 51
52 52 self.progress("counting objects: %d, done.\n" % len(objects_iter))
53 53 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
54 54 objects_iter)
55 55 messages = []
56 56 messages.append('thank you for using rhodecode')
57 57
58 58 for msg in messages:
59 59 self.progress(msg + "\n")
60 60 # we are done
61 61 self.proto.write("0000")
62 62
63 63
64 64 dulserver.DEFAULT_HANDLERS = {
65 65 #git-ls-remote, git-clone, git-fetch and git-pull
66 66 'git-upload-pack': SimpleGitUploadPackHandler,
67 67 #git-push
68 68 'git-receive-pack': dulserver.ReceivePackHandler,
69 69 }
70 70
71 71 from dulwich.repo import Repo
72 72 from dulwich.web import make_wsgi_chain
73 73
74 74 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
75 75
76 76 from rhodecode.lib.utils2 import safe_str
77 77 from rhodecode.lib.base import BaseVCSController
78 78 from rhodecode.lib.auth import get_container_username
79 79 from rhodecode.lib.utils import is_valid_repo, make_ui
80 80 from rhodecode.model.db import User
81 81
82 82 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
83 83
84 84 log = logging.getLogger(__name__)
85 85
86 86
87 87 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
88 88
89 89
90 90 def is_git(environ):
91 91 path_info = environ['PATH_INFO']
92 92 isgit_path = GIT_PROTO_PAT.match(path_info)
93 93 log.debug('pathinfo: %s detected as GIT %s' % (
94 94 path_info, isgit_path != None)
95 95 )
96 96 return isgit_path
97 97
98 98
99 99 class SimpleGit(BaseVCSController):
100 100
101 101 def _handle_request(self, environ, start_response):
102 102
103 103 if not is_git(environ):
104 104 return self.application(environ, start_response)
105 105
106 106 ipaddr = self._get_ip_addr(environ)
107 107 username = None
108 108 self._git_first_op = False
109 109 # skip passing error to error controller
110 110 environ['pylons.status_code_redirect'] = True
111 111
112 112 #======================================================================
113 113 # EXTRACT REPOSITORY NAME FROM ENV
114 114 #======================================================================
115 115 try:
116 116 repo_name = self.__get_repository(environ)
117 117 log.debug('Extracted repo name is %s' % repo_name)
118 118 except:
119 119 return HTTPInternalServerError()(environ, start_response)
120 120
121 121 # quick check if that dir exists...
122 122 if is_valid_repo(repo_name, self.basepath) is False:
123 123 return HTTPNotFound()(environ, start_response)
124 124
125 125 #======================================================================
126 126 # GET ACTION PULL or PUSH
127 127 #======================================================================
128 128 action = self.__get_action(environ)
129 129
130 130 #======================================================================
131 131 # CHECK ANONYMOUS PERMISSION
132 132 #======================================================================
133 133 if action in ['pull', 'push']:
134 134 anonymous_user = self.__get_user('default')
135 135 username = anonymous_user.username
136 136 anonymous_perm = self._check_permission(action, anonymous_user,
137 137 repo_name)
138 138
139 139 if anonymous_perm is not True or anonymous_user.active is False:
140 140 if anonymous_perm is not True:
141 141 log.debug('Not enough credentials to access this '
142 142 'repository as anonymous user')
143 143 if anonymous_user.active is False:
144 144 log.debug('Anonymous access is disabled, running '
145 145 'authentication')
146 146 #==============================================================
147 147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
148 148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
149 149 #==============================================================
150 150
151 151 # Attempting to retrieve username from the container
152 152 username = get_container_username(environ, self.config)
153 153
154 154 # If not authenticated by the container, running basic auth
155 155 if not username:
156 156 self.authenticate.realm = \
157 157 safe_str(self.config['rhodecode_realm'])
158 158 result = self.authenticate(environ)
159 159 if isinstance(result, str):
160 160 AUTH_TYPE.update(environ, 'basic')
161 161 REMOTE_USER.update(environ, result)
162 162 username = result
163 163 else:
164 164 return result.wsgi_application(environ, start_response)
165 165
166 166 #==============================================================
167 167 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
168 168 #==============================================================
169 169 if action in ['pull', 'push']:
170 170 try:
171 171 user = self.__get_user(username)
172 172 if user is None or not user.active:
173 173 return HTTPForbidden()(environ, start_response)
174 174 username = user.username
175 175 except:
176 176 log.error(traceback.format_exc())
177 177 return HTTPInternalServerError()(environ,
178 178 start_response)
179 179
180 180 #check permissions for this repository
181 181 perm = self._check_permission(action, user, repo_name)
182 182 if perm is not True:
183 183 return HTTPForbidden()(environ, start_response)
184 184 extras = {
185 185 'ip': ipaddr,
186 186 'username': username,
187 187 'action': action,
188 188 'repository': repo_name,
189 189 'scm': 'git',
190 190 }
191 191
192 192 #===================================================================
193 193 # GIT REQUEST HANDLING
194 194 #===================================================================
195 195 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
196 196 log.debug('Repository path is %s' % repo_path)
197 197
198 198 baseui = make_ui('db')
199 199 self.__inject_extras(repo_path, baseui, extras)
200 200
201 201 try:
202 202 # invalidate cache on push
203 203 if action == 'push':
204 204 self._invalidate_cache(repo_name)
205 205 self._handle_githooks(repo_name, action, baseui, environ)
206 206
207 207 log.info('%s action on GIT repo "%s"' % (action, repo_name))
208 208 app = self.__make_app(repo_name, repo_path)
209 209 return app(environ, start_response)
210 210 except Exception:
211 211 log.error(traceback.format_exc())
212 212 return HTTPInternalServerError()(environ, start_response)
213 213
214 214 def __make_app(self, repo_name, repo_path):
215 215 """
216 216 Make an wsgi application using dulserver
217 217
218 218 :param repo_name: name of the repository
219 219 :param repo_path: full path to the repository
220 220 """
221 _d = {'/' + repo_name: Repo(repo_path)}
222 backend = dulserver.DictBackend(_d)
223 gitserve = make_wsgi_chain(backend)
224 221
225 return gitserve
222 from rhodecode.lib.middleware.pygrack import make_wsgi_app
223 app = make_wsgi_app(
224 repo_root=os.path.dirname(repo_path),
225 repo_name=repo_name,
226 )
227 return app
226 228
227 229 def __get_repository(self, environ):
228 230 """
229 231 Get's repository name out of PATH_INFO header
230 232
231 233 :param environ: environ where PATH_INFO is stored
232 234 """
233 235 try:
234 236 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
235 237 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
236 238 except:
237 239 log.error(traceback.format_exc())
238 240 raise
239 241
240 242 return repo_name
241 243
242 244 def __get_user(self, username):
243 245 return User.get_by_username(username)
244 246
245 247 def __get_action(self, environ):
246 248 """
247 249 Maps git request commands into a pull or push command.
248 250
249 251 :param environ:
250 252 """
251 253 service = environ['QUERY_STRING'].split('=')
252 254
253 255 if len(service) > 1:
254 256 service_cmd = service[1]
255 257 mapping = {
256 258 'git-receive-pack': 'push',
257 259 'git-upload-pack': 'pull',
258 260 }
259 261 op = mapping[service_cmd]
260 262 self._git_stored_op = op
261 263 return op
262 264 else:
263 265 # try to fallback to stored variable as we don't know if the last
264 266 # operation is pull/push
265 267 op = getattr(self, '_git_stored_op', 'pull')
266 268 return op
267 269
268 270 def _handle_githooks(self, repo_name, action, baseui, environ):
269 271 from rhodecode.lib.hooks import log_pull_action, log_push_action
270 272 service = environ['QUERY_STRING'].split('=')
271 273 if len(service) < 2:
272 274 return
273 275
274 276 from rhodecode.model.db import Repository
275 277 _repo = Repository.get_by_repo_name(repo_name)
276 278 _repo = _repo.scm_instance
277 279 _repo._repo.ui = baseui
278 280
279 281 push_hook = 'pretxnchangegroup.push_logger'
280 282 pull_hook = 'preoutgoing.pull_logger'
281 283 _hooks = dict(baseui.configitems('hooks')) or {}
282 284 if action == 'push' and _hooks.get(push_hook):
283 285 log_push_action(ui=baseui, repo=_repo._repo)
284 286 elif action == 'pull' and _hooks.get(pull_hook):
285 287 log_pull_action(ui=baseui, repo=_repo._repo)
286 288
287 289 def __inject_extras(self, repo_path, baseui, extras={}):
288 290 """
289 291 Injects some extra params into baseui instance
290 292
291 293 :param baseui: baseui instance
292 294 :param extras: dict with extra params to put into baseui
293 295 """
294 296
295 297 # make our hgweb quiet so it doesn't print output
296 298 baseui.setconfig('ui', 'quiet', 'true')
297 299
298 300 #inject some additional parameters that will be available in ui
299 301 #for hooks
300 302 for k, v in extras.items():
301 303 baseui.setconfig('rhodecode_extras', k, v)
General Comments 0
You need to be logged in to leave comments. Login now