@@ -79,6 +79,8 @@ def make_map(config):
#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:
@@ -29,9 +29,11 @@ from pylons.controllers.util import abor
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
@@ -99,17 +101,19 @@ class PermissionsController(BaseControll
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")
@@ -146,6 +150,7 @@ class PermissionsController(BaseControll
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
@@ -163,3 +168,50 @@ class PermissionsController(BaseControll
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()()
try:
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')
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occured during update of ldap settings'),
category='error')
return redirect(url('edit_permission', id=id_user))
@@ -31,14 +31,15 @@ from rhodecode.lib import helpers as h
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
@@ -118,18 +119,12 @@ class SettingsController(BaseController)
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']
@@ -25,6 +25,7 @@ Created on April 4, 2010
from pylons import config, session, url, request
from pylons.controllers.util import abort, redirect
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
@@ -34,6 +35,7 @@ import bcrypt
from decorator import decorator
import random
import traceback
log = logging.getLogger(__name__)
@@ -74,17 +76,18 @@ def check_password(password, 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:
@@ -97,6 +100,40 @@ def authfunc(environ, username, 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):
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger('ldap')
#==============================================================================
# LDAP
#Name = Just a description for the auth modes page
@@ -11,76 +7,87 @@ log = logging.getLogger('ldap')
#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]
@@ -25,6 +25,7 @@ from formencode.validators import Unicod
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
@@ -82,7 +83,7 @@ class ValidAuth(formencode.validators.Fa
messages = {
'invalid_password':_('invalid password'),
'invalid_login':_('invalid user name'),
'disabled_account':_('Your acccount is disabled')
'disabled_account':_('Your account is disabled')
#error mapping
@@ -236,6 +237,16 @@ class ValidSystemEmail(formencode.valida
return value
class LdapLibValidator(formencode.validators.FancyValidator):
def to_python(self, value, state):
raise LdapImportError
#===============================================================================
# FORMS
@@ -352,10 +363,26 @@ def DefaultPermissionsForm(perms_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):
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
@@ -96,8 +96,3 @@ class PermissionModel(object):
self.sa.rollback()
@@ -51,6 +51,17 @@ class SettingsModel(object):
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\
@@ -68,6 +68,36 @@ 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)
return True
def create_registration(self, form_data):
Status change: