diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -19,29 +19,34 @@ list=[1,2,3,4,5] for SELECT use formencode.All(OneOf(list), Int()) """ +import os +import re +import logging + +import formencode 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 check_password, get_crypt_password + +import rhodecode.lib.helpers as h +from rhodecode.lib.auth import authenticate, get_crypt_password +from rhodecode.lib.exceptions import LdapImportError from rhodecode.model import meta -from rhodecode.model.user_model import UserModel -from rhodecode.model.db import User, Repository -from sqlalchemy.exc import OperationalError -from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound +from rhodecode.model.user import UserModel +from rhodecode.model.repo import RepoModel +from rhodecode.model.db import User +from rhodecode import BACKENDS + from webhelpers.pylonslib.secure_form import authentication_token -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 #=============================================================================== @@ -53,75 +58,114 @@ class ValidAuthToken(formencode.validato if value != authentication_token(): raise formencode.Invalid(self.message('invalid_token', state, search_number=value), value, state) - -def ValidUsername(edit, old_data): + +def ValidUsername(edit, old_data): class _ValidUsername(formencode.validators.FancyValidator): - + def validate_python(self, value, state): if value in ['default', 'new_user']: raise formencode.Invalid(_('Invalid username'), value, state) - #check if user is uniq - sa = meta.Session + #check if user is unique old_un = None if edit: - old_un = sa.query(User).get(old_data.get('user_id')).username - - if old_un != value or not edit: - if sa.query(User).filter(User.username == value).scalar(): + old_un = UserModel().get(old_data.get('user_id')).username + + if old_un != value or not edit: + if UserModel().get_by_username(value, cache=False, + case_insensitive=True): raise formencode.Invalid(_('This username already exists') , value, state) - meta.Session.remove() - - return _ValidUsername - + + + if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_]+$', value) is None: + raise formencode.Invalid(_('Username may only contain ' + 'alphanumeric characters underscores ' + 'or dashes and must begin with ' + 'alphanumeric character'), + value, state) + + + + return _ValidUsername + class ValidPassword(formencode.validators.FancyValidator): - + def to_python(self, value, state): + if value: - return get_crypt_password(value) - + + if value.get('password'): + try: + 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'): + try: + value['password_confirmation'] = \ + get_crypt_password(value['password_confirmation']) + except UnicodeEncodeError: + e_dict = {'password_confirmation':_('Invalid characters in password')} + raise formencode.Invalid('', value, state, error_dict=e_dict) + + if value.get('new_password'): + try: + value['new_password'] = \ + get_crypt_password(value['new_password']) + except UnicodeEncodeError: + e_dict = {'new_password':_('Invalid characters in password')} + raise formencode.Invalid('', value, state, error_dict=e_dict) + + return value + +class ValidPasswordsMatch(formencode.validators.FancyValidator): + + def validate_python(self, value, state): + + if value['password'] != value['password_confirmation']: + e_dict = {'password_confirmation': + _('Password do not match')} + raise formencode.Invalid('', value, state, error_dict=e_dict) + class ValidAuth(formencode.validators.FancyValidator): messages = { 'invalid_password':_('invalid password'), 'invalid_login':_('invalid user name'), - 'disabled_account':_('Your acccount is disabled') - + 'disabled_account':_('Your account is disabled') + } #error mapping e_dict = {'username':messages['invalid_login'], 'password':messages['invalid_password']} e_dict_disable = {'username':messages['disabled_account']} - + def validate_python(self, value, state): password = value['password'] username = value['username'] - user = UserModel().get_user_by_name(username) - if user is None: - raise formencode.Invalid(self.message('invalid_password', - state=State_obj), value, state, - error_dict=self.e_dict) - if user: - if user.active: - if user.username == username and check_password(password, - user.password): - return value - else: - log.warning('user %s not authenticated', username) - raise formencode.Invalid(self.message('invalid_password', - state=State_obj), value, state, - error_dict=self.e_dict) - else: + user = UserModel().get_by_username(username) + + if authenticate(username, password): + return value + else: + if user and user.active is False: log.warning('user %s is disabled', username) raise formencode.Invalid(self.message('disabled_account', state=State_obj), value, state, error_dict=self.e_dict_disable) - + else: + 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): - + def to_python(self, value, state): + sa = meta.Session() try: - self.user_db = meta.Session.query(User)\ + self.user_db = sa.query(User)\ .filter(User.active == True)\ .filter(User.username == value).one() except Exception: @@ -129,31 +173,39 @@ class ValidRepoUser(formencode.validator value, state) finally: meta.Session.remove() - + return self.user_db.user_id -def ValidRepoName(edit, old_data): +def ValidRepoName(edit, old_data): class _ValidRepoName(formencode.validators.FancyValidator): - + def to_python(self, value, state): slug = h.repo_name_slug(value) if slug in ['_admin']: raise formencode.Invalid(_('This repository name is disallowed'), value, state) - if old_data.get('repo_name') != value or not edit: - sa = meta.Session - if sa.query(Repository).filter(Repository.repo_name == slug).scalar(): + 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') , value, state) - meta.Session.remove() - return slug - - + return slug + + return _ValidRepoName +def ValidForkType(old_data): + class _ValidForkType(formencode.validators.FancyValidator): + + def to_python(self, value, state): + if old_data['repo_type'] != value: + raise formencode.Invalid(_('Fork have to be the same type as original'), + value, state) + return value + return _ValidForkType + class ValidPerms(formencode.validators.FancyValidator): messages = {'perm_new_user_name':_('This username is not valid')} - + def to_python(self, value, state): perms_update = [] perms_new = [] @@ -167,7 +219,7 @@ class ValidPerms(formencode.validators.F if (new_user, new_perm) not in perms_new: perms_new.append((new_user, new_perm)) else: - usr = k[5:] + usr = k[5:] if usr == 'default': if value['private']: #set none for default when updating to private repo @@ -184,60 +236,89 @@ class ValidPerms(formencode.validators.F except Exception: 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}) return value - + class ValidSettings(formencode.validators.FancyValidator): - + def to_python(self, value, state): #settings form can't edit user if value.has_key('user'): del['value']['user'] - + return value - + class ValidPath(formencode.validators.FancyValidator): def to_python(self, value, state): - isdir = os.path.isdir(value.replace('*', '')) - if (value.endswith('/*') or value.endswith('/**')) and isdir: - return value - elif not isdir: - msg = _('This is not a valid path') - else: - msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)') - - raise formencode.Invalid(msg, value, state, - error_dict={'paths_root_path':msg}) + + if not os.path.isdir(value): + msg = _('This is not a valid path') + raise formencode.Invalid(msg, value, state, + error_dict={'paths_root_path':msg}) + return value def UniqSystemEmail(old_data): class _UniqSystemEmail(formencode.validators.FancyValidator): def to_python(self, value, state): + value = value.lower() if old_data.get('email') != value: - sa = meta.Session + sa = meta.Session() try: user = sa.query(User).filter(User.email == value).scalar() if user: - raise formencode.Invalid(_("That e-mail address is already taken") , + raise formencode.Invalid(_("This e-mail address is already taken") , value, state) finally: meta.Session.remove() - + return value - + return _UniqSystemEmail - + class ValidSystemEmail(formencode.validators.FancyValidator): def to_python(self, value, state): + value = value.lower() sa = meta.Session try: user = sa.query(User).filter(User.email == value).scalar() if user is None: - raise formencode.Invalid(_("That e-mail address doesn't exist.") , + raise formencode.Invalid(_("This e-mail address doesn't exist.") , value, state) finally: meta.Session.remove() - - return value + + return value + +class LdapLibValidator(formencode.validators.FancyValidator): + + def to_python(self, value, state): + + try: + import ldap + except ImportError: + raise LdapImportError + return value + +class BaseDnValidator(formencode.validators.FancyValidator): + + def to_python(self, value, state): + + try: + value % {'user':'valid'} + + if value.find('%(user)s') == -1: + raise formencode.Invalid(_("You need to specify %(user)s in " + "template for example uid=%(user)s " + ",dc=company...") , + value, state) + + except KeyError: + raise formencode.Invalid(_("Wrong template used, only %(user)s " + "is an valid entry") , + value, state) + + return value #=============================================================================== # FORMS @@ -266,65 +347,87 @@ class LoginForm(formencode.Schema): #chained validators have access to all data chained_validators = [ValidAuth] - + def UserForm(edit=False, old_data={}): class _UserForm(formencode.Schema): allow_extra_fields = True filter_extra_fields = True - 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)) if edit: - new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword) + new_password = All(UnicodeString(strip=True, min=6, not_empty=False)) admin = StringBoolean(if_missing=False) else: - password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword) + 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 -RegisterForm = UserForm +def RegisterForm(edit=False, old_data={}): + class _RegisterForm(formencode.Schema): + allow_extra_fields = True + filter_extra_fields = True + username = All(ValidUsername(edit, old_data), + UnicodeString(strip=True, min=1, not_empty=True)) + password = All(UnicodeString(strip=True, min=6, not_empty=True)) + password_confirmation = 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 = [ValidPasswordsMatch, ValidPassword] + + return _RegisterForm def PasswordResetForm(): class _PasswordResetForm(formencode.Schema): allow_extra_fields = True filter_extra_fields = True - email = All(ValidSystemEmail(), Email(not_empty=True)) + email = All(ValidSystemEmail(), Email(not_empty=True)) return _PasswordResetForm -def RepoForm(edit=False, old_data={}): +def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): class _RepoForm(formencode.Schema): allow_extra_fields = True 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) - + enable_statistics = StringBoolean(if_missing=False) + repo_type = OneOf(supported_backends) if edit: user = All(Int(not_empty=True), ValidRepoUser) - + chained_validators = [ValidPerms] return _RepoForm -def RepoForkForm(edit=False, old_data={}): +def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): class _RepoForkForm(formencode.Schema): allow_extra_fields = True filter_extra_fields = False - 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), + ValidRepoName(edit, old_data)) description = UnicodeString(strip=True, min=1, not_empty=True) private = StringBoolean(if_missing=False) - + repo_type = All(ValidForkType(old_data), OneOf(supported_backends)) return _RepoForkForm def RepoSettingsForm(edit=False, old_data={}): class _RepoForm(formencode.Schema): allow_extra_fields = True 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) - + chained_validators = [ValidPerms, ValidSettings] return _RepoForm @@ -335,9 +438,9 @@ def ApplicationSettingsForm(): filter_extra_fields = False 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): allow_extra_fields = True @@ -346,16 +449,35 @@ def ApplicationUiSettingsForm(): 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): allow_extra_fields = True filter_extra_fields = True - overwrite_default = OneOf(['true', 'false'], if_missing='false') + 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): + allow_extra_fields = True + filter_extra_fields = True + pre_validators = [LdapLibValidator] + ldap_active = StringBoolean(if_missing=False) + ldap_host = UnicodeString(strip=True,) + ldap_port = Number(strip=True,) + ldap_ldaps = StringBoolean(if_missing=False) + ldap_dn_user = UnicodeString(strip=True,) + ldap_dn_pass = UnicodeString(strip=True,) + ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,)) + + return _LdapSettingsForm