@@ -76,12 +76,14 @@ def make_map(config):
#ADMIN USER REST ROUTES
map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
#ADMIN PERMISSIONS REST ROUTES
map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
map.connect('permissions_ldap', '/_admin/permissions_ldap', controller='admin/permissions', action='ldap')
#ADMIN SETTINGS REST ROUTES
with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
m.connect("admin_settings", "/settings",
action="create", conditions=dict(method=["POST"]))
@@ -26,15 +26,17 @@ permissions controller for pylons
from formencode import htmlfill
from pylons import request, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.auth_ldap import LdapImportError
from rhodecode.lib.base import BaseController, render
from rhodecode.model.forms import UserForm, DefaultPermissionsForm
from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
from rhodecode.model.permission import PermissionModel
from rhodecode.model.settings import SettingsModel
from rhodecode.model.user import UserModel
import formencode
import logging
import traceback
log = logging.getLogger(__name__)
@@ -96,23 +98,25 @@ class PermissionsController(BaseControll
[x[0] for x in self.create_choices])()
try:
form_result = _form.to_python(dict(request.POST))
form_result.update({'perm_user_name':id})
permission_model.update(form_result)
h.flash(_('Default permissions updated succesfully'),
h.flash(_('Default permissions updated successfully'),
category='success')
except formencode.Invalid, errors:
c.perms_choices = self.perms_choices
c.register_choices = self.register_choices
c.create_choices = self.create_choices
defaults = errors.value
defaults.update(SettingsModel().get_ldap_settings())
return htmlfill.render(
render('admin/permissions/permissions.html'),
defaults=errors.value,
defaults=defaults,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occured during update of permissions'),
@@ -143,12 +147,13 @@ class PermissionsController(BaseControll
if id == 'default':
default_user = UserModel().get_by_username('default')
defaults = {'_method':'put',
'anonymous':default_user.active}
for p in default_user.user_perms:
if p.permission.permission_name.startswith('repository.'):
defaults['default_perm'] = p.permission.permission_name
if p.permission.permission_name.startswith('hg.register.'):
defaults['default_register'] = p.permission.permission_name
@@ -160,6 +165,53 @@ class PermissionsController(BaseControll
encoding="UTF-8",
force_defaults=True,)
else:
return redirect(url('admin_home'))
def ldap(self, id_user='default'):
"""
POST ldap create and store ldap settings
settings_model = SettingsModel()
_form = LdapSettingsForm()()
for k, v in form_result.items():
if k.startswith('ldap_'):
setting = settings_model.get(k)
setting.app_settings_value = v
self.sa.add(setting)
self.sa.commit()
h.flash(_('Ldap settings updated successfully'),
except:
raise
except LdapImportError:
h.flash(_('Unable to activate ldap. The "ldap-python" library '
'is missing.'),
category='warning')
h.flash(_('error occured during update of ldap settings'),
category='error')
return redirect(url('edit_permission', id=id_user))
@@ -28,20 +28,21 @@ from pylons import request, session, tmp
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
HasPermissionAnyDecorator
from rhodecode.lib.celerylib import tasks, run_task
from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
set_rhodecode_config, get_hg_settings, get_hg_ui_settings
from rhodecode.model.db import RhodeCodeSettings, RhodeCodeUi, Repository
from rhodecode.model.db import RhodeCodeUi, Repository
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
ApplicationUiSettingsForm
from rhodecode.model.scm import ScmModel
from sqlalchemy import func
@@ -115,24 +116,18 @@ class SettingsController(BaseController)
h.flash(_('Whoosh reindex task scheduled'), category='success')
if setting_id == 'global':
application_form = ApplicationSettingsForm()()
form_result = application_form.to_python(dict(request.POST))
hgsettings1 = self.sa.query(RhodeCodeSettings)\
.filter(RhodeCodeSettings.app_settings_name \
== 'title').one()
hgsettings1 = settings_model.get('title')
hgsettings1.app_settings_value = form_result['rhodecode_title']
hgsettings2 = self.sa.query(RhodeCodeSettings)\
== 'realm').one()
hgsettings2 = settings_model('realm')
hgsettings2.app_settings_value = form_result['rhodecode_realm']
self.sa.add(hgsettings1)
self.sa.add(hgsettings2)
@@ -22,21 +22,23 @@ Created on April 4, 2010
@author: marcink
from pylons import config, session, url, request
from rhodecode.lib.utils import get_repo_slug
from rhodecode.lib.auth_ldap import AuthLdap, UsernameError, PasswordError
from rhodecode.model import meta
from rhodecode.model.caching_query import FromCache
from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
UserToPerm
import bcrypt
from decorator import decorator
import random
class PasswordGenerator(object):
"""This is a simple class for generating password from
different sets of characters
@@ -71,35 +73,70 @@ def get_crypt_password(password):
def check_password(password, hashed):
return bcrypt.hashpw(password, hashed) == hashed
def authfunc(environ, username, password):
Authentication function used in Mercurial/Git/ and access controll,
Authentication function used in Mercurial/Git/ and access control,
firstly checks for db authentication then if ldap is enabled for ldap
authentication
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)
user = UserModel().get_by_username(username, cache=False)
if user:
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):
log.error('user %s is disabled', username)
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')
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 (UsernameError, PasswordError):
return False
class AuthUser(object):
A simple object that handles a mercurial username for authentication
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger('ldap')
#==============================================================================
# LDAP
#Name = Just a description for the auth modes page
#Host = DepartmentName.OrganizationName.local/ IP
#Port = 389 default for ldap
#LDAPS = no set True if You need to use ldaps
#Account = DepartmentName\UserName (or UserName@MyDomain depending on AD server)
#Password = <password>
#Base DN = DC=DepartmentName,DC=OrganizationName,DC=local
#
#On-the-fly user creation = yes
#Attributes
# Login = sAMAccountName
# Firstname = givenName
# Lastname = sN
# Email = mail
class UsernameError(Exception):pass
class PasswordError(Exception):pass
from rhodecode.lib.exceptions import LdapImportError, UsernameError, \
PasswordError, ConnectionError
LDAP_USE_LDAPS = False
ldap_server_type = 'ldap'
LDAP_SERVER_ADDRESS = 'myldap.com'
LDAP_SERVER_PORT = '389'
import ldap
except ImportError:
pass
#USE FOR READ ONLY BIND TO LDAP SERVER
LDAP_BIND_DN = ''
LDAP_BIND_PASS = ''
class AuthLdap(object):
if LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
LDAP_SERVER_ADDRESS,
LDAP_SERVER_PORT)
BASE_DN = "ou=people,dc=server,dc=com"
AUTH_DN = "uid=%s,%s"
def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
use_ldaps=False, ldap_version=3):
self.ldap_version = ldap_version
if use_ldaps:
port = port or 689
self.LDAP_USE_LDAPS = use_ldaps
self.LDAP_SERVER_ADDRESS = server
self.LDAP_SERVER_PORT = port
def authenticate_ldap(username, password):
"""Authenticate a user via LDAP and return his/her LDAP properties.
self.LDAP_BIND_DN = bind_dn
self.LDAP_BIND_PASS = bind_pass
Raises AuthenticationError if the credentials are rejected, or
EnvironmentError if the LDAP server can't be reached.
raise Exception('Could not import ldap make sure You install python-ldap')
if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
self.LDAP_SERVER_ADDRESS,
self.LDAP_SERVER_PORT)
self.BASE_DN = base_dn
self.AUTH_DN = "uid=%s,%s"
from rhodecode.lib.helpers import chop_at
uid = chop_at(username, "@%s" % LDAP_SERVER_ADDRESS)
dn = AUTH_DN % (uid, BASE_DN)
log.debug("Authenticating %r at %s", dn, LDAP_SERVER)
if "," in username:
raise UsernameError("invalid character in username: ,")
#ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
server = ldap.initialize(LDAP_SERVER)
server.protocol = ldap.VERSION3
if LDAP_BIND_DN and LDAP_BIND_PASS:
server.simple_bind_s(AUTH_DN % (LDAP_BIND_DN,
LDAP_BIND_PASS),
password)
def authenticate_ldap(self, username, password):
server.simple_bind_s(dn, password)
properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
if not properties:
raise ldap.NO_SUCH_OBJECT()
except ldap.NO_SUCH_OBJECT, e:
log.debug("LDAP says no such user '%s' (%s)", uid, username)
raise UsernameError()
except ldap.INVALID_CREDENTIALS, e:
log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
raise PasswordError()
except ldap.SERVER_DOWN, e:
raise EnvironmentError("can't access authentication server")
return properties
uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
dn = self.AUTH_DN % (uid, self.BASE_DN)
log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
server = ldap.initialize(self.LDAP_SERVER)
if self.ldap_version == 2:
server.protocol = ldap.VERSION2
if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
self.BASE_DN),
self.LDAP_BIND_PASS)
print authenticate_ldap('test', 'test')
raise ConnectionError("LDAP can't access authentication server")
return properties[0]
@@ -22,12 +22,13 @@ 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 rhodecode.lib.auth import authfunc, get_crypt_password
from rhodecode.lib.exceptions import LdapImportError
from rhodecode.model.repo import RepoModel
from rhodecode.model.db import User
from webhelpers.pylonslib.secure_form import authentication_token
from vcs import BACKENDS
@@ -79,13 +80,13 @@ class ValidPassword(formencode.validator
return get_crypt_password(value)
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']}
@@ -233,12 +234,22 @@ class ValidSystemEmail(formencode.valida
value, state)
finally:
meta.Session.remove()
return value
class LdapLibValidator(formencode.validators.FancyValidator):
def to_python(self, value, state):
raise LdapImportError
#===============================================================================
# FORMS
class LoginForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
@@ -349,13 +360,29 @@ def ApplicationUiSettingsForm():
return _ApplicationUiSettingsForm
def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
class _DefaultPermissionsForm(formencode.Schema):
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):
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 = UnicodeString(strip=True,)
return _LdapSettingsForm
@@ -93,11 +93,6 @@ class PermissionModel(object):
self.sa.rollback()
@@ -48,12 +48,23 @@ class SettingsModel(object):
r = r.options(FromCache("sql_cache_short",
"get_setting_%s" % settings_key))
return r
def get_ldap_settings(self):
Returns ldap settings from database
:returns:
ldap_active
ldap_host
ldap_port
ldap_ldaps
ldap_dn_user
ldap_dn_pass
ldap_base_dn
r = self.sa.query(RhodeCodeSettings)\
.filter(RhodeCodeSettings.app_settings_name\
.startswith('ldap_'))\
.all()
@@ -65,12 +65,42 @@ class UserModel(object):
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 = User()
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 = ''
self.sa.add(new_user)
def create_registration(self, form_data):
for k, v in form_data.items():
if k != 'admin':
Status change: