##// END OF EJS Templates
security bugfix simplehg wasn't checking for permissions on remote commands different than pull or push.
marcink -
r605:72bed562 default
parent child Browse files
Show More
@@ -39,7 +39,7 b' from rhodecode.lib.utils import action_l'
39 import logging
39 import logging
40 import os
40 import os
41 import traceback
41 import traceback
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45 class SimpleHg(object):
45 class SimpleHg(object):
@@ -49,7 +49,7 b' class SimpleHg(object):'
49 self.config = config
49 self.config = config
50 #authenticate this mercurial request using
50 #authenticate this mercurial request using
51 self.authenticate = AuthBasicAuthenticator('', authfunc)
51 self.authenticate = AuthBasicAuthenticator('', authfunc)
52
52
53 def __call__(self, environ, start_response):
53 def __call__(self, environ, start_response):
54 if not is_mercurial(environ):
54 if not is_mercurial(environ):
55 return self.application(environ, start_response)
55 return self.application(environ, start_response)
@@ -66,7 +66,7 b' class SimpleHg(object):'
66 REMOTE_USER.update(environ, result)
66 REMOTE_USER.update(environ, result)
67 else:
67 else:
68 return result.wsgi_application(environ, start_response)
68 return result.wsgi_application(environ, start_response)
69
69
70 try:
70 try:
71 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
71 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
72 if repo_name.endswith('/'):
72 if repo_name.endswith('/'):
@@ -74,7 +74,7 b' class SimpleHg(object):'
74 except:
74 except:
75 log.error(traceback.format_exc())
75 log.error(traceback.format_exc())
76 return HTTPInternalServerError()(environ, start_response)
76 return HTTPInternalServerError()(environ, start_response)
77
77
78 #===================================================================
78 #===================================================================
79 # CHECK PERMISSIONS FOR THIS REQUEST
79 # CHECK PERMISSIONS FOR THIS REQUEST
80 #===================================================================
80 #===================================================================
@@ -87,24 +87,26 b' class SimpleHg(object):'
87 log.error(traceback.format_exc())
87 log.error(traceback.format_exc())
88 return HTTPInternalServerError()(environ, start_response)
88 return HTTPInternalServerError()(environ, start_response)
89 #check permissions for this repository
89 #check permissions for this repository
90 if action == 'pull':
90
91 if action == 'push':
92 if not HasPermissionAnyMiddleware('repository.write',
93 'repository.admin')\
94 (user, repo_name):
95 return HTTPForbidden()(environ, start_response)
96
97 else:
91 if not HasPermissionAnyMiddleware('repository.read',
98 if not HasPermissionAnyMiddleware('repository.read',
92 'repository.write',
99 'repository.write',
93 'repository.admin')\
100 'repository.admin')\
94 (user, repo_name):
101 (user, repo_name):
95 return HTTPForbidden()(environ, start_response)
102 return HTTPForbidden()(environ, start_response)
96 if action == 'push':
103
97 if not HasPermissionAnyMiddleware('repository.write',
98 'repository.admin')\
99 (user, repo_name):
100 return HTTPForbidden()(environ, start_response)
101
102 #log action
104 #log action
103 proxy_key = 'HTTP_X_REAL_IP'
105 proxy_key = 'HTTP_X_REAL_IP'
104 def_key = 'REMOTE_ADDR'
106 def_key = 'REMOTE_ADDR'
105 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
107 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
106 self.__log_user_action(user, action, repo_name, ipaddr)
108 self.__log_user_action(user, action, repo_name, ipaddr)
107
109
108 #===================================================================
110 #===================================================================
109 # MERCURIAL REQUEST HANDLING
111 # MERCURIAL REQUEST HANDLING
110 #===================================================================
112 #===================================================================
@@ -124,16 +126,16 b' class SimpleHg(object):'
124 except Exception:
126 except Exception:
125 log.error(traceback.format_exc())
127 log.error(traceback.format_exc())
126 return HTTPInternalServerError()(environ, start_response)
128 return HTTPInternalServerError()(environ, start_response)
127
129
128 #invalidate cache on push
130 #invalidate cache on push
129 if action == 'push':
131 if action == 'push':
130 self.__invalidate_cache(repo_name)
132 self.__invalidate_cache(repo_name)
131 messages = []
133 messages = []
132 messages.append('thank you for using rhodecode')
134 messages.append('thank you for using rhodecode')
133
135
134 return self.msg_wrapper(app, environ, start_response, messages)
136 return self.msg_wrapper(app, environ, start_response, messages)
135 else:
137 else:
136 return app(environ, start_response)
138 return app(environ, start_response)
137
139
138
140
139 def msg_wrapper(self, app, environ, start_response, messages=[]):
141 def msg_wrapper(self, app, environ, start_response, messages=[]):
@@ -141,9 +143,9 b' class SimpleHg(object):'
141 Wrapper for custom messages that come out of mercurial respond messages
143 Wrapper for custom messages that come out of mercurial respond messages
142 is a list of messages that the user will see at the end of response
144 is a list of messages that the user will see at the end of response
143 from merurial protocol actions that involves remote answers
145 from merurial protocol actions that involves remote answers
144 @param app:
146 :param app:
145 @param environ:
147 :param environ:
146 @param start_response:
148 :param start_response:
147 """
149 """
148 def custom_messages(msg_list):
150 def custom_messages(msg_list):
149 for msg in msg_list:
151 for msg in msg_list:
@@ -154,17 +156,18 b' class SimpleHg(object):'
154 def __make_app(self):
156 def __make_app(self):
155 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
157 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
156 return self.__load_web_settings(hgserve)
158 return self.__load_web_settings(hgserve)
157
159
158 def __get_environ_user(self, environ):
160 def __get_environ_user(self, environ):
159 return environ.get('REMOTE_USER')
161 return environ.get('REMOTE_USER')
160
162
161 def __get_user(self, username):
163 def __get_user(self, username):
162 return get_user_cached(username)
164 return get_user_cached(username)
163
165
164 def __get_action(self, environ):
166 def __get_action(self, environ):
165 """
167 """
166 Maps mercurial request commands into a pull or push command.
168 Maps mercurial request commands into a pull or push command.
167 @param environ:
169 This should return generally always something
170 :param environ:
168 """
171 """
169 mapping = {'changegroup': 'pull',
172 mapping = {'changegroup': 'pull',
170 'changegroupsubset': 'pull',
173 'changegroupsubset': 'pull',
@@ -172,50 +175,51 b' class SimpleHg(object):'
172 'listkeys': 'pull',
175 'listkeys': 'pull',
173 'unbundle': 'push',
176 'unbundle': 'push',
174 'pushkey': 'push', }
177 'pushkey': 'push', }
175
176 for qry in environ['QUERY_STRING'].split('&'):
178 for qry in environ['QUERY_STRING'].split('&'):
177 if qry.startswith('cmd'):
179 if qry.startswith('cmd'):
178 cmd = qry.split('=')[-1]
180 cmd = qry.split('=')[-1]
179 if mapping.has_key(cmd):
181 if mapping.has_key(cmd):
180 return mapping[cmd]
182 return mapping[cmd]
181
183 else:
184 return cmd
185
182 def __log_user_action(self, user, action, repo, ipaddr):
186 def __log_user_action(self, user, action, repo, ipaddr):
183 action_logger(user, action, repo, ipaddr)
187 action_logger(user, action, repo, ipaddr)
184
188
185 def __invalidate_cache(self, repo_name):
189 def __invalidate_cache(self, repo_name):
186 """we know that some change was made to repositories and we should
190 """we know that some change was made to repositories and we should
187 invalidate the cache to see the changes right away but only for
191 invalidate the cache to see the changes right away but only for
188 push requests"""
192 push requests"""
189 invalidate_cache('cached_repo_list')
193 invalidate_cache('cached_repo_list')
190 invalidate_cache('full_changelog', repo_name)
194 invalidate_cache('full_changelog', repo_name)
191
195
192
196
193 def __load_web_settings(self, hgserve):
197 def __load_web_settings(self, hgserve):
194 #set the global ui for hgserve
198 #set the global ui for hgserve instance passed
195 hgserve.repo.ui = self.baseui
199 hgserve.repo.ui = self.baseui
196
200
197 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
201 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
198 repoui = make_ui('file', hgrc, False)
202 repoui = make_ui('file', hgrc, False)
199
203
200
204
201 if repoui:
205 if repoui:
202 #overwrite our ui instance with the section from hgrc file
206 #overwrite our ui instance with the section from hgrc file
203 for section in ui_sections:
207 for section in ui_sections:
204 for k, v in repoui.configitems(section):
208 for k, v in repoui.configitems(section):
205 hgserve.repo.ui.setconfig(section, k, v)
209 hgserve.repo.ui.setconfig(section, k, v)
206
210
207 return hgserve
211 return hgserve
208
212
209
213
210
214
211
215
212
216
213
217
214
218
215
219
216
220
217
221
218
222
219
223
220
224
221
225
General Comments 0
You need to be logged in to leave comments. Login now