# HG changeset patch # User Marcin Kuzminski # Date 1287363998 -7200 # Node ID 72bed56219d6c5ed974b0aa3f1b7d4154f16adb2 # Parent 5cc96df705b9fef9832b01af56c2f37a5ef0e856 security bugfix simplehg wasn't checking for permissions on remote commands different than pull or push. diff -r 5cc96df705b9 -r 72bed56219d6 rhodecode/lib/middleware/simplehg.py --- a/rhodecode/lib/middleware/simplehg.py Mon Oct 18 03:04:58 2010 +0200 +++ b/rhodecode/lib/middleware/simplehg.py Mon Oct 18 03:06:38 2010 +0200 @@ -39,7 +39,7 @@ import logging import os import traceback - + log = logging.getLogger(__name__) class SimpleHg(object): @@ -49,7 +49,7 @@ self.config = config #authenticate this mercurial request using self.authenticate = AuthBasicAuthenticator('', authfunc) - + def __call__(self, environ, start_response): if not is_mercurial(environ): return self.application(environ, start_response) @@ -66,7 +66,7 @@ REMOTE_USER.update(environ, result) else: return result.wsgi_application(environ, start_response) - + try: repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) if repo_name.endswith('/'): @@ -74,7 +74,7 @@ except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) - + #=================================================================== # CHECK PERMISSIONS FOR THIS REQUEST #=================================================================== @@ -87,24 +87,26 @@ log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository - if action == 'pull': + + if action == 'push': + if not HasPermissionAnyMiddleware('repository.write', + 'repository.admin')\ + (user, repo_name): + return HTTPForbidden()(environ, start_response) + + else: if not HasPermissionAnyMiddleware('repository.read', 'repository.write', 'repository.admin')\ (user, repo_name): return HTTPForbidden()(environ, start_response) - if action == 'push': - if not HasPermissionAnyMiddleware('repository.write', - 'repository.admin')\ - (user, repo_name): - return HTTPForbidden()(environ, start_response) - + #log action proxy_key = 'HTTP_X_REAL_IP' def_key = 'REMOTE_ADDR' ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) - self.__log_user_action(user, action, repo_name, ipaddr) - + self.__log_user_action(user, action, repo_name, ipaddr) + #=================================================================== # MERCURIAL REQUEST HANDLING #=================================================================== @@ -124,16 +126,16 @@ except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) - + #invalidate cache on push if action == 'push': self.__invalidate_cache(repo_name) messages = [] messages.append('thank you for using rhodecode') - + return self.msg_wrapper(app, environ, start_response, messages) else: - return app(environ, start_response) + return app(environ, start_response) def msg_wrapper(self, app, environ, start_response, messages=[]): @@ -141,9 +143,9 @@ Wrapper for custom messages that come out of mercurial respond messages is a list of messages that the user will see at the end of response from merurial protocol actions that involves remote answers - @param app: - @param environ: - @param start_response: + :param app: + :param environ: + :param start_response: """ def custom_messages(msg_list): for msg in msg_list: @@ -154,17 +156,18 @@ def __make_app(self): hgserve = hgweb(str(self.repo_path), baseui=self.baseui) return self.__load_web_settings(hgserve) - + def __get_environ_user(self, environ): return environ.get('REMOTE_USER') - + def __get_user(self, username): return get_user_cached(username) - + def __get_action(self, environ): """ Maps mercurial request commands into a pull or push command. - @param environ: + This should return generally always something + :param environ: """ mapping = {'changegroup': 'pull', 'changegroupsubset': 'pull', @@ -172,50 +175,51 @@ 'listkeys': 'pull', 'unbundle': 'push', 'pushkey': 'push', } - for qry in environ['QUERY_STRING'].split('&'): if qry.startswith('cmd'): cmd = qry.split('=')[-1] if mapping.has_key(cmd): return mapping[cmd] - + else: + return cmd + def __log_user_action(self, user, action, repo, ipaddr): action_logger(user, action, repo, ipaddr) - + def __invalidate_cache(self, repo_name): """we know that some change was made to repositories and we should invalidate the cache to see the changes right away but only for push requests""" invalidate_cache('cached_repo_list') invalidate_cache('full_changelog', repo_name) - - + + def __load_web_settings(self, hgserve): - #set the global ui for hgserve + #set the global ui for hgserve instance passed hgserve.repo.ui = self.baseui - + hgrc = os.path.join(self.repo_path, '.hg', 'hgrc') repoui = make_ui('file', hgrc, False) - - + + if repoui: #overwrite our ui instance with the section from hgrc file for section in ui_sections: for k, v in repoui.configitems(section): hgserve.repo.ui.setconfig(section, k, v) - + return hgserve - - - - - - - - - - - - - - + + + + + + + + + + + + + +