changeset 1292:c0335c1dee36 beta

added some fixes to LDAP form re-submition, new simples ldap-settings getter. Updated docs for new ldap fixes. Removed depracated settings model, in exchange for db model classmethods.
author Marcin Kuzminski <marcin@python-works.com>
date Wed, 27 Apr 2011 01:19:17 +0200
parents 34a9b64a5e00
children ae5b07e75ffd
files docs/setup.rst rhodecode/controllers/admin/ldap_settings.py rhodecode/controllers/admin/settings.py rhodecode/lib/auth.py rhodecode/lib/auth_ldap.py rhodecode/lib/utils.py rhodecode/model/db.py rhodecode/model/settings.py rhodecode/tests/functional/test_admin_ldap_settings.py rhodecode/tests/functional/test_admin_settings.py
diffstat 10 files changed, 99 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/docs/setup.rst	Tue Apr 26 20:28:46 2011 +0200
+++ b/docs/setup.rst	Wed Apr 27 01:19:17 2011 +0200
@@ -143,14 +143,14 @@
 -----------------------
 
 RhodeCode starting from version 1.1 supports ldap authentication. In order
-to use LDAP, you have to install the python-ldap_ package. This package is available
-via pypi, so you can install it by running
+to use LDAP, you have to install the python-ldap_ package. This package is 
+available via pypi, so you can install it by running
 
-::
+using easy_install::
 
     easy_install python-ldap
  
-::
+using pip::
 
     pip install python-ldap
 
@@ -168,7 +168,7 @@
  Port                 = 389
  Account              = <account>
  Password             = <password>
- Enable LDAPS         = checked
+ Connection Security  = LDAPS connection
  Certificate Checks   = DEMAND
 
  Search settings
@@ -212,11 +212,19 @@
 
 .. _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.
+Connection Security : required
+    Defines the connection to LDAP server
+
+    No encryption
+        Plain non encrypted connection
+        
+    LDAPS connection
+        Enable ldaps connection. 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.
+        
+    START_TLS on LDAP connection
+        START TLS connection
 
 .. _Certificate Checks:
 
--- a/rhodecode/controllers/admin/ldap_settings.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/controllers/admin/ldap_settings.py	Wed Apr 27 01:19:17 2011 +0200
@@ -32,13 +32,14 @@
 from pylons.controllers.util import abort, redirect
 from pylons.i18n.translation import _
 
+from sqlalchemy.exc import DatabaseError
+
 from rhodecode.lib.base import BaseController, render
 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.lib.exceptions import LdapImportError
 from rhodecode.model.forms import LdapSettingsForm
-from sqlalchemy.exc import DatabaseError
+from rhodecode.model.db import RhodeCodeSettings
 
 log = logging.getLogger(__name__)
 
@@ -74,10 +75,15 @@
         c.search_scope_choices = self.search_scope_choices
         c.tls_reqcert_choices = self.tls_reqcert_choices
         c.tls_kind_choices = self.tls_kind_choices
+
+        c.search_scope_cur = self.search_scope_default
+        c.tls_reqcert_cur = self.tls_reqcert_default
+        c.tls_kind_cur = self.tls_kind_default
+
         super(LdapSettingsController, self).__before__()
 
     def index(self):
-        defaults = SettingsModel().get_ldap_settings()
+        defaults = RhodeCodeSettings.get_ldap_settings()
         c.search_scope_cur = defaults.get('ldap_search_scope')
         c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
         c.tls_kind_cur = defaults.get('ldap_tls_kind')
@@ -91,7 +97,6 @@
     def ldap_settings(self):
         """POST ldap create and store ldap settings"""
 
-        settings_model = SettingsModel()
         _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
                                  [x[0] for x in self.search_scope_choices],
                                  [x[0] for x in self.tls_kind_choices])()
@@ -102,7 +107,7 @@
 
                 for k, v in form_result.items():
                     if k.startswith('ldap_'):
-                        setting = settings_model.get(k)
+                        setting = RhodeCodeSettings.get_by_name(k)
                         setting.app_settings_value = v
                         self.sa.add(setting)
 
@@ -116,14 +121,13 @@
                       'is missing.'), category='warning')
 
         except formencode.Invalid, errors:
+            e = errors.error_dict or {}
 
-            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 {},
+                errors=e,
                 prefix_error=False,
                 encoding="UTF-8")
         except Exception:
--- a/rhodecode/controllers/admin/settings.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/controllers/admin/settings.py	Wed Apr 27 01:19:17 2011 +0200
@@ -40,11 +40,11 @@
 from rhodecode.lib.celerylib import tasks, run_task
 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
     set_rhodecode_config, repo_name_slug
-from rhodecode.model.db import RhodeCodeUi, Repository, Group
+from rhodecode.model.db import RhodeCodeUi, Repository, Group, \
+    RhodeCodeSettings
 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
     ApplicationUiSettingsForm
 from rhodecode.model.scm import ScmModel
-from rhodecode.model.settings import SettingsModel
 from rhodecode.model.user import UserModel
 
 log = logging.getLogger(__name__)
@@ -68,7 +68,7 @@
         """GET /admin/settings: All items in the collection"""
         # url('admin_settings')
 
-        defaults = SettingsModel().get_app_settings()
+        defaults = RhodeCodeSettings.get_app_settings()
         defaults.update(self.get_hg_ui_settings())
         return htmlfill.render(
             render('admin/settings/settings.html'),
@@ -121,18 +121,17 @@
             application_form = ApplicationSettingsForm()()
             try:
                 form_result = application_form.to_python(dict(request.POST))
-                settings_model = SettingsModel()
 
                 try:
-                    hgsettings1 = settings_model.get('title')
+                    hgsettings1 = RhodeCodeSettings.get_by_name('title')
                     hgsettings1.app_settings_value = \
                         form_result['rhodecode_title']
 
-                    hgsettings2 = settings_model.get('realm')
+                    hgsettings2 = RhodeCodeSettings.get_by_name('realm')
                     hgsettings2.app_settings_value = \
                         form_result['rhodecode_realm']
 
-                    hgsettings3 = settings_model.get('ga_code')
+                    hgsettings3 = RhodeCodeSettings.get_by_name('ga_code')
                     hgsettings3.app_settings_value = \
                         form_result['rhodecode_ga_code']
 
--- a/rhodecode/lib/auth.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/lib/auth.py	Wed Apr 27 01:19:17 2011 +0200
@@ -48,7 +48,7 @@
 
 from rhodecode.model import meta
 from rhodecode.model.user import UserModel
-from rhodecode.model.db import Permission
+from rhodecode.model.db import Permission, RhodeCodeSettings
 
 log = logging.getLogger(__name__)
 
@@ -149,6 +149,7 @@
     :param username: username
     :param password: password
     """
+
     user_model = UserModel()
     user = user_model.get_by_username(username, cache=False)
 
@@ -176,9 +177,7 @@
             log.debug('this user already exists as non ldap')
             return False
 
-        from rhodecode.model.settings import SettingsModel
-        ldap_settings = SettingsModel().get_ldap_settings()
-
+        ldap_settings = RhodeCodeSettings.get_ldap_settings()
         #======================================================================
         # FALLBACK TO LDAP AUTH IF ENABLE
         #======================================================================
@@ -204,13 +203,13 @@
                                                                 password)
                 log.debug('Got ldap DN response %s', user_dn)
 
+                get_ldap_attr = lambda k:ldap_attrs.get(ldap_settings\
+                                                           .get(k), [''])[0]
+
                 user_attrs = {
-                    'name': ldap_attrs.get(ldap_settings\
-                                       .get('ldap_attr_firstname'), [''])[0],
-                    'lastname': ldap_attrs.get(ldap_settings\
-                                           .get('ldap_attr_lastname'),[''])[0],
-                    'email': ldap_attrs.get(ldap_settings\
-                                        .get('ldap_attr_email'), [''])[0],
+                    'name': get_ldap_attr('ldap_attr_firstname'),
+                    'lastname': get_ldap_attr('ldap_attr_lastname'),
+                    'email': get_ldap_attr('ldap_attr_email'),
                     }
 
                 if user_model.create_ldap(username, password, user_dn,
--- a/rhodecode/lib/auth_ldap.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/lib/auth_ldap.py	Wed Apr 27 01:19:17 2011 +0200
@@ -1,8 +1,15 @@
-#!/usr/bin/env python
-# encoding: utf-8
-# ldap authentication lib
-# Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
-#
+# -*- coding: utf-8 -*-
+"""
+    rhodecode.controllers.changelog
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    RhodeCode authentication library for LDAP
+
+    :created_on: Created on Nov 17, 2010
+    :author: marcink
+    :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
+    :license: GPLv3, see COPYING for more details.
+"""
 # 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, either version 3 of the License, or
@@ -15,26 +22,26 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-"""
-Created on Nov 17, 2010
 
-@author: marcink
-"""
-
-from rhodecode.lib.exceptions import *
 import logging
 
+from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
+    LdapPasswordError
+
 log = logging.getLogger(__name__)
 
+
 try:
     import ldap
 except ImportError:
+    # means that python-ldap is not installed
     pass
 
+
 class AuthLdap(object):
 
     def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
-                 tls_kind = 'PLAIN', tls_reqcert='DEMAND', ldap_version=3,
+                 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
                  ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
                  search_scope='SUBTREE',
                  attr_login='uid'):
@@ -64,7 +71,6 @@
         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.
 
@@ -102,7 +108,8 @@
             if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
                 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
 
-            filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login, username)
+            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,
@@ -114,7 +121,8 @@
             for (dn, _attrs) in lobjects:
                 try:
                     server.simple_bind_s(dn, password)
-                    attrs = server.search_ext_s(dn, ldap.SCOPE_BASE, '(objectClass=*)')[0][1]
+                    attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
+                                                '(objectClass=*)')[0][1]
                     break
 
                 except ldap.INVALID_CREDENTIALS, e:
@@ -130,6 +138,7 @@
             log.debug("LDAP says no such user '%s' (%s)", uid, username)
             raise LdapUsernameError()
         except ldap.SERVER_DOWN, e:
-            raise LdapConnectionError("LDAP can't access authentication server")
+            raise LdapConnectionError("LDAP can't access "
+                                      "authentication server")
 
         return (dn, attrs)
--- a/rhodecode/lib/utils.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/lib/utils.py	Wed Apr 27 01:19:17 2011 +0200
@@ -44,7 +44,8 @@
 
 from rhodecode.model import meta
 from rhodecode.model.caching_query import FromCache
-from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
+from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
+    RhodeCodeSettings
 from rhodecode.model.repo import RepoModel
 from rhodecode.model.user import UserModel
 
@@ -287,8 +288,7 @@
 
     :param config:
     """
-    from rhodecode.model.settings import SettingsModel
-    hgsettings = SettingsModel().get_app_settings()
+    hgsettings = RhodeCodeSettings.get_app_settings()
 
     for k, v in hgsettings.items():
         config[k] = v
--- a/rhodecode/model/db.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/model/db.py	Wed Apr 27 01:19:17 2011 +0200
@@ -65,6 +65,11 @@
 
 
     @classmethod
+    def get_by_name(cls, ldap_key):
+        return Session.query(cls)\
+            .filter(cls.app_settings_name == ldap_key).scalar()
+
+    @classmethod
     def get_app_settings(cls, cache=False):
 
         ret = Session.query(cls)
@@ -88,7 +93,7 @@
                 .all()
         fd = {}
         for row in ret:
-            fd.update({row.app_settings_name:str2bool(row.app_settings_value)})
+            fd.update({row.app_settings_name:row.app_settings_value})
         return fd
 
 
--- a/rhodecode/model/settings.py	Tue Apr 26 20:28:46 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    rhodecode.model.settings
-    ~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Settings model for RhodeCode
-
-    :created on Nov 17, 2010
-    :author: marcink
-    :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
-    :license: GPLv3, see COPYING for more details.
-"""
-# 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, either version 3 of the License, or
-# (at your option) any later version.
-#
-# 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, see <http://www.gnu.org/licenses/>.
-
-import logging
-
-from rhodecode.model import BaseModel
-from rhodecode.model.caching_query import FromCache
-from rhodecode.model.db import  RhodeCodeSettings
-
-log = logging.getLogger(__name__)
-
-class SettingsModel(BaseModel):
-    """
-    Settings model
-    """
-
-    def get(self, settings_key, cache=False):
-        r = self.sa.query(RhodeCodeSettings)\
-            .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
-        if cache:
-            r = r.options(FromCache("sql_cache_short",
-                                          "get_setting_%s" % settings_key))
-        return r
-
-    def get_app_settings(self, cache=False):
-        """Get's config from database, each config key is prefixed with
-        'rhodecode_' prefix, than global pylons config is updated with such
-        keys
-        """
-
-        ret = self.sa.query(RhodeCodeSettings)
-
-        if cache:
-            ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
-
-        if not ret:
-            raise Exception('Could not get application settings !')
-        settings = {}
-        for each in ret:
-            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_tls_kind
-        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 ['true', 'yes', 'on', 'y', 't', '1']:
-                v = True
-            elif v in ['false', 'no', 'off', 'n', 'f', '0']:
-                v = False
-
-            fd.update({row.app_settings_name:v})
-
-        return fd
--- a/rhodecode/tests/functional/test_admin_ldap_settings.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/tests/functional/test_admin_ldap_settings.py	Wed Apr 27 01:19:17 2011 +0200
@@ -3,5 +3,19 @@
 class TestLdapSettingsController(TestController):
 
     def test_index(self):
-        response = self.app.get(url(controller='admin/ldap_settings', action='index'))
+        self.log_user()
+        response = self.app.get(url(controller='admin/ldap_settings',
+                                    action='index'))
         # Test response...
+
+    def test_ldap_save_settings(self):
+        pass
+
+    def test_ldap_error_form(self):
+        pass
+
+    def test_ldap_login(self):
+        pass
+
+    def test_ldap_login_incorrect(self):
+        pass
--- a/rhodecode/tests/functional/test_admin_settings.py	Tue Apr 26 20:28:46 2011 +0200
+++ b/rhodecode/tests/functional/test_admin_settings.py	Wed Apr 27 01:19:17 2011 +0200
@@ -1,7 +1,6 @@
 from rhodecode.lib.auth import get_crypt_password, check_password
-from rhodecode.model.db import User
+from rhodecode.model.db import User, RhodeCodeSettings
 from rhodecode.tests import *
-from rhodecode.model.settings import SettingsModel
 
 class TestAdminSettingsController(TestController):
 
@@ -60,7 +59,7 @@
                                                  ))
 
         assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
-        assert SettingsModel(self.sa).get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
+        assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
 
         response = response.follow()
         assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code in response.body
@@ -79,7 +78,7 @@
                                                  ))
 
         assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
-        assert SettingsModel(self.sa).get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
+        assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
 
         response = response.follow()
         assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code not in response.body
@@ -100,7 +99,7 @@
 
 
         assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
-        assert SettingsModel(self.sa).get_app_settings()['rhodecode_title'] == new_title, 'change not in database'
+        assert RhodeCodeSettings.get_app_settings()['rhodecode_title'] == new_title, 'change not in database'
 
         response = response.follow()
         assert """<h1><a href="/">%s</a></h1>""" % new_title in response.body