@@ -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.model import meta
from rhodecode.model.db import UserLog, User
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 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()
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()
@@ -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)
return result.wsgi_application(environ, start_response)
#=======================================================================
# GET REPOSITORY
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)
user = self.__get_user(username)
self.username = user.username
#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)
#any other action need at least read permission
if not HasPermissionAnyMiddleware('repository.read',
'repository.write',
#log action
if action in ('push', 'pull', 'clone'):
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:
#invalidate cache on push
self.__invalidate_cache(repo_name)
messages = []
messages.append('thank you for using rhodecode')
return self.msg_wrapper(app, environ, start_response, messages)
@@ -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]
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
@@ -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
log = logging.getLogger(__name__)
@@ -74,34 +75,34 @@ def action_logger(user, action, repo, ip
if not sa:
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)
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
log.info('Adding user %s, action %s on %s',
user.username, action, repo)
user_obj.username, action, repo)
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:
Status change: