@@ -62,97 +62,99 @@ class PasswordGenerator(object):
self.passwd = passwd
def gen_password(self, len, type):
self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
return self.passwd
def get_crypt_password(password):
"""Cryptographic function used for password hashing based on sha1
:param password: password to hash
"""
return bcrypt.hashpw(password, bcrypt.gensalt(10))
def check_password(password, hashed):
return bcrypt.hashpw(password, hashed) == hashed
def authfunc(environ, username, password):
Authentication function used in Mercurial/Git/ and access control,
firstly checks for db authentication then if ldap is enabled for ldap
authentication, also creates ldap user if not in database
:param environ: needed only for using in Basic auth, can be None
:param username: username
:param password: password
user_model = UserModel()
user = user_model.get_by_username(username, cache=False)
if user is not None and user.is_ldap is False:
if user.active:
if user.username == 'default' and user.active:
log.info('user %s authenticated correctly', username)
return True
elif user.username == username and check_password(password, user.password):
else:
log.error('user %s is disabled', username)
#since ldap is searching in case insensitive check if this user is still
#not in our system
username = username.lower()
if user_model.get_by_username(username, cache=False) is not None:
user_obj = user_model.get_by_username(username, cache=False,
case_insensitive=True)
if user_obj is not None:
return False
from rhodecode.model.settings import SettingsModel
ldap_settings = SettingsModel().get_ldap_settings()
#======================================================================
# FALLBACK TO LDAP AUTH IN ENABLE
if ldap_settings.get('ldap_active', False):
kwargs = {
'server':ldap_settings.get('ldap_host', ''),
'base_dn':ldap_settings.get('ldap_base_dn', ''),
'port':ldap_settings.get('ldap_port'),
'bind_dn':ldap_settings.get('ldap_dn_user'),
'bind_pass':ldap_settings.get('ldap_dn_pass'),
'use_ldaps':ldap_settings.get('ldap_ldaps'),
'ldap_version':3,
}
log.debug('Checking for ldap authentication')
try:
aldap = AuthLdap(**kwargs)
res = aldap.authenticate_ldap(username, password)
authenticated = res[1]['uid'][0] == username
if authenticated and user_model.create_ldap(username, password):
log.info('created new ldap user')
return authenticated
except (LdapUsernameError, LdapPasswordError):
except:
log.error(traceback.format_exc())
class AuthUser(object):
A simple object that handles a mercurial username for authentication
def __init__(self):
self.username = 'None'
self.name = ''
self.lastname = ''
self.email = ''
self.user_id = None
self.is_authenticated = False
@@ -22,97 +22,98 @@ for SELECT use formencode.All(OneOf(list
from formencode import All
from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
Email, Bool, StringBoolean
from pylons import session
from pylons.i18n.translation import _
from rhodecode.lib.auth import authfunc, get_crypt_password
from rhodecode.lib.exceptions import LdapImportError
from rhodecode.model import meta
from rhodecode.model.user import UserModel
from rhodecode.model.repo import RepoModel
from rhodecode.model.db import User
from webhelpers.pylonslib.secure_form import authentication_token
from rhodecode import BACKENDS
import formencode
import logging
import os
import rhodecode.lib.helpers as h
log = logging.getLogger(__name__)
#this is needed to translate the messages using _() in validators
class State_obj(object):
_ = staticmethod(_)
#===============================================================================
# VALIDATORS
class ValidAuthToken(formencode.validators.FancyValidator):
messages = {'invalid_token':_('Token mismatch')}
def validate_python(self, value, state):
if value != authentication_token():
raise formencode.Invalid(self.message('invalid_token', state,
search_number=value), value, state)
def ValidUsername(edit, old_data):
class _ValidUsername(formencode.validators.FancyValidator):
if value in ['default', 'new_user']:
raise formencode.Invalid(_('Invalid username'), value, state)
#check if user is unique
old_un = None
if edit:
old_un = UserModel().get(old_data.get('user_id')).username
if old_un != value or not edit:
if UserModel().get_by_username(value.lower(), cache=False):
if UserModel().get_by_username(value, cache=False,
case_insensitive=True):
raise formencode.Invalid(_('This username already exists') ,
value, state)
return _ValidUsername
class ValidPassword(formencode.validators.FancyValidator):
def to_python(self, value, state):
if value:
if value.get('password'):
value['password'] = get_crypt_password(value['password'])
except UnicodeEncodeError:
e_dict = {'password':_('Invalid characters in password')}
raise formencode.Invalid('', value, state, error_dict=e_dict)
if value.get('password_confirmation'):
value['password_confirmation'] = \
get_crypt_password(value['password_confirmation'])
e_dict = {'password_confirmation':_('Invalid characters in password')}
if value.get('new_password'):
value['new_password'] = \
get_crypt_password(value['new_password'])
e_dict = {'new_password':_('Invalid characters in password')}
return value
class ValidPasswordsMatch(formencode.validators.FancyValidator):
if value['password'] != value['password_confirmation']:
e_dict = {'password_confirmation':
_('Password do not match')}
class ValidAuth(formencode.validators.FancyValidator):
messages = {
'invalid_password':_('invalid password'),
@@ -138,296 +139,303 @@ class ValidAuth(formencode.validators.Fa
raise formencode.Invalid(self.message('disabled_account',
state=State_obj),
value, state,
error_dict=self.e_dict_disable)
log.warning('user %s not authenticated', username)
raise formencode.Invalid(self.message('invalid_password',
state=State_obj), value, state,
error_dict=self.e_dict)
class ValidRepoUser(formencode.validators.FancyValidator):
sa = meta.Session()
self.user_db = sa.query(User)\
.filter(User.active == True)\
.filter(User.username == value).one()
except Exception:
raise formencode.Invalid(_('This username is not valid'),
finally:
meta.Session.remove()
return self.user_db.user_id
def ValidRepoName(edit, old_data):
class _ValidRepoName(formencode.validators.FancyValidator):
slug = h.repo_name_slug(value)
if slug in ['_admin']:
raise formencode.Invalid(_('This repository name is disallowed'),
if old_data.get('repo_name') != value or not edit:
if RepoModel().get_by_repo_name(slug, cache=False):
raise formencode.Invalid(_('This repository already exists') ,
return slug
return _ValidRepoName
def ValidForkType(old_data):
class _ValidForkType(formencode.validators.FancyValidator):
if old_data['repo_type'] != value:
raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
raise formencode.Invalid(_('Fork have to be the same type as original'),
return _ValidForkType
class ValidPerms(formencode.validators.FancyValidator):
messages = {'perm_new_user_name':_('This username is not valid')}
perms_update = []
perms_new = []
#build a list of permission to update and new permission to create
for k, v in value.items():
if k.startswith('perm_'):
if k.startswith('perm_new_user'):
new_perm = value.get('perm_new_user', False)
new_user = value.get('perm_new_user_name', False)
if new_user and new_perm:
if (new_user, new_perm) not in perms_new:
perms_new.append((new_user, new_perm))
usr = k[5:]
if usr == 'default':
if value['private']:
#set none for default when updating to private repo
v = 'repository.none'
perms_update.append((usr, v))
value['perms_updates'] = perms_update
value['perms_new'] = perms_new
sa = meta.Session
for k, v in perms_new:
.filter(User.username == k).one()
msg = self.message('perm_new_user_name',
state=State_obj)
raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
raise formencode.Invalid(msg, value, state,
error_dict={'perm_new_user_name':msg})
class ValidSettings(formencode.validators.FancyValidator):
#settings form can't edit user
if value.has_key('user'):
del['value']['user']
class ValidPath(formencode.validators.FancyValidator):
if not os.path.isdir(value):
msg = _('This is not a valid path')
error_dict={'paths_root_path':msg})
def UniqSystemEmail(old_data):
class _UniqSystemEmail(formencode.validators.FancyValidator):
value = value.lower()
#TODO:write test for MixedCase scenarios
if old_data.get('email') != value:
user = sa.query(User).filter(User.email == value).scalar()
if user:
raise formencode.Invalid(_("That e-mail address is already taken") ,
return _UniqSystemEmail
class ValidSystemEmail(formencode.validators.FancyValidator):
if user is None:
raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
class LdapLibValidator(formencode.validators.FancyValidator):
import ldap
except ImportError:
raise LdapImportError
# FORMS
class LoginForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
username = UnicodeString(
strip=True,
min=1,
not_empty=True,
messages={
'empty':_('Please enter a login'),
'tooShort':_('Enter a value %(min)i characters long or more')}
)
password = UnicodeString(
min=6,
'empty':_('Please enter a password'),
'tooShort':_('Enter %(min)i characters or more')}
#chained validators have access to all data
chained_validators = [ValidAuth]
def UserForm(edit=False, old_data={}):
class _UserForm(formencode.Schema):
username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
username = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidUsername(edit, old_data))
new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
admin = StringBoolean(if_missing=False)
password = All(UnicodeString(strip=True, min=6, not_empty=True))
active = StringBoolean(if_missing=False)
name = UnicodeString(strip=True, min=1, not_empty=True)
lastname = UnicodeString(strip=True, min=1, not_empty=True)
email = All(Email(not_empty=True), UniqSystemEmail(old_data))
chained_validators = [ValidPassword]
return _UserForm
def RegisterForm(edit=False, old_data={}):
class _RegisterForm(formencode.Schema):
username = All(ValidUsername(edit, old_data), UnicodeString(strip=True, min=1, not_empty=True))
username = All(ValidUsername(edit, old_data),
UnicodeString(strip=True, min=1, not_empty=True))
password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
chained_validators = [ValidPasswordsMatch, ValidPassword]
return _RegisterForm
def PasswordResetForm():
class _PasswordResetForm(formencode.Schema):
email = All(ValidSystemEmail(), Email(not_empty=True))
return _PasswordResetForm
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
class _RepoForm(formencode.Schema):
filter_extra_fields = False
repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidRepoName(edit, old_data))
description = UnicodeString(strip=True, min=1, not_empty=True)
private = StringBoolean(if_missing=False)
repo_type = OneOf(supported_backends)
user = All(Int(not_empty=True), ValidRepoUser)
chained_validators = [ValidPerms]
return _RepoForm
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
class _RepoForkForm(formencode.Schema):
fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
return _RepoForkForm
def RepoSettingsForm(edit=False, old_data={}):
chained_validators = [ValidPerms, ValidSettings]
def ApplicationSettingsForm():
class _ApplicationSettingsForm(formencode.Schema):
rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
return _ApplicationSettingsForm
def ApplicationUiSettingsForm():
class _ApplicationUiSettingsForm(formencode.Schema):
web_push_ssl = OneOf(['true', 'false'], if_missing='false')
paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
return _ApplicationUiSettingsForm
def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
class _DefaultPermissionsForm(formencode.Schema):
overwrite_default = StringBoolean(if_missing=False)
anonymous = OneOf(['True', 'False'], if_missing=False)
default_perm = OneOf(perms_choices)
default_register = OneOf(register_choices)
default_create = OneOf(create_choices)
return _DefaultPermissionsForm
def LdapSettingsForm():
class _LdapSettingsForm(formencode.Schema):
pre_validators = [LdapLibValidator]
ldap_active = StringBoolean(if_missing=False)
@@ -3,99 +3,103 @@
# Model for users
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
#
# 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; version 2
# of the License or (at your opinion) any later version of the license.
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
Created on April 9, 2010
Model for users
:author: marcink
from rhodecode.model.caching_query import FromCache
from rhodecode.model.meta import Session
from rhodecode.lib.exceptions import *
import traceback
class UserModel(object):
self.sa = Session()
def get(self, user_id, cache=False):
user = self.sa.query(User)
if cache:
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % user_id))
return user.get(user_id)
def get_by_username(self, username, cache=False):
user = self.sa.query(User)\
.filter(User.username == username)
def get_by_username(self, username, cache=False, case_insensitive=False):
if case_insensitive:
user = self.sa.query(User).filter(User.username.ilike(username))
"get_user_%s" % username))
return user.scalar()
def create(self, form_data):
new_user = User()
for k, v in form_data.items():
setattr(new_user, k, v)
self.sa.add(new_user)
self.sa.commit()
self.sa.rollback()
raise
def create_ldap(self, username, password):
Checks if user is in database, if not creates this user marked
as ldap user
:param username:
:param password:
if self.get_by_username(username) is None:
new_user.username = username
new_user.password = password
new_user.email = '%s@ldap.server' % username
new_user.active = True
new_user.is_ldap = True
new_user.name = '%s@ldap' % username
new_user.lastname = ''
Status change: