Changeset - 7f5976da192c
[Not reviewed]
beta
0 3 0
Marcin Kuzminski - 15 years ago 2010-11-05 18:26:26
marcin@python-works.com
#48 rewrite action loggers into hooks with all changesets that are inside a push
3 files changed with 90 insertions and 54 deletions:
0 comments (0 inline, 0 general)
rhodecode/lib/hooks.py
Show inline comments
 
@@ -19,18 +19,18 @@
 
# MA  02110-1301, USA.
 
"""
 
Created on Aug 6, 2010
 

	
 
@author: marcink
 
"""
 

	
 
import sys
 
from mercurial.cmdutil import revrange
 
from mercurial.node import nullrev
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.utils import action_logger
 
import os
 
from rhodecode.lib import helpers as h
 
from rhodecode.model import meta
 
from rhodecode.model.db import UserLog, User
 
import sys
 

	
 
def repo_size(ui, repo, hooktype=None, **kwargs):
 

	
 
    if hooktype != 'changegroup':
 
        return False
 
    size_hg, size_root = 0, 0
 
@@ -45,34 +45,55 @@ def repo_size(ui, repo, hooktype=None, *
 
    size_hg_f = h.format_byte_size(size_hg)
 
    size_root_f = h.format_byte_size(size_root)
 
    size_total_f = h.format_byte_size(size_root + size_hg)
 
    sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
 
                     % (size_hg_f, size_root_f, size_total_f))
 
    
 
    user_action_mapper(ui, repo, hooktype, **kwargs)
 
def log_pull_action(ui, repo, **kwargs):
 
    """
 
    Logs user last pull action
 
    :param ui:
 
    :param repo:
 
    """
 
        
 
    extra_params = dict(repo.ui.configitems('rhodecode_extras'))
 
    username = extra_params['username']
 
    repository = extra_params['repository']
 
    action = 'pull'
 
       
 
    action_logger(username, action, repository, extra_params['ip'])
 
    
 
    return 0
 

	
 
def user_action_mapper(ui, repo, hooktype=None, **kwargs):
 
def log_push_action(ui, repo, **kwargs):
 
    """
 
    Maps user last push action to new changeset id, from mercurial
 
    :param ui:
 
    :param repo:
 
    :param hooktype:
 
    """
 
    
 
    try:
 
        sa = meta.Session()
 
        username = kwargs['url'].split(':')[-1]
 
        user_log = sa.query(UserLog)\
 
            .filter(UserLog.user == sa.query(User)\
 
                                    .filter(User.username == username).one())\
 
            .order_by(UserLog.user_log_id.desc()).first()
 
    extra_params = dict(repo.ui.configitems('rhodecode_extras'))
 
    username = extra_params['username']
 
    repository = extra_params['repository']
 
    action = 'push:%s'
 
    node = kwargs['node']
 
    
 
    def get_revs(repo, rev_opt):
 
        if rev_opt:
 
            revs = revrange(repo, rev_opt)
 
            
 
            if len(revs) == 0:
 
                return (nullrev, nullrev)
 
            return (max(revs), min(revs))
 
        else:
 
            return (len(repo) - 1, 0)
 
    
 
    stop, start = get_revs(repo, [node + ':'])
 
    
 
    revs = (str(repo[r]) for r in xrange(start, stop + 1))
 
    
 
    action = action % ','.join(revs)
 
        
 
        if user_log and not user_log.revision:
 
            user_log.revision = str(repo['tip'])
 
            sa.add(user_log)
 
            sa.commit()
 
        
 
    except Exception, e:
 
        sa.rollback()
 
        raise
 
    finally:
 
        meta.Session.remove()    
 
    action_logger(username, action, repository, extra_params['ip'])
 
    
 
    return 0
 
  
rhodecode/lib/middleware/simplehg.py
Show inline comments
 
@@ -46,72 +46,84 @@ class SimpleHg(object):
 

	
 
    def __init__(self, application, config):
 
        self.application = application
 
        self.config = config
 
        #authenticate this mercurial request using 
 
        self.authenticate = AuthBasicAuthenticator('', authfunc)
 

	
 
        self.ipaddr = '0.0.0.0'
 
        self.repository = None
 
        self.username = None
 
        self.action = None
 
        
 
    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'))
 
        
 
        #===================================================================
 
        # AUTHENTICATE THIS MERCURIAL REQUEST
 
        #===================================================================
 
        username = REMOTE_USER(environ)
 
        
 
        if not username:
 
            self.authenticate.realm = 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)
 

	
 
            
 
        #=======================================================================
 
        # GET REPOSITORY
 
        #=======================================================================
 
        try:
 
            repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
 
            if repo_name.endswith('/'):
 
                repo_name = repo_name.rstrip('/')
 
            self.repository = repo_name
 
        except:
 
            log.error(traceback.format_exc())
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
        #===================================================================
 
        # CHECK PERMISSIONS FOR THIS REQUEST
 
        #===================================================================
 
        action = self.__get_action(environ)
 
        if action:
 
        self.action = self.__get_action(environ)
 
        if self.action:
 
            username = self.__get_environ_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
 
            if action == 'push':
 
            if self.action == 'push':
 
                if not HasPermissionAnyMiddleware('repository.write',
 
                                                  'repository.admin')\
 
                                                    (user, repo_name):
 
                    return HTTPForbidden()(environ, start_response)
 

	
 
            else:
 
                #any other action need at least read permission
 
                if not HasPermissionAnyMiddleware('repository.read',
 
                                                  'repository.write',
 
                                                  'repository.admin')\
 
                                                    (user, repo_name):
 
                    return HTTPForbidden()(environ, start_response)
 

	
 
            #log action
 
            if action in ('push', 'pull', 'clone'):
 
                proxy_key = 'HTTP_X_REAL_IP'
 
                def_key = 'REMOTE_ADDR'
 
                ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
 
                self.__log_user_action(user, action, repo_name, ipaddr)
 

	
 
                
 
        self.extras = {'ip':self.ipaddr,
 
                       'username':self.username,
 
                       'action':self.action,
 
                       'repository':self.repository}
 
        print self.extras
 
        #===================================================================
 
        # 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']
 
@@ -127,13 +139,13 @@ class SimpleHg(object):
 
                return HTTPNotFound()(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
        #invalidate cache on push
 
        if action == 'push':
 
        if self.action == 'push':
 
            self.__invalidate_cache(repo_name)
 
            messages = []
 
            messages.append('thank you for using rhodecode')
 

	
 
            return self.msg_wrapper(app, environ, start_response, messages)
 
        else:
 
@@ -154,13 +166,13 @@ class SimpleHg(object):
 
                yield msg + '\n'
 
        org_response = app(environ, start_response)
 
        return chain(org_response, custom_messages(messages))
 

	
 
    def __make_app(self):
 
        hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
 
        return  self.__load_web_settings(hgserve)
 
        return  self.__load_web_settings(hgserve, self.extras)
 

	
 
    def __get_environ_user(self, environ):
 
        return environ.get('REMOTE_USER')
 

	
 
    def __get_user(self, username):
 
        return UserModel().get_by_username(username, cache=True)
 
@@ -171,48 +183,50 @@ class SimpleHg(object):
 
        This should always return a valid command string
 
        :param environ:
 
        """
 
        mapping = {'changegroup': 'pull',
 
                   'changegroupsubset': 'pull',
 
                   'stream_out': 'pull',
 
                   #'listkeys': 'pull',
 
                   'listkeys': 'pull',
 
                   'unbundle': 'push',
 
                   'pushkey': 'push', }
 
        for qry in environ['QUERY_STRING'].split('&'):
 
            if qry.startswith('cmd'):
 
                cmd = qry.split('=')[-1]
 
                if mapping.has_key(cmd):
 
                    return mapping[cmd]
 
                else:
 
                    return cmd
 

	
 
    def __log_user_action(self, user, action, repo, ipaddr):
 
        action_logger(user, action, repo, ipaddr)
 

	
 
    def __invalidate_cache(self, repo_name):
 
        """we know that some change was made to repositories and we should
 
        invalidate the cache to see the changes right away but only for
 
        push requests"""
 
        invalidate_cache('cached_repo_list')
 
        invalidate_cache('full_changelog', repo_name)
 

	
 

	
 
    def __load_web_settings(self, hgserve):
 
    def __load_web_settings(self, hgserve, extras={}):
 
        #set the global ui for hgserve instance passed
 
        hgserve.repo.ui = self.baseui
 

	
 
        hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
 
    
 
        #inject some additional parameters that will be available in ui
 
        #for hooks
 
        for k, v in extras.items():
 
            hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
 
        
 
        repoui = make_ui('file', hgrc, False)
 

	
 

	
 
        if repoui:
 
            #overwrite our ui instance with the section from hgrc file
 
            for section in ui_sections:
 
                for k, v in repoui.configitems(section):
 
                    hgserve.repo.ui.setconfig(section, k, v)
 

	
 
            
 
        return hgserve
 

	
 

	
 

	
 

	
 

	
rhodecode/lib/utils.py
Show inline comments
 
@@ -32,12 +32,13 @@ from rhodecode.model.db import Repositor
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.user import UserModel
 
from vcs.backends.base import BaseChangeset
 
from vcs.backends.git import GitRepository
 
from vcs.backends.hg import MercurialRepository
 
from vcs.utils.lazy import LazyProperty
 
import traceback
 
import datetime
 
import logging
 
import os
 

	
 
log = logging.getLogger(__name__)
 

	
 
@@ -74,34 +75,34 @@ def action_logger(user, action, repo, ip
 

	
 
    if not sa:
 
        sa = meta.Session()
 

	
 
    try:
 
        if hasattr(user, 'user_id'):
 
            user_id = user.user_id
 
            user_obj = user
 
        elif isinstance(user, basestring):
 
            user_id = UserModel(sa).get_by_username(user, cache=False).user_id
 
            user_obj = UserModel(sa).get_by_username(user, cache=False)
 
        else:
 
            raise Exception('You have to provide user object or username')
 

	
 
        repo_name = repo.lstrip('/')
 
        user_log = UserLog()
 
        user_log.user_id = user_id
 
        user_log.user_id = user_obj.user_id
 
        user_log.action = action
 
        user_log.repository_name = repo_name
 
        user_log.repository = RepoModel(sa).get(repo_name, cache=False)
 
        user_log.action_date = datetime.datetime.now()
 
        user_log.user_ip = ipaddr
 
        sa.add(user_log)
 
        sa.commit()
 

	
 
        log.info('Adding user %s, action %s on %s',
 
                                        user.username, action, repo)
 
    except Exception, e:
 
                                        user_obj.username, action, repo)
 
    except:
 
        log.error(traceback.format_exc())
 
        sa.rollback()
 
        log.error('could not log user action:%s', str(e))
 

	
 
def get_repos(path, recursive=False, initial=False):
 
    """
 
    Scans given path for repos and return (name,(type,path)) tuple 
 
    :param prefix:
 
    :param path:
0 comments (0 inline, 0 general)