Beispiel #1
0
 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)
Beispiel #2
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
 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)
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
    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)
Beispiel #8
0
 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 )
Beispiel #9
0
 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 )
Beispiel #10
0
    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)
Beispiel #12
0
    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)
Beispiel #13
0
 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)
Beispiel #14
0
    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)
Beispiel #15
0
    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)