##// END OF EJS Templates
Making RhodeCode ready for dulwich 0.8.4
Tony Bussieres -
r2137:462a845c beta
parent child Browse files
Show More
@@ -1,17 +1,17 b''
1 1 Pylons==1.0.0
2 2 Beaker==1.6.3
3 3 WebHelpers==1.3
4 4 formencode==1.2.4
5 5 SQLAlchemy==0.7.5
6 6 Mako==0.5.0
7 7 pygments>=1.4
8 8 whoosh>=2.3.0,<2.4
9 9 celery>=2.2.5,<2.3
10 10 babel
11 11 python-dateutil>=1.5.0,<2.0.0
12 dulwich>=0.8.0,<0.9.0
12 dulwich>=0.8.4,<0.9.0
13 13 webob==1.0.8
14 14 markdown==2.1.1
15 15 docutils==0.8.1
16 16 py-bcrypt
17 mercurial>=2.1,<2.2 No newline at end of file
17 mercurial>=2.1,<2.2
@@ -1,251 +1,251 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplegit
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 7 It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import re
29 29 import logging
30 30 import traceback
31 31
32 32 from dulwich import server as dulserver
33 33
34 34
35 35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36 36
37 37 def handle(self):
38 38 write = lambda x: self.proto.write_sideband(1, x)
39 39
40 40 graph_walker = dulserver.ProtocolGraphWalker(self,
41 41 self.repo.object_store,
42 42 self.repo.get_peeled)
43 43 objects_iter = self.repo.fetch_objects(
44 44 graph_walker.determine_wants, graph_walker, self.progress,
45 45 get_tagged=self.get_tagged)
46 46
47 47 # Do they want any objects?
48 48 if objects_iter is None or len(objects_iter) == 0:
49 49 return
50 50
51 51 self.progress("counting objects: %d, done.\n" % len(objects_iter))
52 52 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
53 53 objects_iter, len(objects_iter))
54 54 messages = []
55 55 messages.append('thank you for using rhodecode')
56 56
57 57 for msg in messages:
58 58 self.progress(msg + "\n")
59 59 # we are done
60 60 self.proto.write("0000")
61 61
62 62 dulserver.DEFAULT_HANDLERS = {
63 63 'git-upload-pack': SimpleGitUploadPackHandler,
64 64 'git-receive-pack': dulserver.ReceivePackHandler,
65 65 }
66 66
67 67 from dulwich.repo import Repo
68 from dulwich.web import HTTPGitApplication
68 from dulwich.web import make_wsgi_chain
69 69
70 70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
71 71
72 72 from rhodecode.lib.utils2 import safe_str
73 73 from rhodecode.lib.base import BaseVCSController
74 74 from rhodecode.lib.auth import get_container_username
75 75 from rhodecode.lib.utils import is_valid_repo
76 76 from rhodecode.model.db import User
77 77
78 78 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
79 79
80 80 log = logging.getLogger(__name__)
81 81
82 82
83 83 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
84 84
85 85
86 86 def is_git(environ):
87 87 path_info = environ['PATH_INFO']
88 88 isgit_path = GIT_PROTO_PAT.match(path_info)
89 89 log.debug('pathinfo: %s detected as GIT %s' % (
90 90 path_info, isgit_path != None)
91 91 )
92 92 return isgit_path
93 93
94 94
95 95 class SimpleGit(BaseVCSController):
96 96
97 97 def _handle_request(self, environ, start_response):
98 98
99 99 if not is_git(environ):
100 100 return self.application(environ, start_response)
101 101
102 102 proxy_key = 'HTTP_X_REAL_IP'
103 103 def_key = 'REMOTE_ADDR'
104 104 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
105 105 username = None
106 106 # skip passing error to error controller
107 107 environ['pylons.status_code_redirect'] = True
108 108
109 109 #======================================================================
110 110 # EXTRACT REPOSITORY NAME FROM ENV
111 111 #======================================================================
112 112 try:
113 113 repo_name = self.__get_repository(environ)
114 114 log.debug('Extracted repo name is %s' % repo_name)
115 115 except:
116 116 return HTTPInternalServerError()(environ, start_response)
117 117
118 118 #======================================================================
119 119 # GET ACTION PULL or PUSH
120 120 #======================================================================
121 121 action = self.__get_action(environ)
122 122
123 123 #======================================================================
124 124 # CHECK ANONYMOUS PERMISSION
125 125 #======================================================================
126 126 if action in ['pull', 'push']:
127 127 anonymous_user = self.__get_user('default')
128 128 username = anonymous_user.username
129 129 anonymous_perm = self._check_permission(action, anonymous_user,
130 130 repo_name)
131 131
132 132 if anonymous_perm is not True or anonymous_user.active is False:
133 133 if anonymous_perm is not True:
134 134 log.debug('Not enough credentials to access this '
135 135 'repository as anonymous user')
136 136 if anonymous_user.active is False:
137 137 log.debug('Anonymous access is disabled, running '
138 138 'authentication')
139 139 #==============================================================
140 140 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
141 141 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
142 142 #==============================================================
143 143
144 144 # Attempting to retrieve username from the container
145 145 username = get_container_username(environ, self.config)
146 146
147 147 # If not authenticated by the container, running basic auth
148 148 if not username:
149 149 self.authenticate.realm = \
150 150 safe_str(self.config['rhodecode_realm'])
151 151 result = self.authenticate(environ)
152 152 if isinstance(result, str):
153 153 AUTH_TYPE.update(environ, 'basic')
154 154 REMOTE_USER.update(environ, result)
155 155 username = result
156 156 else:
157 157 return result.wsgi_application(environ, start_response)
158 158
159 159 #==============================================================
160 160 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
161 161 #==============================================================
162 162 if action in ['pull', 'push']:
163 163 try:
164 164 user = self.__get_user(username)
165 165 if user is None or not user.active:
166 166 return HTTPForbidden()(environ, start_response)
167 167 username = user.username
168 168 except:
169 169 log.error(traceback.format_exc())
170 170 return HTTPInternalServerError()(environ,
171 171 start_response)
172 172
173 173 #check permissions for this repository
174 174 perm = self._check_permission(action, user, repo_name)
175 175 if perm is not True:
176 176 return HTTPForbidden()(environ, start_response)
177 177
178 178 #===================================================================
179 179 # GIT REQUEST HANDLING
180 180 #===================================================================
181 181 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
182 182 log.debug('Repository path is %s' % repo_path)
183 183
184 184 # quick check if that dir exists...
185 185 if is_valid_repo(repo_name, self.basepath) is False:
186 186 return HTTPNotFound()(environ, start_response)
187 187
188 188 try:
189 189 #invalidate cache on push
190 190 if action == 'push':
191 191 self._invalidate_cache(repo_name)
192 192 log.info('%s action on GIT repo "%s"' % (action, repo_name))
193 193 app = self.__make_app(repo_name, repo_path)
194 194 return app(environ, start_response)
195 195 except Exception:
196 196 log.error(traceback.format_exc())
197 197 return HTTPInternalServerError()(environ, start_response)
198 198
199 199 def __make_app(self, repo_name, repo_path):
200 200 """
201 201 Make an wsgi application using dulserver
202 202
203 203 :param repo_name: name of the repository
204 204 :param repo_path: full path to the repository
205 205 """
206 206 _d = {'/' + repo_name: Repo(repo_path)}
207 207 backend = dulserver.DictBackend(_d)
208 gitserve = HTTPGitApplication(backend)
208 gitserve = make_wsgi_chain(backend)
209 209
210 210 return gitserve
211 211
212 212 def __get_repository(self, environ):
213 213 """
214 214 Get's repository name out of PATH_INFO header
215 215
216 216 :param environ: environ where PATH_INFO is stored
217 217 """
218 218 try:
219 219 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
220 220 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
221 221 except:
222 222 log.error(traceback.format_exc())
223 223 raise
224 224
225 225 return repo_name
226 226
227 227 def __get_user(self, username):
228 228 return User.get_by_username(username)
229 229
230 230 def __get_action(self, environ):
231 231 """
232 232 Maps git request commands into a pull or push command.
233 233
234 234 :param environ:
235 235 """
236 236 service = environ['QUERY_STRING'].split('=')
237 237
238 238 if len(service) > 1:
239 239 service_cmd = service[1]
240 240 mapping = {
241 241 'git-receive-pack': 'push',
242 242 'git-upload-pack': 'pull',
243 243 }
244 244 op = mapping[service_cmd]
245 245 self._git_stored_op = op
246 246 return op
247 247 else:
248 248 # try to fallback to stored variable as we don't know if the last
249 249 # operation is pull/push
250 250 op = getattr(self, '_git_stored_op', 'pull')
251 251 return op
General Comments 0
You need to be logged in to leave comments. Login now