##// END OF EJS Templates
Rewrite simehg for enabling cloning with raw url for anonymous access + some optimizations for making less queries when authenticating users....
marcink -
r910:811fa5d4 beta
parent child Browse files
Show More
@@ -76,13 +76,16 b' class SummaryController(BaseController):'
76 e = request.environ
76 e = request.environ
77
77
78 if self.rhodecode_user.username == 'default':
78 if self.rhodecode_user.username == 'default':
79 password = ':default'
79 #for default(anonymous) user we don't need to pass credentials
80 username = ''
81 password = ''
80 else:
82 else:
83 username = str(c.rhodecode_user.username)
81 password = ''
84 password = ''
82
85
83 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
86 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
84 'protocol': e.get('wsgi.url_scheme'),
87 'protocol': e.get('wsgi.url_scheme'),
85 'user':str(c.rhodecode_user.username),
88 'user':username,
86 'password':password,
89 'password':password,
87 'host':e.get('HTTP_HOST'),
90 'host':e.get('HTTP_HOST'),
88 'prefix':e.get('SCRIPT_NAME'),
91 'prefix':e.get('SCRIPT_NAME'),
@@ -26,19 +26,23 b''
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
27 # MA 02110-1301, USA.
28
28
29 import os
30 import logging
31 import traceback
32
29 from mercurial.error import RepoError
33 from mercurial.error import RepoError
30 from mercurial.hgweb import hgweb
34 from mercurial.hgweb import hgweb
31 from mercurial.hgweb.request import wsgiapplication
35 from mercurial.hgweb.request import wsgiapplication
36
32 from paste.auth.basic import AuthBasicAuthenticator
37 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
38 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
39
34 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
40 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
35 from rhodecode.lib.utils import make_ui, invalidate_cache, \
41 from rhodecode.lib.utils import make_ui, invalidate_cache, \
36 check_repo_fast, ui_sections
42 check_repo_fast, ui_sections
37 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
44
38 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
45 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
39 import logging
40 import os
41 import traceback
42
46
43 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
44
48
@@ -59,7 +63,7 b' class SimpleHg(object):'
59 #authenticate this mercurial request using authfunc
63 #authenticate this mercurial request using authfunc
60 self.authenticate = AuthBasicAuthenticator('', authfunc)
64 self.authenticate = AuthBasicAuthenticator('', authfunc)
61 self.ipaddr = '0.0.0.0'
65 self.ipaddr = '0.0.0.0'
62 self.repository = None
66 self.repo_name = None
63 self.username = None
67 self.username = None
64 self.action = None
68 self.action = None
65
69
@@ -72,64 +76,73 b' class SimpleHg(object):'
72 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'))
73 # skip passing error to error controller
77 # skip passing error to error controller
74 environ['pylons.status_code_redirect'] = True
78 environ['pylons.status_code_redirect'] = True
75 #===================================================================
76 # AUTHENTICATE THIS MERCURIAL REQUEST
77 #===================================================================
78 username = REMOTE_USER(environ)
79
79
80 if not username:
80 #======================================================================
81 self.authenticate.realm = self.config['rhodecode_realm']
81 # GET ACTION PULL or PUSH
82 result = self.authenticate(environ)
82 #======================================================================
83 if isinstance(result, str):
83 self.action = self.__get_action(environ)
84 AUTH_TYPE.update(environ, 'basic')
85 REMOTE_USER.update(environ, result)
86 else:
87 return result.wsgi_application(environ, start_response)
88
89 #=======================================================================
90 # GET REPOSITORY
91 #=======================================================================
92 try:
84 try:
93 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
85 #==================================================================
94 if repo_name.endswith('/'):
86 # GET REPOSITORY NAME
95 repo_name = repo_name.rstrip('/')
87 #==================================================================
96 self.repository = repo_name
88 self.repo_name = self.__get_repository(environ)
97 except:
89 except:
98 log.error(traceback.format_exc())
99 return HTTPInternalServerError()(environ, start_response)
90 return HTTPInternalServerError()(environ, start_response)
100
91
101 #===================================================================
92 #======================================================================
102 # CHECK PERMISSIONS FOR THIS REQUEST
93 # CHECK ANONYMOUS PERMISSION
103 #===================================================================
94 #======================================================================
104 self.action = self.__get_action(environ)
95 if self.action in ['pull', 'push']:
105 if self.action:
96 anonymous_user = self.__get_user('default')
106 username = self.__get_environ_user(environ)
97 self.username = anonymous_user.username
107 try:
98 anonymous_perm = self.__check_permission(self.action, anonymous_user ,
108 user = self.__get_user(username)
99 self.repo_name)
109 self.username = user.username
100
110 except:
101 if anonymous_perm is not True or anonymous_user.active is False:
111 log.error(traceback.format_exc())
102 if anonymous_perm is not True:
112 return HTTPInternalServerError()(environ, start_response)
103 log.debug('Not enough credentials to access this repository'
104 'as anonymous user')
105 if anonymous_user.active is False:
106 log.debug('Anonymous access is disabled, running '
107 'authentication')
108 #==================================================================
109 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE NEED
110 # TO AUTHENTICATE AND ASK FOR AUTHENTICATED USER PERMISSIONS
111 #==================================================================
113
112
114 #check permissions for this repository
113 if not REMOTE_USER(environ):
115 if self.action == 'push':
114 self.authenticate.realm = self.config['rhodecode_realm']
116 if not HasPermissionAnyMiddleware('repository.write',
115 result = self.authenticate(environ)
117 'repository.admin')\
116 if isinstance(result, str):
118 (user, repo_name):
117 AUTH_TYPE.update(environ, 'basic')
119 return HTTPForbidden()(environ, start_response)
118 REMOTE_USER.update(environ, result)
119 else:
120 return result.wsgi_application(environ, start_response)
121
120
122
121 else:
123 #==================================================================
122 #any other action need at least read permission
124 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
123 if not HasPermissionAnyMiddleware('repository.read',
125 # BASIC AUTH
124 'repository.write',
126 #==================================================================
125 'repository.admin')\
127
126 (user, repo_name):
128 if self.action in ['pull', 'push']:
127 return HTTPForbidden()(environ, start_response)
129 username = self.__get_environ_user(environ)
130 try:
131 user = self.__get_user(username)
132 self.username = user.username
133 except:
134 log.error(traceback.format_exc())
135 return HTTPInternalServerError()(environ, start_response)
136
137 #check permissions for this repository
138 perm = self.__check_permission(self.action, user, self.repo_name)
139 if perm is not True:
140 return HTTPForbidden()(environ, start_response)
128
141
129 self.extras = {'ip':self.ipaddr,
142 self.extras = {'ip':self.ipaddr,
130 'username':self.username,
143 'username':self.username,
131 'action':self.action,
144 'action':self.action,
132 'repository':self.repository}
145 'repository':self.repo_name}
133
146
134 #===================================================================
147 #===================================================================
135 # MERCURIAL REQUEST HANDLING
148 # MERCURIAL REQUEST HANDLING
@@ -137,10 +150,10 b' class SimpleHg(object):'
137 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
150 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
138 self.baseui = make_ui('db')
151 self.baseui = make_ui('db')
139 self.basepath = self.config['base_path']
152 self.basepath = self.config['base_path']
140 self.repo_path = os.path.join(self.basepath, repo_name)
153 self.repo_path = os.path.join(self.basepath, self.repo_name)
141
154
142 #quick check if that dir exists...
155 #quick check if that dir exists...
143 if check_repo_fast(repo_name, self.basepath):
156 if check_repo_fast(self.repo_name, self.basepath):
144 return HTTPNotFound()(environ, start_response)
157 return HTTPNotFound()(environ, start_response)
145 try:
158 try:
146 app = wsgiapplication(self.__make_app)
159 app = wsgiapplication(self.__make_app)
@@ -153,15 +166,60 b' class SimpleHg(object):'
153
166
154 #invalidate cache on push
167 #invalidate cache on push
155 if self.action == 'push':
168 if self.action == 'push':
156 self.__invalidate_cache(repo_name)
169 self.__invalidate_cache(self.repo_name)
157
170
158 return app(environ, start_response)
171 return app(environ, start_response)
159
172
160
173
161 def __make_app(self):
174 def __make_app(self):
175 """Make an wsgi application using hgweb, and my generated baseui
176 instance
177 """
178
162 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
179 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
163 return self.__load_web_settings(hgserve, self.extras)
180 return self.__load_web_settings(hgserve, self.extras)
164
181
182
183 def __check_permission(self, action, user, repo_name):
184 """Checks permissions using action (push/pull) user and repository
185 name
186
187 :param action: push or pull action
188 :param user: user instance
189 :param repo_name: repository name
190 """
191 if action == 'push':
192 if not HasPermissionAnyMiddleware('repository.write',
193 'repository.admin')\
194 (user, repo_name):
195 return False
196
197 else:
198 #any other action need at least read permission
199 if not HasPermissionAnyMiddleware('repository.read',
200 'repository.write',
201 'repository.admin')\
202 (user, repo_name):
203 return False
204
205 return True
206
207
208 def __get_repository(self, environ):
209 """Get's repository name out of PATH_INFO header
210
211 :param environ: environ where PATH_INFO is stored
212 """
213 try:
214 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
215 if repo_name.endswith('/'):
216 repo_name = repo_name.rstrip('/')
217 except:
218 log.error(traceback.format_exc())
219 raise
220
221 return repo_name
222
165 def __get_environ_user(self, environ):
223 def __get_environ_user(self, environ):
166 return environ.get('REMOTE_USER')
224 return environ.get('REMOTE_USER')
167
225
@@ -171,6 +229,7 b' class SimpleHg(object):'
171 def __get_action(self, environ):
229 def __get_action(self, environ):
172 """Maps mercurial request commands into a clone,pull or push command.
230 """Maps mercurial request commands into a clone,pull or push command.
173 This should always return a valid command string
231 This should always return a valid command string
232
174 :param environ:
233 :param environ:
175 """
234 """
176 mapping = {'changegroup': 'pull',
235 mapping = {'changegroup': 'pull',
@@ -23,7 +23,7 b' from rhodecode.tests import TESTS_TMP_PA'
23 USER = 'test_admin'
23 USER = 'test_admin'
24 PASS = 'test12'
24 PASS = 'test12'
25 HOST = '127.0.0.1:5000'
25 HOST = '127.0.0.1:5000'
26
26 DEBUG = True
27 log = logging.getLogger(__name__)
27 log = logging.getLogger(__name__)
28
28
29
29
@@ -38,16 +38,18 b' class Command(object):'
38
38
39 command = cmd + ' ' + ' '.join(args)
39 command = cmd + ' ' + ' '.join(args)
40 log.debug('Executing %s' % command)
40 log.debug('Executing %s' % command)
41 print command
41 if DEBUG:
42 print command
42 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
43 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
43 stdout, stderr = p.communicate()
44 stdout, stderr = p.communicate()
44 print stdout, stderr
45 if DEBUG:
46 print stdout, stderr
45 return stdout, stderr
47 return stdout, stderr
46
48
47
49
48 #===============================================================================
50 #==============================================================================
49 # TESTS
51 # TESTS
50 #===============================================================================
52 #==============================================================================
51 def test_clone():
53 def test_clone():
52 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
54 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
53
55
@@ -215,9 +217,10 b' def test_push_wrong_path():'
215
217
216 if __name__ == '__main__':
218 if __name__ == '__main__':
217 test_clone()
219 test_clone()
218 test_clone_wrong_credentials()
220
221 #test_clone_wrong_credentials()
219 ##test_clone_anonymous_ok()
222 ##test_clone_anonymous_ok()
220
223 test_pull()
221 #test_push_new_file()
224 #test_push_new_file()
222 #test_push_wrong_path()
225 #test_push_wrong_path()
223 #test_push_wrong_credentials()
226 #test_push_wrong_credentials()
General Comments 0
You need to be logged in to leave comments. Login now