def _authenticate(self): ctx = self.ctx authkey = ctx.req.params.get('authkey') cookie = ctx.req.cookies.get('assnet_auth') authby = ctx.req.params.get('authby') valid_user = None if authby == 'http': username = REMOTE_USER(ctx.req.environ) if not username: username = self.basic_auther(ctx.req.environ) if isinstance(username, str): if username == '_key': username = ctx.req.environ['key_username'] AUTH_TYPE.update(ctx.req.environ, 'basic') REMOTE_USER.update(ctx.req.environ, username) valid_user = ctx.storage.get_user(username) else: raise WSGIMethodException(username.wsgi_application) if authkey: for user in ctx.storage.iter_users(): if authkey == user.key: # set the cookie for the following requests valid_user = user elif cookie: signer = AuthCookieSigner(secret=ctx.cookie_secret) username = signer.auth(cookie) if username: username = username.decode('utf-8') valid_user = ctx.storage.get_user(username) if valid_user: has_cookies = cookie and 'assnet_session' in ctx.req.cookies ctx.login(valid_user, set_cookie=not has_cookies)
def __call__(self, environ, start_response): if not is_git(environ): return self.application(environ, start_response) proxy_key = 'HTTP_X_REAL_IP' def_key = 'REMOTE_ADDR' ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) username = None # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # EXTRACT REPOSITORY NAME FROM ENV #====================================================================== try: repo_name = self.__get_repository(environ) log.debug('Extracted repo name is %s' % repo_name) except: return HTTPInternalServerError()(environ, start_response) #====================================================================== # GET ACTION PULL or PUSH #====================================================================== action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if action in ['pull', 'push']: anonymous_user = self.__get_user('default') username = anonymous_user.username anonymous_perm = self.__check_permission(action, anonymous_user, repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: log.debug('Not enough credentials to access this ' 'repository as anonymous user') if anonymous_user.active is False: log.debug('Anonymous access is disabled, running ' 'authentication') #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== if not REMOTE_USER(environ): self.authenticate.realm = \ safe_str(self.config['rhodecode_realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM # BASIC AUTH #============================================================== if action in ['pull', 'push']: username = REMOTE_USER(environ) try: user = self.__get_user(username) username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self.__check_permission(action, user, repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) extras = {'ip': ipaddr, 'username': username, 'action': action, 'repository': repo_name} #=================================================================== # GIT REQUEST HANDLING #=================================================================== repo_path = safe_str(os.path.join(self.basepath, repo_name)) log.debug('Repository path is %s' % repo_path) # quick check if that dir exists... if is_valid_repo(repo_name, self.basepath) is False: return HTTPNotFound()(environ, start_response) try: #invalidate cache on push if action == 'push': self.__invalidate_cache(repo_name) app = self.__make_app(repo_name, repo_path) return app(environ, start_response) except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response)
def _handle_request(self, environ, start_response): if not is_mercurial(environ): return self.application(environ, start_response) ipaddr = self._get_ip_addr(environ) # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # EXTRACT REPOSITORY NAME FROM ENV #====================================================================== try: repo_name = environ['REPO_NAME'] = self.__get_repository(environ) log.debug('Extracted repo name is %s' % repo_name) except: return HTTPInternalServerError()(environ, start_response) # quick check if that dir exists... if is_valid_repo(repo_name, self.basepath) is False: return HTTPNotFound()(environ, start_response) #====================================================================== # GET ACTION PULL or PUSH #====================================================================== action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if action in ['pull', 'push']: anonymous_user = self.__get_user('default') username = anonymous_user.username anonymous_perm = self._check_permission(action, anonymous_user, repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: log.debug('Not enough credentials to access this ' 'repository as anonymous user') if anonymous_user.active is False: log.debug('Anonymous access is disabled, running ' 'authentication') #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== # Attempting to retrieve username from the container username = get_container_username(environ, self.config) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = \ safe_str(self.config['rhodecode_realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME #============================================================== if action in ['pull', 'push']: try: user = self.__get_user(username) if user is None or not user.active: return HTTPForbidden()(environ, start_response) username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self._check_permission(action, user, repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) # extras are injected into mercurial UI object and later available # in hg hooks executed by rhodecode extras = { 'ip': ipaddr, 'username': username, 'action': action, 'repository': repo_name, 'scm': 'hg', } #====================================================================== # MERCURIAL REQUEST HANDLING #====================================================================== repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name)) log.debug('Repository path is %s' % repo_path) baseui = make_ui('db') self.__inject_extras(repo_path, baseui, extras) try: # invalidate cache on push if action == 'push': self._invalidate_cache(repo_name) log.info('%s action on HG repo "%s"' % (action, repo_name)) app = self.__make_app(repo_path, baseui, extras) return app(environ, start_response) except RepoError, e: if str(e).find('not found') != -1: return HTTPNotFound()(environ, start_response)
def __call__(self, environ, start_response): if 'PATH_INFO' in environ: path_info = environ['PATH_INFO'].lstrip('/') if path_info == 'repository/reset_all_metadata': self.setting_repository_metadata = True cmd = self.__get_hg_command(**environ) # The 'getbundle' command indicates that a mercurial client is getting a bundle of one or more changesets, indicating # a clone or a pull. However, we do not want to increment the times_downloaded count if we're only setting repository # metadata. if cmd == 'getbundle' and not self.setting_repository_metadata: common, _ = environ['HTTP_X_HGARG_1'].split('&') # The 'common' parameter indicates the full sha-1 hash of the changeset the client currently has checked out. If # this is 0000000000000000000000000000000000000000, then the client is performing a fresh checkout. If it has any # other value, the client is getting updates to an existing checkout. if common == 'common=0000000000000000000000000000000000000000': # Increment the value of the times_downloaded column in the repository table for the cloned repository. if 'PATH_INFO' in environ: # Instantiate a database connection engine = sqlalchemy.create_engine(self.db_url) connection = engine.connect() path_info = environ['PATH_INFO'].lstrip('/') user_id, repository_name = self.__get_user_id_repository_name_from_path_info( connection, path_info) sql_cmd = "SELECT times_downloaded FROM repository WHERE user_id = %d AND name = '%s'" % \ ( user_id, repository_name.lower() ) result_set = connection.execute(sql_cmd) for row in result_set: # Should only be 1 row... times_downloaded = row['times_downloaded'] times_downloaded += 1 sql_cmd = "UPDATE repository SET times_downloaded = %d WHERE user_id = %d AND name = '%s'" % \ ( times_downloaded, user_id, repository_name.lower() ) connection.execute(sql_cmd) connection.close() elif cmd in ['unbundle', 'pushkey']: # This is an hg push from the command line. When doing this, the following commands, in order, # will be retrieved from environ (see the docs at http://mercurial.selenic.com/wiki/WireProtocol): # # If mercurial version >= '2.2.3': capabilities -> batch -> branchmap -> unbundle -> listkeys -> pushkey -> listkeys # # The mercurial API unbundle() ( i.e., hg push ) and pushkey() methods ultimately require authorization. # We'll force password entry every time a change set is pushed. # # When a user executes hg commit, it is not guaranteed to succeed. Mercurial records your name # and address with each change that you commit, so that you and others will later be able to # tell who made each change. Mercurial tries to automatically figure out a sensible username # to commit the change with. It will attempt each of the following methods, in order: # # 1) If you specify a -u option to the hg commit command on the command line, followed by a username, # this is always given the highest precedence. # 2) If you have set the HGUSER environment variable, this is checked next. # 3) If you create a file in your home directory called .hgrc with a username entry, that # will be used next. # 4) If you have set the EMAIL environment variable, this will be used next. # 5) Mercurial will query your system to find out your local user name and host name, and construct # a username from these components. Since this often results in a username that is not very useful, # it will print a warning if it has to do this. # # If all of these mechanisms fail, Mercurial will fail, printing an error message. In this case, it # will not let you commit until you set up a username. result = self.authentication(environ) if not isinstance( result, str) and cmd == 'unbundle' and 'wsgi.input' in environ: bundle_data_stream = environ['wsgi.input'] # Convert the incoming mercurial bundle into a json object and persit it to a temporary file for inspection. fh = tempfile.NamedTemporaryFile('wb', prefix="tmp-hg-bundle") tmp_filename = fh.name fh.close() fh = open(tmp_filename, 'wb') while 1: chunk = bundle_data_stream.read(CHUNK_SIZE) if not chunk: break fh.write(chunk) fh.close() fh = open(tmp_filename, 'rb') changeset_groups = json.loads(hg_util.bundle_to_json(fh)) fh.close() try: os.unlink(tmp_filename) except: pass if changeset_groups: # Check the repository type to make sure inappropriate files are not being pushed. if 'PATH_INFO' in environ: # Instantiate a database connection engine = sqlalchemy.create_engine(self.db_url) connection = engine.connect() path_info = environ['PATH_INFO'].lstrip('/') user_id, repository_name = self.__get_user_id_repository_name_from_path_info( connection, path_info) sql_cmd = "SELECT type FROM repository WHERE user_id = %d AND name = '%s'" % ( user_id, repository_name.lower()) result_set = connection.execute(sql_cmd) for row in result_set: # Should only be 1 row... repository_type = str(row['type']) if repository_type == rt_util.REPOSITORY_SUITE_DEFINITION: # Handle repositories of type repository_suite_definition, which can only contain a single # file named repository_dependencies.xml. for entry in changeset_groups: if len(entry) == 2: # We possibly found an altered file entry. filename, change_list = entry if filename and isinstance(filename, str): if filename == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME: # Make sure the any complex repository dependency definitions contain valid <repository> tags. is_valid, error_msg = self.repository_tags_are_valid( filename, change_list) if not is_valid: log.debug(error_msg) return self.__display_exception_remotely( start_response, error_msg) else: msg = "Only a single file named repository_dependencies.xml can be pushed to a repository " msg += "of type 'Repository suite definition'." log.debug(msg) return self.__display_exception_remotely( start_response, msg) elif repository_type == rt_util.TOOL_DEPENDENCY_DEFINITION: # Handle repositories of type tool_dependency_definition, which can only contain a single # file named tool_dependencies.xml. for entry in changeset_groups: if len(entry) == 2: # We possibly found an altered file entry. filename, change_list = entry if filename and isinstance(filename, str): if filename == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME: # Make sure the any complex repository dependency definitions contain valid <repository> tags. is_valid, error_msg = self.repository_tags_are_valid( filename, change_list) if not is_valid: log.debug(error_msg) return self.__display_exception_remotely( start_response, error_msg) else: msg = "Only a single file named tool_dependencies.xml can be pushed to a repository " msg += "of type 'Tool dependency definition'." log.debug(msg) return self.__display_exception_remotely( start_response, msg) else: # If the changeset includes changes to dependency definition files, make sure tag sets # are not missing "toolshed" or "changeset_revision" attributes since automatically populating # them is not supported when pushing from the command line. These attributes are automatically # populated only when using the tool shed upload utility. for entry in changeset_groups: if len(entry) == 2: # We possibly found an altered file entry. filename, change_list = entry if filename and isinstance(filename, str): if filename in [ rt_util. REPOSITORY_DEPENDENCY_DEFINITION_FILENAME, rt_util. TOOL_DEPENDENCY_DEFINITION_FILENAME ]: # We check both files since tool dependency definitions files can contain complex # repository dependency definitions. is_valid, error_msg = self.repository_tags_are_valid( filename, change_list) if not is_valid: log.debug(error_msg) return self.__display_exception_remotely( start_response, error_msg) if isinstance(result, str): # Authentication was successful AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) else: return result.wsgi_application(environ, start_response) return self.app(environ, start_response)
def _handle_request(self, environ, start_response): if not is_git(environ): return self.application(environ, start_response) if not self._check_ssl(environ, start_response): return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response) ipaddr = self._get_ip_addr(environ) username = None self._git_first_op = False # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # EXTRACT REPOSITORY NAME FROM ENV #====================================================================== try: repo_name = self.__get_repository(environ) log.debug('Extracted repo name is %s' % repo_name) except: return HTTPInternalServerError()(environ, start_response) # quick check if that dir exists... if is_valid_repo(repo_name, self.basepath, 'git') is False: return HTTPNotFound()(environ, start_response) #====================================================================== # GET ACTION PULL or PUSH #====================================================================== action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if action in ['pull', 'push']: anonymous_user = self.__get_user('default') username = anonymous_user.username anonymous_perm = self._check_permission(action, anonymous_user, repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: log.debug('Not enough credentials to access this ' 'repository as anonymous user') if anonymous_user.active is False: log.debug('Anonymous access is disabled, running ' 'authentication') #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== # Attempting to retrieve username from the container username = get_container_username(environ, self.config) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = \ safe_str(self.config['rhodecode_realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME #============================================================== try: user = self.__get_user(username) if user is None or not user.active: return HTTPForbidden()(environ, start_response) username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self._check_permission(action, user, repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) # extras are injected into UI object and later available # in hooks executed by rhodecode from rhodecode import CONFIG extras = { 'ip': ipaddr, 'username': username, 'action': action, 'repository': repo_name, 'scm': 'git', 'config': CONFIG['__file__'], 'make_lock': None, 'locked_by': [None, None] } #=================================================================== # GIT REQUEST HANDLING #=================================================================== repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name)) log.debug('Repository path is %s' % repo_path) # CHECK LOCKING only if it's not ANONYMOUS USER if username != User.DEFAULT_USER: log.debug('Checking locking on repository') (make_lock, locked, locked_by) = self._check_locking_state( environ=environ, action=action, repo=repo_name, user_id=user.user_id ) # store the make_lock for later evaluation in hooks extras.update({'make_lock': make_lock, 'locked_by': locked_by}) # set the environ variables for this request os.environ['RC_SCM_DATA'] = json.dumps(extras) fix_PATH() log.debug('HOOKS extras is %s' % extras) baseui = make_ui('db') self.__inject_extras(repo_path, baseui, extras) try: # invalidate cache on push if action == 'push': self._invalidate_cache(repo_name) self._handle_githooks(repo_name, action, baseui, environ) log.info('%s action on GIT repo "%s"' % (action, repo_name)) app = self.__make_app(repo_name, repo_path, extras) return app(environ, start_response) except HTTPLockedRC, e: log.debug('Repositry LOCKED ret code 423!') return e(environ, start_response)
def _handle_request(self, environ, start_response): if not is_git(environ): return self.application(environ, start_response) if not self._check_ssl(environ): return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response) ip_addr = self._get_ip_addr(environ) username = None self._git_first_op = False # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # EXTRACT REPOSITORY NAME FROM ENV #====================================================================== try: str_repo_name = self.__get_repository(environ) repo_name = safe_unicode(str_repo_name) log.debug('Extracted repo name is %s', repo_name) except Exception as e: log.error('error extracting repo_name: %r', e) return HTTPInternalServerError()(environ, start_response) # quick check if that dir exists... if not is_valid_repo(repo_name, self.basepath, 'git'): return HTTPNotFound()(environ, start_response) #====================================================================== # GET ACTION PULL or PUSH #====================================================================== action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if action in ['pull', 'push']: anonymous_user = User.get_default_user(cache=True) username = anonymous_user.username if anonymous_user.active: # ONLY check permissions if the user is activated anonymous_perm = self._check_permission(action, anonymous_user, repo_name, ip_addr) else: anonymous_perm = False if not anonymous_user.active or not anonymous_perm: if not anonymous_user.active: log.debug('Anonymous access is disabled, running ' 'authentication') if not anonymous_perm: log.debug('Not enough credentials to access this ' 'repository as anonymous user') username = None #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== # try to auth based on environ, container auth methods log.debug('Running PRE-AUTH for container based authentication') pre_auth = auth_modules.authenticate('', '', environ) if pre_auth is not None and pre_auth.get('username'): username = pre_auth['username'] log.debug('PRE-AUTH got %s as username', username) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = \ safe_str(self.config['realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME #============================================================== try: user = User.get_by_username_or_email(username) if user is None or not user.active: return HTTPForbidden()(environ, start_response) username = user.username except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self._check_permission(action, user, repo_name, ip_addr) if not perm: return HTTPForbidden()(environ, start_response) # extras are injected into UI object and later available # in hooks executed by kallithea from kallithea import CONFIG server_url = get_server_url(environ) extras = { 'ip': ip_addr, 'username': username, 'action': action, 'repository': repo_name, 'scm': 'git', 'config': CONFIG['__file__'], 'server_url': server_url, 'make_lock': None, 'locked_by': [None, None] } #=================================================================== # GIT REQUEST HANDLING #=================================================================== repo_path = os.path.join(safe_str(self.basepath),str_repo_name) log.debug('Repository path is %s', repo_path) # CHECK LOCKING only if it's not ANONYMOUS USER if username != User.DEFAULT_USER: log.debug('Checking locking on repository') (make_lock, locked, locked_by) = self._check_locking_state( environ=environ, action=action, repo=repo_name, user_id=user.user_id ) # store the make_lock for later evaluation in hooks extras.update({'make_lock': make_lock, 'locked_by': locked_by}) fix_PATH() log.debug('HOOKS extras is %s', extras) baseui = make_ui('db') self.__inject_extras(repo_path, baseui, extras) try: self._handle_githooks(repo_name, action, baseui, environ) log.info('%s action on Git repo "%s" by "%s" from %s', action, str_repo_name, safe_str(username), ip_addr) app = self.__make_app(repo_name, repo_path, extras) result = app(environ, start_response) if action == 'push': result = WSGIResultCloseCallback(result, lambda: self._invalidate_cache(repo_name)) return result except HTTPLockedRC as e: _code = CONFIG.get('lock_ret_code') log.debug('Repository LOCKED ret code %s!', _code) return e(environ, start_response) except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response)
def _handle_request(self, environ, start_response): if not is_mercurial(environ): return self.application(environ, start_response) if not self._check_ssl(environ, start_response): return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response) ip_addr = self._get_ip_addr(environ) username = None # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # EXTRACT REPOSITORY NAME FROM ENV #====================================================================== try: repo_name = environ['REPO_NAME'] = self.__get_repository(environ) log.debug('Extracted repo name is %s' % repo_name) except Exception: return HTTPInternalServerError()(environ, start_response) # quick check if that dir exists... if not is_valid_repo(repo_name, self.basepath, 'hg'): return HTTPNotFound()(environ, start_response) #====================================================================== # GET ACTION PULL or PUSH #====================================================================== action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if action in ['pull', 'push']: anonymous_user = self.__get_user('default') username = anonymous_user.username anonymous_perm = self._check_permission(action, anonymous_user, repo_name, ip_addr) if not anonymous_perm or not anonymous_user.active: if not anonymous_perm: log.debug('Not enough credentials to access this ' 'repository as anonymous user') if not anonymous_user.active: log.debug('Anonymous access is disabled, running ' 'authentication') #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== # Attempting to retrieve username from the container username = get_container_username(environ, self.config) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = \ safe_str(self.config['rhodecode_realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME #============================================================== try: user = self.__get_user(username) if user is None or not user.active: return HTTPForbidden()(environ, start_response) username = user.username except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self._check_permission(action, user, repo_name, ip_addr) if not perm: return HTTPForbidden()(environ, start_response) # extras are injected into mercurial UI object and later available # in hg hooks executed by rhodecode from rhodecode import CONFIG server_url = get_server_url(environ) extras = { 'ip': ip_addr, 'username': username, 'action': action, 'repository': repo_name, 'scm': 'hg', 'config': CONFIG['__file__'], 'server_url': server_url, 'make_lock': None, 'locked_by': [None, None] } #====================================================================== # MERCURIAL REQUEST HANDLING #====================================================================== str_repo_name = safe_str(repo_name) repo_path = os.path.join(safe_str(self.basepath), str_repo_name) log.debug('Repository path is %s' % repo_path) # CHECK LOCKING only if it's not ANONYMOUS USER if username != User.DEFAULT_USER: log.debug('Checking locking on repository') (make_lock, locked, locked_by) = self._check_locking_state(environ=environ, action=action, repo=repo_name, user_id=user.user_id) # store the make_lock for later evaluation in hooks extras.update({'make_lock': make_lock, 'locked_by': locked_by}) fix_PATH() log.debug('HOOKS extras is %s' % extras) baseui = make_ui('db') self.__inject_extras(repo_path, baseui, extras) try: log.info('%s action on HG repo "%s" by "%s" from %s' % (action, str_repo_name, safe_str(username), ip_addr)) app = self.__make_app(repo_path, baseui, extras) return app(environ, start_response) except RepoError, e: if str(e).find('not found') != -1: return HTTPNotFound()(environ, start_response)
def __call__( self, environ, start_response ): if 'PATH_INFO' in environ: path_info = environ[ 'PATH_INFO' ].lstrip( '/' ) if path_info == 'repository/reset_all_metadata': self.setting_repository_metadata = True cmd = self.__get_hg_command( **environ ) # The 'getbundle' command indicates that a mercurial client is getting a bundle of one or more changesets, indicating # a clone or a pull. However, we do not want to increment the times_downloaded count if we're only setting repository # metadata. if cmd == 'getbundle' and not self.setting_repository_metadata: hg_args = urlparse.parse_qs( environ[ 'HTTP_X_HGARG_1' ] ) # The 'common' parameter indicates the full sha-1 hash of the changeset the client currently has checked out. If # this is 0000000000000000000000000000000000000000, then the client is performing a fresh checkout. If it has any # other value, the client is getting updates to an existing checkout. if 'common' in hg_args and hg_args[ 'common' ][-1] == '0000000000000000000000000000000000000000': # Increment the value of the times_downloaded column in the repository table for the cloned repository. if 'PATH_INFO' in environ: # Instantiate a database connection engine = sqlalchemy.create_engine( self.db_url ) connection = engine.connect() path_info = environ[ 'PATH_INFO' ].lstrip( '/' ) user_id, repository_name = self.__get_user_id_repository_name_from_path_info( connection, path_info ) sql_cmd = "SELECT times_downloaded FROM repository WHERE user_id = %d AND name = '%s'" % \ ( user_id, repository_name.lower() ) result_set = connection.execute( sql_cmd ) for row in result_set: # Should only be 1 row... times_downloaded = row[ 'times_downloaded' ] times_downloaded += 1 sql_cmd = "UPDATE repository SET times_downloaded = %d WHERE user_id = %d AND name = '%s'" % \ ( times_downloaded, user_id, repository_name.lower() ) connection.execute( sql_cmd ) connection.close() elif cmd in [ 'unbundle', 'pushkey' ]: # This is an hg push from the command line. When doing this, the following commands, in order, # will be retrieved from environ (see the docs at http://mercurial.selenic.com/wiki/WireProtocol): # # If mercurial version >= '2.2.3': capabilities -> batch -> branchmap -> unbundle -> listkeys -> pushkey -> listkeys # # The mercurial API unbundle() ( i.e., hg push ) and pushkey() methods ultimately require authorization. # We'll force password entry every time a change set is pushed. # # When a user executes hg commit, it is not guaranteed to succeed. Mercurial records your name # and address with each change that you commit, so that you and others will later be able to # tell who made each change. Mercurial tries to automatically figure out a sensible username # to commit the change with. It will attempt each of the following methods, in order: # # 1) If you specify a -u option to the hg commit command on the command line, followed by a username, # this is always given the highest precedence. # 2) If you have set the HGUSER environment variable, this is checked next. # 3) If you create a file in your home directory called .hgrc with a username entry, that # will be used next. # 4) If you have set the EMAIL environment variable, this will be used next. # 5) Mercurial will query your system to find out your local user name and host name, and construct # a username from these components. Since this often results in a username that is not very useful, # it will print a warning if it has to do this. # # If all of these mechanisms fail, Mercurial will fail, printing an error message. In this case, it # will not let you commit until you set up a username. result = self.authentication( environ ) if not isinstance( result, str ) and cmd == 'unbundle' and 'wsgi.input' in environ: bundle_data_stream = environ[ 'wsgi.input' ] # Convert the incoming mercurial bundle into a json object and persit it to a temporary file for inspection. fh = tempfile.NamedTemporaryFile( 'wb', prefix="tmp-hg-bundle" ) tmp_filename = fh.name fh.close() fh = open( tmp_filename, 'wb' ) while 1: chunk = bundle_data_stream.read( CHUNK_SIZE ) if not chunk: break fh.write( chunk ) fh.close() fh = open( tmp_filename, 'rb' ) changeset_groups = json.loads( hg_util.bundle_to_json( fh ) ) fh.close() try: os.unlink( tmp_filename ) except: pass if changeset_groups: # Check the repository type to make sure inappropriate files are not being pushed. if 'PATH_INFO' in environ: # Instantiate a database connection engine = sqlalchemy.create_engine( self.db_url ) connection = engine.connect() path_info = environ[ 'PATH_INFO' ].lstrip( '/' ) user_id, repository_name = self.__get_user_id_repository_name_from_path_info( connection, path_info ) sql_cmd = "SELECT type FROM repository WHERE user_id = %d AND name = '%s'" % ( user_id, repository_name.lower() ) result_set = connection.execute( sql_cmd ) for row in result_set: # Should only be 1 row... repository_type = str( row[ 'type' ] ) if repository_type == rt_util.REPOSITORY_SUITE_DEFINITION: # Handle repositories of type repository_suite_definition, which can only contain a single # file named repository_dependencies.xml. for entry in changeset_groups: if len( entry ) == 2: # We possibly found an altered file entry. filename, change_list = entry if filename and isinstance( filename, str ): if filename == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME: # Make sure the any complex repository dependency definitions contain valid <repository> tags. is_valid, error_msg = self.repository_tags_are_valid( filename, change_list ) if not is_valid: log.debug( error_msg ) return self.__display_exception_remotely( start_response, error_msg ) else: msg = "Only a single file named repository_dependencies.xml can be pushed to a repository " msg += "of type 'Repository suite definition'." log.debug( msg ) return self.__display_exception_remotely( start_response, msg ) elif repository_type == rt_util.TOOL_DEPENDENCY_DEFINITION: # Handle repositories of type tool_dependency_definition, which can only contain a single # file named tool_dependencies.xml. for entry in changeset_groups: if len( entry ) == 2: # We possibly found an altered file entry. filename, change_list = entry if filename and isinstance( filename, str ): if filename == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME: # Make sure the any complex repository dependency definitions contain valid <repository> tags. is_valid, error_msg = self.repository_tags_are_valid( filename, change_list ) if not is_valid: log.debug( error_msg ) return self.__display_exception_remotely( start_response, error_msg ) else: msg = "Only a single file named tool_dependencies.xml can be pushed to a repository " msg += "of type 'Tool dependency definition'." log.debug( msg ) return self.__display_exception_remotely( start_response, msg ) else: # If the changeset includes changes to dependency definition files, make sure tag sets # are not missing "toolshed" or "changeset_revision" attributes since automatically populating # them is not supported when pushing from the command line. These attributes are automatically # populated only when using the tool shed upload utility. for entry in changeset_groups: if len( entry ) == 2: # We possibly found an altered file entry. filename, change_list = entry if filename and isinstance( filename, str ): if filename in [ rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME, rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME ]: # We check both files since tool dependency definitions files can contain complex # repository dependency definitions. is_valid, error_msg = self.repository_tags_are_valid( filename, change_list ) if not is_valid: log.debug( error_msg ) return self.__display_exception_remotely( start_response, error_msg ) if isinstance( result, str ): # Authentication was successful AUTH_TYPE.update( environ, 'basic' ) REMOTE_USER.update( environ, result ) else: return result.wsgi_application( environ, start_response ) return self.app( environ, start_response )
def __call__( self, environ, start_response ): cmd = self.__get_hg_command( **environ ) if cmd == 'changegroup': # This is an hg clone from the command line. When doing this, the following 5 commands, in order, # will be retrieved from environ: # between -> heads -> changegroup -> capabilities -> listkeys # # Increment the value of the times_downloaded column in the repository table for the cloned repository. if 'PATH_INFO' in environ: path_info = environ[ 'PATH_INFO' ].lstrip( '/' ) # An example of path_info is: '/repos/test/column1' path_info_components = path_info.split( '/' ) username = path_info_components[1] name = path_info_components[2] # Instantiate a database connection engine = sqlalchemy.create_engine( self.db_url ) connection = engine.connect() result_set = connection.execute( "select id from galaxy_user where username = '******'" % username.lower() ) for row in result_set: # Should only be 1 row... user_id = row[ 'id' ] result_set = connection.execute( "select times_downloaded from repository where user_id = %d and name = '%s'" % ( user_id, name.lower() ) ) for row in result_set: # Should only be 1 row... times_downloaded = row[ 'times_downloaded' ] times_downloaded += 1 connection.execute( "update repository set times_downloaded = %d where user_id = %d and name = '%s'" % ( times_downloaded, user_id, name.lower() ) ) connection.close() if cmd == 'unbundle': # This is an hg push from the command line. When doing this, the following commands, in order, # will be retrieved from environ (see the docs at http://mercurial.selenic.com/wiki/WireProtocol): # # If mercurial version >= '2.2.3': capabilities -> batch -> branchmap -> unbundle -> listkeys -> pushkey # # The mercurial API unbundle() ( i.e., hg push ) method ultimately requires authorization. # We'll force password entry every time a change set is pushed. # # When a user executes hg commit, it is not guaranteed to succeed. Mercurial records your name # and address with each change that you commit, so that you and others will later be able to # tell who made each change. Mercurial tries to automatically figure out a sensible username # to commit the change with. It will attempt each of the following methods, in order: # # 1) If you specify a -u option to the hg commit command on the command line, followed by a username, # this is always given the highest precedence. # 2) If you have set the HGUSER environment variable, this is checked next. # 3) If you create a file in your home directory called .hgrc with a username entry, that # will be used next. # 4) If you have set the EMAIL environment variable, this will be used next. # 5) Mercurial will query your system to find out your local user name and host name, and construct # a username from these components. Since this often results in a username that is not very useful, # it will print a warning if it has to do this. # # If all of these mechanisms fail, Mercurial will fail, printing an error message. In this case, it # will not let you commit until you set up a username. result = self.authentication( environ ) if isinstance( result, str ): # Authentication was successful AUTH_TYPE.update( environ, 'basic' ) REMOTE_USER.update( environ, result ) else: return result.wsgi_application( environ, start_response ) return self.app( environ, start_response )
def _handle_request(self, environ, start_response): if not is_mercurial(environ): return self.application(environ, start_response) if not self._check_ssl(environ, start_response): return HTTPNotAcceptable("SSL REQUIRED !")(environ, start_response) ip_addr = self._get_ip_addr(environ) username = None # skip passing error to error controller environ["pylons.status_code_redirect"] = True # ====================================================================== # EXTRACT REPOSITORY NAME FROM ENV # ====================================================================== try: repo_name = environ["REPO_NAME"] = self.__get_repository(environ) log.debug("Extracted repo name is %s" % repo_name) except Exception: return HTTPInternalServerError()(environ, start_response) # quick check if that dir exists... if not is_valid_repo(repo_name, self.basepath, "hg"): return HTTPNotFound()(environ, start_response) # ====================================================================== # GET ACTION PULL or PUSH # ====================================================================== action = self.__get_action(environ) # ====================================================================== # CHECK ANONYMOUS PERMISSION # ====================================================================== if action in ["pull", "push"]: anonymous_user = self.__get_user("default") username = anonymous_user.username anonymous_perm = self._check_permission(action, anonymous_user, repo_name, ip_addr) if not anonymous_perm or not anonymous_user.active: if not anonymous_perm: log.debug("Not enough credentials to access this " "repository as anonymous user") if not anonymous_user.active: log.debug("Anonymous access is disabled, running " "authentication") # ============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS # ============================================================== # Attempting to retrieve username from the container username = get_container_username(environ, self.config) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = safe_str(self.config["rhodecode_realm"]) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, "basic") REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) # ============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME # ============================================================== try: user = self.__get_user(username) if user is None or not user.active: return HTTPForbidden()(environ, start_response) username = user.username except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) # check permissions for this repository perm = self._check_permission(action, user, repo_name, ip_addr) if not perm: return HTTPForbidden()(environ, start_response) # extras are injected into mercurial UI object and later available # in hg hooks executed by rhodecode from rhodecode import CONFIG server_url = get_server_url(environ) extras = { "ip": ip_addr, "username": username, "action": action, "repository": repo_name, "scm": "hg", "config": CONFIG["__file__"], "server_url": server_url, "make_lock": None, "locked_by": [None, None], } # ====================================================================== # MERCURIAL REQUEST HANDLING # ====================================================================== str_repo_name = safe_str(repo_name) repo_path = os.path.join(safe_str(self.basepath), str_repo_name) log.debug("Repository path is %s" % repo_path) # CHECK LOCKING only if it's not ANONYMOUS USER if username != User.DEFAULT_USER: log.debug("Checking locking on repository") (make_lock, locked, locked_by) = self._check_locking_state( environ=environ, action=action, repo=repo_name, user_id=user.user_id ) # store the make_lock for later evaluation in hooks extras.update({"make_lock": make_lock, "locked_by": locked_by}) # set the environ variables for this request os.environ["RC_SCM_DATA"] = json.dumps(extras) fix_PATH() log.debug("HOOKS extras is %s" % extras) baseui = make_ui("db") self.__inject_extras(repo_path, baseui, extras) try: log.info('%s action on HG repo "%s" by "%s" from %s' % (action, str_repo_name, safe_str(username), ip_addr)) app = self.__make_app(repo_path, baseui, extras) return app(environ, start_response) except RepoError, e: if str(e).find("not found") != -1: return HTTPNotFound()(environ, start_response)
def _handle_request(self, environ, start_response): if not self._check_ssl(environ, start_response): reason = ('SSL required, while RhodeCode was unable ' 'to detect this as SSL request') log.debug('User not allowed to proceed, %s', reason) return HTTPNotAcceptable(reason)(environ, start_response) ip_addr = get_ip_addr(environ) username = None # skip passing error to error controller environ['pylons.status_code_redirect'] = True # ====================================================================== # EXTRACT REPOSITORY NAME FROM ENV # ====================================================================== environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO']) repo_name = self._get_repository_name(environ) environ['REPO_NAME'] = repo_name log.debug('Extracted repo name is %s', repo_name) # check for type, presence in database and on filesystem if not self.is_valid_and_existing_repo(repo_name, self.basepath, self.SCM): return HTTPNotFound()(environ, start_response) # ====================================================================== # GET ACTION PULL or PUSH # ====================================================================== action = self._get_action(environ) # ====================================================================== # CHECK ANONYMOUS PERMISSION # ====================================================================== if action in ['pull', 'push']: anonymous_user = User.get_default_user() username = anonymous_user.username if anonymous_user.active: # ONLY check permissions if the user is activated anonymous_perm = self._check_permission( action, anonymous_user, repo_name, ip_addr) else: anonymous_perm = False if not anonymous_user.active or not anonymous_perm: if not anonymous_user.active: log.debug('Anonymous access is disabled, running ' 'authentication') if not anonymous_perm: log.debug('Not enough credentials to access this ' 'repository as anonymous user') username = None # ============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS # ============================================================== # try to auth based on environ, container auth methods log.debug( 'Running PRE-AUTH for container based authentication') pre_auth = authenticate('', '', environ, VCS_TYPE) if pre_auth and pre_auth.get('username'): username = pre_auth['username'] log.debug('PRE-AUTH got %s as username', username) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = \ safe_str(self.config['rhodecode_realm']) try: result = self.authenticate(environ) except (UserCreationError, NotAllowedToCreateUserError) as e: log.error(e) reason = safe_str(e) return HTTPNotAcceptable(reason)(environ, start_response) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) # ============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME # ============================================================== user = User.get_by_username(username) if not self.valid_and_active_user(user): return HTTPForbidden()(environ, start_response) username = user.username user.update_lastactivity() meta.Session().commit() # check user attributes for password change flag user_obj = user if user_obj and user_obj.username != User.DEFAULT_USER and \ user_obj.user_data.get('force_password_change'): reason = 'password change required' log.debug('User not allowed to authenticate, %s', reason) return HTTPNotAcceptable(reason)(environ, start_response) # check permissions for this repository perm = self._check_permission(action, user, repo_name, ip_addr) if not perm: return HTTPForbidden()(environ, start_response) # extras are injected into UI object and later available # in hooks executed by rhodecode check_locking = _should_check_locking(environ.get('QUERY_STRING')) extras = vcs_operation_context(environ, repo_name=repo_name, username=username, action=action, scm=self.SCM, check_locking=check_locking) # ====================================================================== # REQUEST HANDLING # ====================================================================== str_repo_name = safe_str(repo_name) repo_path = os.path.join(safe_str(self.basepath), str_repo_name) log.debug('Repository path is %s', repo_path) fix_PATH() log.info('%s action on %s repo "%s" by "%s" from %s', action, self.SCM, str_repo_name, safe_str(username), ip_addr) return self._generate_vcs_response(environ, start_response, repo_path, repo_name, extras, action)
def __call__(self, environ, start_response): if not is_mercurial(environ): return self.application(environ, start_response) proxy_key = "HTTP_X_REAL_IP" def_key = "REMOTE_ADDR" ipaddr = environ.get(proxy_key, environ.get(def_key, "0.0.0.0")) # skip passing error to error controller environ["pylons.status_code_redirect"] = True # ====================================================================== # EXTRACT REPOSITORY NAME FROM ENV # ====================================================================== try: repo_name = environ["REPO_NAME"] = self.__get_repository(environ) log.debug("Extracted repo name is %s" % repo_name) except: return HTTPInternalServerError()(environ, start_response) # ====================================================================== # GET ACTION PULL or PUSH # ====================================================================== action = self.__get_action(environ) # ====================================================================== # CHECK ANONYMOUS PERMISSION # ====================================================================== if action in ["pull", "push"]: anonymous_user = self.__get_user("default") username = anonymous_user.username anonymous_perm = self.__check_permission(action, anonymous_user, repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: log.debug("Not enough credentials to access this " "repository as anonymous user") if anonymous_user.active is False: log.debug("Anonymous access is disabled, running " "authentication") # ============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS # ============================================================== if not REMOTE_USER(environ): self.authenticate.realm = safe_str(self.config["rhodecode_realm"]) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, "basic") REMOTE_USER.update(environ, result) else: return result.wsgi_application(environ, start_response) # ============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM # BASIC AUTH # ============================================================== if action in ["pull", "push"]: username = REMOTE_USER(environ) try: user = self.__get_user(username) username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) # check permissions for this repository perm = self.__check_permission(action, user, repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) extras = {"ip": ipaddr, "username": username, "action": action, "repository": repo_name} # ====================================================================== # MERCURIAL REQUEST HANDLING # ====================================================================== repo_path = safe_str(os.path.join(self.basepath, repo_name)) log.debug("Repository path is %s" % repo_path) baseui = make_ui("db") self.__inject_extras(repo_path, baseui, extras) # quick check if that dir exists... if is_valid_repo(repo_name, self.basepath) is False: return HTTPNotFound()(environ, start_response) try: # invalidate cache on push if action == "push": self.__invalidate_cache(repo_name) app = self.__make_app(repo_path, baseui, extras) return app(environ, start_response) except RepoError, e: if str(e).find("not found") != -1: return HTTPNotFound()(environ, start_response)
def __call__(self, environ, start_response): cmd = self.__get_hg_command(**environ) if cmd == 'changegroup': # This is an hg clone from the command line. When doing this, the following 5 commands, in order, # will be retrieved from environ: # between -> heads -> changegroup -> capabilities -> listkeys # # Increment the value of the times_downloaded column in the repository table for the cloned repository. if 'PATH_INFO' in environ: path_info = environ['PATH_INFO'].lstrip('/') # An example of path_info is: '/repos/test/column1' path_info_components = path_info.split('/') username = path_info_components[1] name = path_info_components[2] # Instantiate a database connection engine = sqlalchemy.create_engine(self.db_url) connection = engine.connect() result_set = connection.execute( "select id from galaxy_user where username = '******'" % username.lower()) for row in result_set: # Should only be 1 row... user_id = row['id'] result_set = connection.execute( "select times_downloaded from repository where user_id = %d and name = '%s'" % (user_id, name.lower())) for row in result_set: # Should only be 1 row... times_downloaded = row['times_downloaded'] times_downloaded += 1 connection.execute( "update repository set times_downloaded = %d where user_id = %d and name = '%s'" % (times_downloaded, user_id, name.lower())) connection.close() if cmd == 'unbundle': # This is an hg push from the command line. When doing this, the following commands, in order, # will be retrieved from environ (see the docs at http://mercurial.selenic.com/wiki/WireProtocol): # # If mercurial version >= '2.2.3': capabilities -> batch -> branchmap -> unbundle -> listkeys -> pushkey # # The mercurial API unbundle() ( i.e., hg push ) method ultimately requires authorization. # We'll force password entry every time a change set is pushed. # # When a user executes hg commit, it is not guaranteed to succeed. Mercurial records your name # and address with each change that you commit, so that you and others will later be able to # tell who made each change. Mercurial tries to automatically figure out a sensible username # to commit the change with. It will attempt each of the following methods, in order: # # 1) If you specify a -u option to the hg commit command on the command line, followed by a username, # this is always given the highest precedence. # 2) If you have set the HGUSER environment variable, this is checked next. # 3) If you create a file in your home directory called .hgrc with a username entry, that # will be used next. # 4) If you have set the EMAIL environment variable, this will be used next. # 5) Mercurial will query your system to find out your local user name and host name, and construct # a username from these components. Since this often results in a username that is not very useful, # it will print a warning if it has to do this. # # If all of these mechanisms fail, Mercurial will fail, printing an error message. In this case, it # will not let you commit until you set up a username. result = self.authentication(environ) if isinstance(result, str): # Authentication was successful AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) else: return result.wsgi_application(environ, start_response) return self.app(environ, start_response)
def __call__(self, environ, start_response): if not is_mercurial(environ): return self.application(environ, start_response) proxy_key = 'HTTP_X_REAL_IP' def_key = 'REMOTE_ADDR' self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # GET ACTION PULL or PUSH #====================================================================== self.action = self.__get_action(environ) try: #================================================================== # GET REPOSITORY NAME #================================================================== self.repo_name = self.__get_repository(environ) except: return HTTPInternalServerError()(environ, start_response) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if self.action in ['pull', 'push']: anonymous_user = self.__get_user('default') self.username = anonymous_user.username anonymous_perm = self.__check_permission(self.action, anonymous_user, self.repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: log.debug('Not enough credentials to access this ' 'repository as anonymous user') if anonymous_user.active is False: log.debug('Anonymous access is disabled, running ' 'authentication') #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== if not REMOTE_USER(environ): self.authenticate.realm = \ safe_str(self.config['rhodecode_realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM # BASIC AUTH #============================================================== if self.action in ['pull', 'push']: username = REMOTE_USER(environ) try: user = self.__get_user(username) self.username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self.__check_permission(self.action, user, self.repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) self.extras = {'ip': self.ipaddr, 'username': self.username, 'action': self.action, 'repository': self.repo_name} #====================================================================== # MERCURIAL REQUEST HANDLING #====================================================================== environ['PATH_INFO'] = '/' # since we wrap into hgweb, reset the path self.baseui = make_ui('db') self.basepath = self.config['base_path'] self.repo_path = os.path.join(self.basepath, self.repo_name) #quick check if that dir exists... if check_repo_fast(self.repo_name, self.basepath): return HTTPNotFound()(environ, start_response) try: app = wsgiapplication(self.__make_app) except RepoError, e: if str(e).find('not found') != -1: return HTTPNotFound()(environ, start_response)