Changeset - e80b2cbbd4ba
[Not reviewed]
Merge beta
0 12 0
Marcin Kuzminski - 15 years ago 2011-02-04 12:29:45
marcin@python-works.com
merge thayne ldap extensions
12 files changed with 324 insertions and 77 deletions:
0 comments (0 inline, 0 general)
docs/setup.rst
Show inline comments
 
@@ -118,72 +118,214 @@ When using incremental (default) mode wh
 
of each file and add it to reindex if newer file is available. Also indexing 
 
daemon checks for removed files and removes them from index. 
 

	
 
Sometime You might want to rebuild index from scratch. You can do that using 
 
the `-f` flag passed to paster command or, in admin panel You can check 
 
`build from scratch` flag.
 

	
 

	
 
Setting up LDAP support
 
-----------------------
 

	
 
RhodeCode starting from version 1.1 supports ldap authentication. In order
 
to use ldap, You have to install python-ldap package. This package is available
 
to use LDAP, You have to install python-ldap_ package. This package is available
 
via pypi, so You can install it by running
 

	
 
::
 

	
 
 easy_install python-ldap
 
 
 
::
 

	
 
 pip install python-ldap
 

	
 
.. note::
 
   python-ldap requires some certain libs on Your system, so before installing 
 
   it check that You have at least `openldap`, and `sasl` libraries.
 

	
 
ldap settings are located in admin->ldap section,
 
LDAP settings are located in admin->ldap section,
 

	
 
This is a typical LDAP setup::
 

	
 
 Connection settings
 
 Enable LDAP          = checked
 
 Host                 = host.example.org
 
 Port                 = 389
 
 Account              = <account>
 
 Password             = <password>
 
 Enable LDAPS         = checked
 
 Certificate Checks   = DEMAND
 

	
 
 Search settings
 
 Base DN              = CN=users,DC=host,DC=example,DC=org
 
 LDAP Filter          = (&(objectClass=user)(!(objectClass=computer)))
 
 LDAP Search Scope    = SUBTREE
 

	
 
Here's a typical ldap setup::
 
 Attribute mappings
 
 Login Attribute      = uid
 
 First Name Attribute = firstName
 
 Last Name Attribute  = lastName
 
 E-mail Attribute     = mail
 

	
 
.. _enable_ldap:
 

	
 
Enable LDAP : required
 
    Whether to use LDAP for authenticating users.
 

	
 
.. _ldap_host:
 

	
 
Host : required
 
    LDAP server hostname or IP address.
 

	
 
.. _Port:
 

	
 
Port : required
 
    389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
 

	
 
.. _ldap_account:
 

	
 
 Enable ldap  = checked                 #controls if ldap access is enabled
 
 Host         = host.domain.org         #actual ldap server to connect
 
 Port         = 389 or 689 for ldaps    #ldap server ports
 
 Enable LDAPS = unchecked               #enable disable ldaps
 
 Account      = <account>               #access for ldap server(if required)
 
 Password     = <password>              #password for ldap server(if required)
 
 Base DN      = uid=%(user)s,CN=users,DC=host,DC=domain,DC=org
 
 
 
Account : optional
 
    Only required if the LDAP server does not allow anonymous browsing of
 
    records.  This should be a special account for record browsing.  This
 
    will require `LDAP Password`_ below.
 

	
 
.. _LDAP Password:
 

	
 
Password : optional
 
    Only required if the LDAP server does not allow anonymous browsing of
 
    records.
 

	
 
.. _Enable LDAPS:
 

	
 
Enable LDAPS : optional
 
    Check this if SSL encryption is necessary for communication with the
 
    LDAP server - it will likely require `Port`_ to be set to a different
 
    value (standard LDAPS port is 636).  When LDAPS is enabled then
 
    `Certificate Checks`_ is required.
 

	
 
.. _Certificate Checks:
 

	
 
`Account` and `Password` are optional, and used for two-phase ldap 
 
authentication so those are credentials to access Your ldap, if it doesn't 
 
support anonymous search/user lookups. 
 
Certificate Checks : optional
 
    How SSL certificates verification is handled - this is only useful when
 
    `Enable LDAPS`_ is enabled.  Only DEMAND or HARD offer full SSL security while
 
    the other options are susceptible to man-in-the-middle attacks.  SSL
 
    certificates can be installed to /etc/openldap/cacerts so that the
 
    DEMAND or HARD options can be used with self-signed certificates or
 
    certificates that do not have traceable certificates of authority.
 

	
 
    NEVER
 
        A serve certificate will never be requested or checked.
 

	
 
    ALLOW
 
        A server certificate is requested.  Failure to provide a
 
        certificate or providing a bad certificate will not terminate the
 
        session.
 

	
 
    TRY
 
        A server certificate is requested.  Failure to provide a
 
        certificate does not halt the session; providing a bad certificate
 
        halts the session.
 

	
 
    DEMAND
 
        A server certificate is requested and must be provided and
 
        authenticated for the session to proceed.
 

	
 
Base DN must have %(user)s template inside, it's a placer where Your uid used
 
to login would go, it allows admins to specify not standard schema for uid 
 
variable
 
    HARD
 
        The same as DEMAND.
 

	
 
.. _Base DN:
 

	
 
Base DN : required
 
    The Distinguished Name (DN) where searches for users will be performed.
 
    Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
 

	
 
.. _LDAP Filter:
 

	
 
LDAP Filter : optional
 
    A LDAP filter defined by RFC 2254.  This is more useful when `LDAP
 
    Search Scope`_ is set to SUBTREE.  The filter is useful for limiting
 
    which LDAP objects are identified as representing Users for
 
    authentication.  The filter is augmented by `Login Attribute`_ below.
 
    This can commonly be left blank.
 

	
 
.. _LDAP Search Scope:
 

	
 
LDAP Search Scope : required
 
    This limits how far LDAP will search for a matching object.
 

	
 
    BASE
 
        Only allows searching of `Base DN`_ and is usually not what you
 
        want.
 

	
 
    ONELEVEL
 
        Searches all entries under `Base DN`_, but not Base DN itself.
 

	
 
    SUBTREE
 
        Searches all entries below `Base DN`_, but not Base DN itself.
 
        When using SUBTREE `LDAP Filter`_ is useful to limit object
 
        location.
 

	
 
.. _Login Attribute:
 

	
 
If all data are entered correctly, and `python-ldap` is properly installed
 
Users should be granted to access RhodeCode wit ldap accounts. When 
 
logging at the first time an special ldap account is created inside RhodeCode, 
 
so You can control over permissions even on ldap users. If such user exists 
 
already in RhodeCode database ldap user with the same username would be not 
 
able to access RhodeCode.
 
Login Attribute : required        
 
    The LDAP record attribute that will be matched as the USERNAME or
 
    ACCOUNT used to connect to RhodeCode.  This will be added to `LDAP
 
    Filter`_ for locating the User object.  If `LDAP Filter`_ is specified as
 
    "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
 
    connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
 
    ::
 

	
 
        (&(LDAPFILTER)(uid=jsmith))
 

	
 
.. _ldap_attr_firstname:
 

	
 
First Name Attribute : required
 
    The LDAP record attribute which represents the user's first name.
 

	
 
.. _ldap_attr_lastname:
 

	
 
Last Name Attribute : required
 
    The LDAP record attribute which represents the user's last name.
 

	
 
.. _ldap_attr_email:
 

	
 
Email Attribute : required
 
    The LDAP record attribute which represents the user's email address.
 

	
 
If You have problems with ldap access and believe You entered correct 
 
information check out the RhodeCode logs,any error messages sent from 
 
ldap will be saved there.
 
If all data are entered correctly, and python-ldap_ is properly installed
 
users should be granted access to RhodeCode with ldap accounts.  At this
 
time user information is copied from LDAP into the RhodeCode user database.
 
This means that updates of an LDAP user object may not be reflected as a
 
user update in RhodeCode.
 

	
 
If You have problems with LDAP access and believe You entered correct
 
information check out the RhodeCode logs, any error messages sent from LDAP
 
will be saved there.
 

	
 
Active Directory
 
''''''''''''''''
 

	
 
RhodeCode can use Microsoft Active Directory for user authentication.  This
 
is done through an LDAP or LDAPS connection to Active Directory.  The
 
following LDAP configuration settings are typical for using Active
 
Directory ::
 

	
 
 Base DN              = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
 
 Login Attribute      = sAMAccountName
 
 First Name Attribute = givenName
 
 Last Name Attribute  = sn
 
 E-mail Attribute     = mail
 

	
 
All other LDAP settings will likely be site-specific and should be
 
appropriately configured.
 

	
 
Setting Up Celery
 
-----------------
 

	
 
Since version 1.1 celery is configured by the rhodecode ini configuration files
 
simply set use_celery=true in the ini file then add / change the configuration 
 
variables inside the ini file.
 

	
 
Remember that the ini files uses format with '.' not with '_' like celery
 
so for example setting `BROKER_HOST` in celery means setting `broker.host` in
 
the config file.
 

	
 
@@ -317,13 +459,14 @@ Troubleshooting
 
   are caused by https server and not RhodeCode
 

	
 
- large pushes timeouts ?
 
 
 
 - make sure You set a proper max_body_size for the http server
 

	
 

	
 

	
 
.. _virtualenv: http://pypi.python.org/pypi/virtualenv
 
.. _python: http://www.python.org/
 
.. _mercurial: http://mercurial.selenic.com/
 
.. _celery: http://celeryproject.org/
 
.. _rabbitmq: http://www.rabbitmq.com/
 
\ No newline at end of file
 
.. _rabbitmq: http://www.rabbitmq.com/
 
.. _python-ldap: http://www.python-ldap.org/
rhodecode/controllers/admin/ldap_settings.py
Show inline comments
 
@@ -39,67 +39,89 @@ from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 
from rhodecode.lib.auth_ldap import LdapImportError
 
from rhodecode.model.settings import SettingsModel
 
from rhodecode.model.forms import LdapSettingsForm
 
from sqlalchemy.exc import DatabaseError
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 

	
 
class LdapSettingsController(BaseController):
 

	
 
    search_scope_choices = [('BASE',     _('BASE'),),
 
                            ('ONELEVEL', _('ONELEVEL'),),
 
                            ('SUBTREE',  _('SUBTREE'),),
 
                            ]
 
    search_scope_default = 'SUBTREE'
 

	
 
    tls_reqcert_choices = [('NEVER',  _('NEVER'),),
 
                           ('ALLOW',  _('ALLOW'),),
 
                           ('TRY',    _('TRY'),),
 
                           ('DEMAND', _('DEMAND'),),
 
                           ('HARD',   _('HARD'),),
 
                           ]
 
    tls_reqcert_default = 'DEMAND'
 

	
 
    @LoginRequired()
 
    @HasPermissionAllDecorator('hg.admin')
 
    def __before__(self):
 
        c.admin_user = session.get('admin_user')
 
        c.admin_username = session.get('admin_username')
 
        c.search_scope_choices = self.search_scope_choices
 
        c.tls_reqcert_choices  = self.tls_reqcert_choices
 
        super(LdapSettingsController, self).__before__()
 

	
 
    def index(self):
 
        defaults = SettingsModel().get_ldap_settings()
 
        c.search_scope_cur = defaults.get('ldap_search_scope')
 
        c.tls_reqcert_cur  = defaults.get('ldap_tls_reqcert')
 

	
 
        return htmlfill.render(
 
                    render('admin/ldap/ldap.html'),
 
                    defaults=defaults,
 
                    encoding="UTF-8",
 
                    force_defaults=True,)
 

	
 
    def ldap_settings(self):
 
        """POST ldap create and store ldap settings"""
 

	
 
        settings_model = SettingsModel()
 
        _form = LdapSettingsForm()()
 
        _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
 
                                 [x[0] for x in self.search_scope_choices])()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            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'),
 
                    category='success')
 
            except (DatabaseError,):
 
                raise
 
        except LdapImportError:
 
            h.flash(_('Unable to activate ldap. The "python-ldap" library '
 
                      'is missing.'), category='warning')
 

	
 
        except formencode.Invalid, errors:
 

	
 
            c.search_scope_cur = self.search_scope_default
 
            c.tls_reqcert_cur  = self.search_scope_default
 

	
 
            return htmlfill.render(
 
                render('admin/ldap/ldap.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of ldap settings'),
 
                    category='error')
 

	
 
        return redirect(url('ldap_home'))
rhodecode/lib/auth.py
Show inline comments
 
@@ -94,71 +94,81 @@ def authenticate(username, password):
 
    """
 
    Authentication function used for access control,
 
    firstly checks for db authentication then if ldap is enabled for ldap
 
    authentication, also creates ldap user if not in database
 
    
 
    :param username: username
 
    :param password: password
 
    """
 
    user_model = UserModel()
 
    user = user_model.get_by_username(username, cache=False)
 

	
 
    log.debug('Authenticating user using RhodeCode account')
 
    if user is not None and user.is_ldap is False:
 
    if user is not None and not user.ldap_dn:
 
        if user.active:
 

	
 
            if user.username == 'default' and user.active:
 
                log.info('user %s authenticated correctly as anonymous user',
 
                         username)
 
                return True
 

	
 
            elif user.username == username and check_password(password, user.password):
 
                log.info('user %s authenticated correctly', username)
 
                return True
 
        else:
 
            log.warning('user %s is disabled', username)
 

	
 
    else:
 
        log.debug('Regular authentication failed')
 
        user_obj = user_model.get_by_username(username, cache=False,
 
                                            case_insensitive=True)
 

	
 
        if user_obj is not None and user_obj.is_ldap is False:
 
        if user_obj is not None and not user_obj.ldap_dn:
 
            log.debug('this user already exists as non ldap')
 
            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):
 
            log.debug("Authenticating user using ldap")
 
            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'),
 
                  'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
 
                  'ldap_filter':ldap_settings.get('ldap_filter'),
 
                  'search_scope':ldap_settings.get('ldap_search_scope'),
 
                  'attr_login':ldap_settings.get('ldap_attr_login'),
 
                  'ldap_version':3,
 
                  }
 
            log.debug('Checking for ldap authentication')
 
            try:
 
                aldap = AuthLdap(**kwargs)
 
                res = aldap.authenticate_ldap(username, password)
 
                log.debug('Got ldap response %s', res)
 
                (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
 
                log.debug('Got ldap DN response %s', user_dn)
 

	
 
                if user_model.create_ldap(username, password):
 
                user_attrs = {
 
                    'name'     : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
 
                    'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
 
                    'email'    : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
 
                    }
 

	
 
                if user_model.create_ldap(username, password, user_dn, user_attrs):
 
                    log.info('created new ldap user')
 

	
 
                return True
 
            except (LdapUsernameError, LdapPasswordError,):
 
                pass
 
            except (Exception,):
 
                log.error(traceback.format_exc())
 
                pass
 
    return False
 

	
 
class  AuthUser(object):
 
    """
rhodecode/lib/auth_ldap.py
Show inline comments
 
@@ -27,78 +27,100 @@ from rhodecode.lib.exceptions import *
 
import logging
 

	
 
log = logging.getLogger(__name__)
 

	
 
try:
 
    import ldap
 
except ImportError:
 
    pass
 

	
 
class AuthLdap(object):
 

	
 
    def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
 
                 use_ldaps=False, ldap_version=3):
 
                 use_ldaps=False, tls_reqcert='DEMAND', ldap_version=3,
 
                 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
 
                 search_scope='SUBTREE',
 
                 attr_login='uid'):
 
        self.ldap_version = ldap_version
 
        if use_ldaps:
 
            port = port or 689
 
        self.LDAP_USE_LDAPS = use_ldaps
 
        self.TLS_REQCERT = ldap.__dict__['OPT_X_TLS_' + tls_reqcert]
 
        self.LDAP_SERVER_ADDRESS = server
 
        self.LDAP_SERVER_PORT = port
 

	
 
        #USE FOR READ ONLY BIND TO LDAP SERVER
 
        self.LDAP_BIND_DN = bind_dn
 
        self.LDAP_BIND_PASS = bind_pass
 

	
 
        ldap_server_type = '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.LDAP_FILTER = ldap_filter
 
        self.SEARCH_SCOPE = ldap.__dict__['SCOPE_' + search_scope]
 
        self.attr_login = attr_login
 

	
 

	
 
    def authenticate_ldap(self, username, password):
 
        """Authenticate a user via LDAP and return his/her LDAP properties.
 
    
 
        Raises AuthenticationError if the credentials are rejected, or
 
        EnvironmentError if the LDAP server can't be reached.
 
        
 
        :param username: username
 
        :param password: password
 
        """
 

	
 
        from rhodecode.lib.helpers import chop_at
 

	
 
        uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
 

	
 
        if "," in username:
 
            raise LdapUsernameError("invalid character in username: ,")
 
        try:
 
            ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
 
            ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
 
            ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
 
            ldap.set_option(ldap.OPT_TIMEOUT, 20)
 
            ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
 
            ldap.set_option(ldap.OPT_TIMELIMIT, 15)
 
            if self.LDAP_USE_LDAPS:
 
                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
 
            server = ldap.initialize(self.LDAP_SERVER)
 
            if self.ldap_version == 2:
 
                server.protocol = ldap.VERSION2
 
            else:
 
                server.protocol = ldap.VERSION3
 

	
 
            if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
 
                server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
 

	
 
            dn = self.BASE_DN % {'user':uid}
 
            log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
 
            server.simple_bind_s(dn, password)
 
            filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login, username)
 
            log.debug("Authenticating %r filt %s at %s", self.BASE_DN, filt, self.LDAP_SERVER)
 
            lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE, filt)
 

	
 
            if not lobjects:
 
                raise ldap.NO_SUCH_OBJECT()
 

	
 
            properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
 
            if not properties:
 
                raise ldap.NO_SUCH_OBJECT()
 
            for (dn, attrs) in lobjects:
 
                try:
 
                    server.simple_bind_s(dn, password)
 
                    break
 

	
 
                except ldap.INVALID_CREDENTIALS, e:
 
                    log.debug("LDAP rejected password for user '%s' (%s): %s", uid, username, dn)
 

	
 
                else:
 
                    log.debug("No matching LDAP objecs for authentication of '%s' (%s)", uid, username)
 
                    raise LdapPasswordError()
 

	
 
        except ldap.NO_SUCH_OBJECT, e:
 
            log.debug("LDAP says no such user '%s' (%s)", uid, username)
 
            raise LdapUsernameError()
 
        except ldap.INVALID_CREDENTIALS, e:
 
            log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
 
            raise LdapPasswordError()
 
        except ldap.SERVER_DOWN, e:
 
            raise LdapConnectionError("LDAP can't access authentication server")
 

	
 
        return properties[0]
 

	
 
        return (dn, attrs)
rhodecode/lib/db_manage.py
Show inline comments
 
@@ -327,25 +327,28 @@ class DbManage(object):
 
            self.sa.add(dotencode_disable)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 

	
 
    def create_ldap_options(self):
 
        """Creates ldap settings"""
 

	
 
        try:
 
            for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
 
                      'ldap_dn_user', 'ldap_dn_pass', 'ldap_base_dn']:
 
                      'ldap_tls_reqcert', 'ldap_dn_user', 'ldap_dn_pass',
 
                      'ldap_base_dn', 'ldap_filter', 'ldap_search_scope',
 
                      'ldap_attr_login', 'ldap_attr_firstname', 'ldap_attr_lastname',
 
                      'ldap_attr_email']:
 

	
 
                setting = RhodeCodeSettings(k, '')
 
                self.sa.add(setting)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 
    def config_prompt(self, test_repo_path=''):
 
        log.info('Setting up repositories config')
 

	
 
        if not self.tests and not test_repo_path:
rhodecode/model/db.py
Show inline comments
 
@@ -96,25 +96,25 @@ class RhodeCodeUi(Base, BaseModel):
 
class User(Base, BaseModel):
 
    __tablename__ = 'users'
 
    __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
 
    user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    active = Column("active", Boolean(), nullable=True, unique=None, default=None)
 
    admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
 
    name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
 
    is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
 
    ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    user_log = relationship('UserLog', cascade='all')
 
    user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
 

	
 
    repositories = relationship('Repository')
 
    user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
 

	
 
    @property
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.name, self.lastname, self.email)
 

	
 

	
rhodecode/model/forms.py
Show inline comments
 
@@ -325,41 +325,32 @@ class ValidSystemEmail(formencode.valida
 
        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):
 
class AttrLoginValidator(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)
 
        if not value or not isinstance(value, (str, unicode)):
 
            raise formencode.Invalid(_("The LDAP Login attribute of the CN must be specified "
 
                                       "- this is the name of the attribute that is equivalent to 'username'"),
 
                                     value, state)
 

	
 
        return value
 

	
 
#===============================================================================
 
# FORMS        
 
#===============================================================================
 
class LoginForm(formencode.Schema):
 
    allow_extra_fields = True
 
    filter_extra_fields = True
 
    username = UnicodeString(
 
                             strip=True,
 
                             min=1,
 
@@ -512,26 +503,33 @@ def DefaultPermissionsForm(perms_choices
 
    class _DefaultPermissionsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        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():
 
def LdapSettingsForm(tls_reqcert_choices, search_scope_choices):
 
    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_tls_reqcert = OneOf(tls_reqcert_choices)
 
        ldap_dn_user = UnicodeString(strip=True,)
 
        ldap_dn_pass = UnicodeString(strip=True,)
 
        ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
 
        ldap_base_dn = UnicodeString(strip=True,)
 
        ldap_filter = UnicodeString(strip=True,)
 
        ldap_search_scope = OneOf(search_scope_choices)
 
        ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
 
        ldap_attr_firstname = UnicodeString(strip=True,)
 
        ldap_attr_lastname = UnicodeString(strip=True,)
 
        ldap_attr_email = UnicodeString(strip=True,)
 

	
 
    return _LdapSettingsForm
rhodecode/model/settings.py
Show inline comments
 
@@ -63,28 +63,36 @@ class SettingsModel(BaseModel):
 
            settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
 

	
 
        return settings
 

	
 
    def get_ldap_settings(self):
 
        """
 
        Returns ldap settings from database
 
        :returns:
 
        ldap_active
 
        ldap_host
 
        ldap_port 
 
        ldap_ldaps
 
        ldap_tls_reqcert
 
        ldap_dn_user 
 
        ldap_dn_pass 
 
        ldap_base_dn
 
        ldap_filter
 
        ldap_search_scope
 
        ldap_attr_login
 
        ldap_attr_firstname
 
        ldap_attr_lastname
 
        ldap_attr_email
 
        """
 
        # ldap_search_scope
 

	
 
        r = self.sa.query(RhodeCodeSettings)\
 
                .filter(RhodeCodeSettings.app_settings_name\
 
                        .startswith('ldap_'))\
 
                .all()
 

	
 
        fd = {}
 

	
 
        for row in r:
 
            v = row.app_settings_value
 
            if v in ['0', '1']:
 
                v = v == '1'
rhodecode/model/user.py
Show inline comments
 
@@ -66,43 +66,45 @@ class UserModel(BaseModel):
 
        try:
 
            new_user = User()
 
            for k, v in form_data.items():
 
                setattr(new_user, k, v)
 

	
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 

	
 
    def create_ldap(self, username, password):
 
    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 username:
 
        :param password:
 
        :param user_dn:
 
        :param attrs:
 
        """
 
        from rhodecode.lib.auth import get_crypt_password
 
        log.debug('Checking for such ldap account in RhodeCode database')
 
        if self.get_by_username(username, case_insensitive=True) is None:
 
            try:
 
                new_user = User()
 
                new_user.username = username.lower()#add ldap account always lowercase
 
                new_user.username = username.lower() # add ldap account always lowercase
 
                new_user.password = get_crypt_password(password)
 
                new_user.email = '%s@ldap.server' % username
 
                new_user.email = attrs['email']
 
                new_user.active = True
 
                new_user.is_ldap = True
 
                new_user.name = '%s@ldap' % username
 
                new_user.lastname = ''
 
                new_user.ldap_dn = user_dn
 
                new_user.name = attrs['name']
 
                new_user.lastname = attrs['lastname']
 

	
 

	
 
                self.sa.add(new_user)
 
                self.sa.commit()
 
                return True
 
            except (DatabaseError,):
 
                log.error(traceback.format_exc())
 
                self.sa.rollback()
 
                raise
 
        log.debug('this %s user exists skipping creation of ldap account',
 
                  username)
 
        return False
rhodecode/templates/admin/ldap/ldap.html
Show inline comments
 
@@ -12,57 +12,87 @@
 
</%def>
 

	
 
<%def name="page_nav()">
 
    ${self.menu('admin')}
 
</%def>
 

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}       
 
    </div>
 
    <h3>${_('LDAP administration')}</h3>
 
    ${h.form(url('ldap_settings'))}
 
    <div class="form">
 
        <div class="fields">
 

	
 
	  <h3>${_('Connection settings')}</h3>
 
            <div class="field">
 
                <div class="label label-checkbox"><label for="ldap_active">${_('Enable ldap')}</label></div>
 
                <div class="label label-checkbox"><label for="ldap_active">${_('Enable LDAP')}</label></div>
 
                <div class="checkboxes"><div class="checkbox">${h.checkbox('ldap_active',True,class_='small')}</div></div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_host">${_('Host')}</label></div>
 
                <div class="input">${h.text('ldap_host',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_port">${_('Port')}</label></div>
 
                <div class="input">${h.text('ldap_port',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label label-checkbox"><label for="ldap_ldaps">${_('Enable LDAPS')}</label></div>
 
                <div class="checkboxes"><div class="checkbox">${h.checkbox('ldap_ldaps',True,class_='small')}</div></div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_dn_user">${_('Account')}</label></div>
 
                <div class="input">${h.text('ldap_dn_user',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_dn_pass">${_('Password')}</label></div>
 
                <div class="input">${h.password('ldap_dn_pass',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label label-checkbox"><label for="ldap_ldaps">${_('Enable LDAPS')}</label></div>
 
                <div class="checkboxes"><div class="checkbox">${h.checkbox('ldap_ldaps',True,class_='small')}</div></div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_tls_reqcert">${_('Certificate Checks')}</label></div>
 
                <div class="select">${h.select('ldap_tls_reqcert',c.tls_reqcert_cur,c.tls_reqcert_choices,class_='small')}</div>
 
            </div>
 
	  <h3>${_('Search settings')}</h3>
 
            <div class="field">
 
                <div class="label"><label for="ldap_base_dn">${_('Base DN')}</label></div>
 
                <div class="input">${h.text('ldap_base_dn',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_filter">${_('LDAP Filter')}</label></div>
 
                <div class="input">${h.text('ldap_filter',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_search_scope">${_('LDAP Search Scope')}</label></div>
 
                <div class="select">${h.select('ldap_search_scope',c.search_scope_cur,c.search_scope_choices,class_='small')}</div>
 
            </div>
 
	  <h3>${_('Attribute mappings')}</h3>
 
            <div class="field">
 
                <div class="label"><label for="ldap_attr_login">${_('Login Attribute')}</label></div>
 
                <div class="input">${h.text('ldap_attr_login',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_attr_firstname">${_('First Name Attribute')}</label></div>
 
                <div class="input">${h.text('ldap_attr_firstname',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_attr_lastname">${_('Last Name Attribute')}</label></div>
 
                <div class="input">${h.text('ldap_attr_lastname',class_='small')}</div>
 
            </div>
 
            <div class="field">
 
                <div class="label"><label for="ldap_attr_email">${_('E-mail Attribute')}</label></div>
 
                <div class="input">${h.text('ldap_attr_email',class_='small')}</div>
 
            </div>
 
            
 
            <div class="buttons">
 
            ${h.submit('save','Save',class_="ui-button")}
 
            </div>              
 
        </div>
 
    </div>     
 
    ${h.end_form()}    
 
</div>
 
</%def>    
 

	
 

	
 

	
rhodecode/templates/admin/users/user_edit.html
Show inline comments
 
@@ -40,24 +40,33 @@
 
        
 
             <div class="field">
 
                <div class="label">
 
                    <label for="username">${_('Username')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('username',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="ldap_dn">${_('LDAP DN')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('ldap_dn',class_='small')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="new_password">${_('New password')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.password('new_password',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="name">${_('First Name')}:</label>
 
                </div>
 
                <div class="input">
 
@@ -222,13 +231,13 @@
 
    
 
            E.addListener(['add_element','remove_element'],'click',prompts_action_callback)
 

	
 
            E.addListener('map_form','submit',function(){
 
                var choosen = D.get('id_granted_permissions');
 
                for (var i = 0; i < choosen.options.length; i++) {
 
                    choosen.options[i].selected = 'selected';
 
                }
 
            })  
 
        });
 
</script>    
 
</div>
 
</%def>  
 
\ No newline at end of file
 
</%def>
rhodecode/templates/admin/users/users.html
Show inline comments
 
@@ -40,25 +40,25 @@
 
            <th class="left">${_('action')}</th>
 
        </tr>
 
            %for cnt,user in enumerate(c.users_list):
 
             %if user.name !='default':
 
                <tr class="parity${cnt%2}">
 
                	<td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div></td>
 
                    <td>${h.link_to(user.username,h.url('edit_user', id=user.user_id))}</td>
 
                    <td>${user.name}</td>
 
                    <td>${user.lastname}</td>
 
                    <td>${user.last_login}</td>
 
                    <td>${h.bool2icon(user.active)}</td>
 
                    <td>${h.bool2icon(user.admin)}</td>
 
                    <td>${h.bool2icon(user.is_ldap)}</td>
 
                    <td>${h.bool2icon(bool(user.ldap_dn))}</td>
 
                    <td>
 
                        ${h.form(url('user', id=user.user_id),method='delete')}
 
                            ${h.submit('remove_','delete',id="remove_user_%s" % user.user_id,
 
                            class_="delete_icon action_button",onclick="return confirm('Confirm to delete this user');")}
 
                        ${h.end_form()}
 
                    </td>
 
                </tr>
 
             %endif
 
            %endfor
 
        </table>
 
    </div>
 
</div>
0 comments (0 inline, 0 general)