modified file chmod 100755 => 100644
@@ -32,48 +32,50 @@ pdebug = false
threadpool_workers = 5
##max request before thread respawn
threadpool_max_requests = 10
##option to use threads of process
use_threadpool = true
use = egg:Paste#http
host = 127.0.0.1
port = 5000
[app:main]
use = egg:rhodecode
full_stack = true
static_files = true
lang=en
cache_dir = %(here)s/data
index_dir = %(here)s/data/index
app_instance_uuid = ${app_instance_uuid}
cut_off_limit = 256000
force_https = false
commit_parse_limit = 50
use_gravatar = true
container_auth_enabled = false
proxypass_auth_enabled = false
####################################
### CELERY CONFIG ####
use_celery = false
broker.host = localhost
broker.vhost = rabbitmqhost
broker.port = 5672
broker.user = rabbitmq
broker.password = qweqwe
celery.imports = rhodecode.lib.celerylib.tasks
celery.result.backend = amqp
celery.result.dburi = amqp://
celery.result.serialier = json
#celery.send.task.error.emails = true
#celery.amqp.task.result.expires = 18000
celeryd.concurrency = 2
#celeryd.log.file = celeryd.log
celeryd.log.level = debug
celeryd.max.tasks.per.child = 1
@@ -202,94 +202,145 @@ def authenticate(username, password):
password)
log.debug('Got ldap DN response %s', user_dn)
get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
.get(k), [''])[0]
user_attrs = {
'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
'email': get_ldap_attr('ldap_attr_email'),
}
if user_model.create_ldap(username, password, user_dn,
user_attrs):
log.info('created new ldap user %s', username)
return True
except (LdapUsernameError, LdapPasswordError,):
pass
except (Exception,):
log.error(traceback.format_exc())
return False
def login_container_auth(username):
user = User.get_by_username(username)
if user is None:
user_model = UserModel()
'name': username,
'lastname': None,
'email': None,
if not user_model.create_for_container_auth(username, user_attrs):
return None
log.info('User %s was created by container authentication', username)
if not user.active:
user.update_lastlogin()
log.debug('User %s is now logged in by container authentication', user.username)
return user
def get_container_username(environ, cfg=config):
from paste.httpheaders import REMOTE_USER
from paste.deploy.converters import asbool
username = REMOTE_USER(environ)
if not username and asbool(cfg.get('proxypass_auth_enabled', False)):
username = environ.get('HTTP_X_FORWARDED_USER')
if username:
#Removing realm and domain from username
username = username.partition('@')[0]
username = username.rpartition('\\')[2]
log.debug('Received username %s from container', username)
return username
class AuthUser(object):
"""
A simple object that handles all attributes of user in RhodeCode
It does lookup based on API key,given user, or user present in session
Then it fills all required information for such user. It also checks if
anonymous access is enabled and if so, it returns default user as logged
in
def __init__(self, user_id=None, api_key=None):
def __init__(self, user_id=None, api_key=None, username=None):
self.user_id = user_id
self.api_key = None
self.username = 'None'
self.username = username
self.name = ''
self.lastname = ''
self.email = ''
self.is_authenticated = False
self.admin = False
self.permissions = {}
self._api_key = api_key
self.propagate_data()
def propagate_data(self):
self.anonymous_user = User.get_by_username('default')
is_user_loaded = False
if self._api_key and self._api_key != self.anonymous_user.api_key:
#try go get user by api key
log.debug('Auth User lookup by API KEY %s', self._api_key)
user_model.fill_data(self, api_key=self._api_key)
else:
is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
elif self.user_id is not None \
and self.user_id != self.anonymous_user.user_id:
log.debug('Auth User lookup by USER ID %s', self.user_id)
if self.user_id is not None \
user_model.fill_data(self, user_id=self.user_id)
is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
elif self.username:
log.debug('Auth User lookup by USER NAME %s', self.username)
dbuser = login_container_auth(self.username)
if dbuser is not None:
for k, v in dbuser.get_dict().items():
setattr(self, k, v)
self.set_authenticated()
is_user_loaded = True
if not is_user_loaded:
if self.anonymous_user.active is True:
user_model.fill_data(self,
user_id=self.anonymous_user.user_id)
#then we set this user is logged in
self.is_authenticated = True
self.user_id = None
self.username = None
if not self.username:
log.debug('Auth User is now %s', self)
user_model.fill_perms(self)
@property
def is_admin(self):
return self.admin
def full_contact(self):
return '%s %s <%s>' % (self.name, self.lastname, self.email)
def __repr__(self):
return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
self.is_authenticated)
def set_authenticated(self, authenticated=True):
if self.user_id != self.anonymous_user.user_id:
self.is_authenticated = authenticated
def set_available_permissions(config):
"""This function will propagate pylons globals with all available defined
"""The base Controller API
Provides the BaseController class for subclassing.
import logging
import time
from pylons import config, tmpl_context as c, request, session, url
from pylons.controllers import WSGIController
from pylons.controllers.util import redirect
from pylons.templating import render_mako as render
from rhodecode import __version__
from rhodecode.lib.auth import AuthUser
from rhodecode.lib.auth import AuthUser, get_container_username
from rhodecode.lib.utils import get_repo_slug
from rhodecode.model import meta
from rhodecode.model.scm import ScmModel
from rhodecode import BACKENDS
from rhodecode.model.db import Repository
log = logging.getLogger(__name__)
class BaseController(WSGIController):
def __before__(self):
c.rhodecode_version = __version__
c.rhodecode_name = config.get('rhodecode_title')
c.ga_code = config.get('rhodecode_ga_code')
c.repo_name = get_repo_slug(request)
c.backends = BACKENDS.keys()
self.cut_off_limit = int(config.get('cut_off_limit'))
self.sa = meta.Session()
self.scm_model = ScmModel(self.sa)
#c.unread_journal = scm_model.get_unread_journal()
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
start = time.time()
try:
# putting this here makes sure that we update permissions each time
api_key = request.GET.get('api_key')
user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
self.rhodecode_user.set_authenticated(
if asbool(config.get('container_auth_enabled', False)):
username = get_container_username(environ)
username = None
self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key, username)
if not self.rhodecode_user.is_authenticated and \
self.rhodecode_user.user_id is not None:
getattr(session.get('rhodecode_user'),
'is_authenticated', False))
session['rhodecode_user'] = self.rhodecode_user
session.save()
return WSGIController.__call__(self, environ, start_response)
finally:
log.debug('Request time: %.3fs' % (time.time()-start))
meta.Session.remove()
class BaseRepoController(BaseController):
Base class for controllers responsible for loading all needed data
for those controllers, loaded items are
c.rhodecode_repo: instance of scm repository (taken from cache)
super(BaseRepoController, self).__before__()
if c.repo_name:
c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
@@ -434,49 +434,50 @@ def action_parser_icon(user_log):
'admin_updated_repo':'database_edit.png',
'push':'script_add.png',
'push_local':'script_edit.png',
'push_remote':'connect.png',
'pull':'down_16.png',
'started_following_repo':'heart_add.png',
'stopped_following_repo':'heart_delete.png',
return literal(tmpl % ((url('/images/icons/')),
map.get(action, action), action))
#==============================================================================
# PERMS
from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
HasRepoPermissionAny, HasRepoPermissionAll
# GRAVATAR URL
def gravatar_url(email_address, size=30):
if not str2bool(config['app_conf'].get('use_gravatar')) or \
email_address == 'anonymous@rhodecode.org':
not email_address or \
return url("/images/user%s.png" % size)
ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
default = 'identicon'
baseurl_nossl = "http://www.gravatar.com/avatar/"
baseurl_ssl = "https://secure.gravatar.com/avatar/"
baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
if isinstance(email_address, unicode):
#hashlib crashes on unicode items
email_address = safe_str(email_address)
# construct the url
gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
return gravatar_url
# REPO PAGER, PAGER FOR REPOSITORY
class RepoPage(Page):
def __init__(self, collection, page=1, items_per_page=20,
@@ -49,49 +49,49 @@ class SimpleGitUploadPackHandler(dulserv
self.progress("counting objects: %d, done.\n" % len(objects_iter))
dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
objects_iter, len(objects_iter))
messages = []
messages.append('thank you for using rhodecode')
for msg in messages:
self.progress(msg + "\n")
# we are done
self.proto.write("0000")
dulserver.DEFAULT_HANDLERS = {
'git-upload-pack': SimpleGitUploadPackHandler,
'git-receive-pack': dulserver.ReceivePackHandler,
from dulwich.repo import Repo
from dulwich.web import HTTPGitApplication
from paste.auth.basic import AuthBasicAuthenticator
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
from rhodecode.lib import safe_str
from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware, get_container_username
from rhodecode.lib.utils import invalidate_cache, is_valid_repo
from rhodecode.model.db import User
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
def is_git(environ):
"""Returns True if request's target is git server.
``HTTP_USER_AGENT`` would then have git client version given.
:param environ:
http_user_agent = environ.get('HTTP_USER_AGENT')
if http_user_agent and http_user_agent.startswith('git'):
class SimpleGit(object):
def __init__(self, application, config):
self.application = application
@@ -127,68 +127,68 @@ class SimpleGit(object):
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):
if not get_container_username(environ, self.config):
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)
return result.wsgi_application(environ, start_response)
# CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
# BASIC AUTH
username = get_container_username(environ, self.config)
user = self.__get_user(username)
if user is None or not user.active:
return HTTPForbidden()(environ, start_response)
username = user.username
except:
return HTTPInternalServerError()(environ,
start_response)
#check permissions for this repository
perm = self.__check_permission(action, user,
if perm is not True:
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)
@@ -14,49 +14,49 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import traceback
from mercurial.error import RepoError
from mercurial.hgweb import hgweb_mod
from rhodecode.lib.utils import make_ui, invalidate_cache, \
is_valid_repo, ui_sections
def is_mercurial(environ):
"""Returns True if request's target is mercurial server - header
``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
http_accept = environ.get('HTTP_ACCEPT')
if http_accept and http_accept.startswith('application/mercurial'):
class SimpleHg(object):
self.config = config
@@ -93,68 +93,68 @@ class SimpleHg(object):
# MERCURIAL REQUEST HANDLING
@@ -71,48 +71,77 @@ class UserModel(BaseModel):
def get_by_api_key(self, api_key, cache=False):
user = self.sa.query(User)\
.filter(User.api_key == api_key)
if cache:
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % api_key))
return user.scalar()
def create(self, form_data):
new_user = User()
for k, v in form_data.items():
setattr(new_user, k, v)
new_user.api_key = generate_api_key(form_data['username'])
self.sa.add(new_user)
self.sa.commit()
return new_user
self.sa.rollback()
raise
def create_for_container_auth(self, username, attrs):
Creates the given user if it's not already in the database
:param username:
:param attrs:
if self.get_by_username(username, case_insensitive=True) is None:
new_user.username = username
new_user.password = None
new_user.api_key = generate_api_key(username)
new_user.email = attrs['email']
new_user.active = True
new_user.name = attrs['name']
new_user.lastname = attrs['lastname']
except (DatabaseError,):
log.debug('User %s already exists. Skipping creation of account for container auth.',
username)
def create_ldap(self, username, password, user_dn, attrs):
Checks if user is in database, if not creates this user marked
as ldap user
:param password:
:param user_dn:
from rhodecode.lib.auth import get_crypt_password
log.debug('Checking for such ldap account in RhodeCode database')
# add ldap account always lowercase
new_user.username = username.lower()
new_user.password = get_crypt_password(password)
new_user.ldap_dn = safe_unicode(user_dn)
@@ -222,58 +251,61 @@ class UserModel(BaseModel):
def reset_password(self, data):
from rhodecode.lib.celerylib import tasks, run_task
run_task(tasks.reset_user_password, data['email'])
def fill_data(self, auth_user, user_id=None, api_key=None):
Fetches auth_user by user_id,or api_key if present.
Fills auth_user attributes with those taken from database.
Additionally set's is_authenitated if lookup fails
present in database
:param auth_user: instance of user to set attributes
:param user_id: user id to fetch by
:param api_key: api key to fetch by
if user_id is None and api_key is None:
raise Exception('You need to pass user_id or api_key')
if api_key:
dbuser = self.get_by_api_key(api_key)
dbuser = self.get(user_id)
if dbuser is not None and dbuser.active:
log.debug('filling %s data', dbuser)
setattr(auth_user, k, v)
auth_user.is_authenticated = False
return auth_user
def fill_perms(self, user):
Fills user permission attribute with permissions taken from database
works for permissions given for repositories, and for permissions that
are granted to groups
:param user: user instance to fill his perms
user.permissions['repositories'] = {}
user.permissions['global'] = set()
# fetch default permissions
default_user = self.get_by_username('default', cache=True)
default_perms = self.sa.query(RepoToPerm, Repository, Permission)\
.join((Repository, RepoToPerm.repository_id ==
Repository.repo_id))\
.join((Permission, RepoToPerm.permission_id ==
Permission.permission_id))\
.filter(RepoToPerm.user == default_user).all()
Status change: