##// END OF EJS Templates
added some fixes to LDAP form re-submition, new simples ldap-settings getter....
marcink -
r1292:c0335c1d beta
parent child Browse files
Show More
@@ -143,14 +143,14 b' Setting up LDAP support'
143 -----------------------
143 -----------------------
144
144
145 RhodeCode starting from version 1.1 supports ldap authentication. In order
145 RhodeCode starting from version 1.1 supports ldap authentication. In order
146 to use LDAP, you have to install the python-ldap_ package. This package is available
146 to use LDAP, you have to install the python-ldap_ package. This package is
147 via pypi, so you can install it by running
147 available via pypi, so you can install it by running
148
148
149 ::
149 using easy_install::
150
150
151 easy_install python-ldap
151 easy_install python-ldap
152
152
153 ::
153 using pip::
154
154
155 pip install python-ldap
155 pip install python-ldap
156
156
@@ -168,7 +168,7 b" Here's a typical ldap setup::"
168 Port = 389
168 Port = 389
169 Account = <account>
169 Account = <account>
170 Password = <password>
170 Password = <password>
171 Enable LDAPS = checked
171 Connection Security = LDAPS connection
172 Certificate Checks = DEMAND
172 Certificate Checks = DEMAND
173
173
174 Search settings
174 Search settings
@@ -212,11 +212,19 b' Password : optional'
212
212
213 .. _Enable LDAPS:
213 .. _Enable LDAPS:
214
214
215 Enable LDAPS : optional
215 Connection Security : required
216 Check this if SSL encryption is necessary for communication with the
216 Defines the connection to LDAP server
217 LDAP server - it will likely require `Port`_ to be set to a different
217
218 value (standard LDAPS port is 636). When LDAPS is enabled then
218 No encryption
219 `Certificate Checks`_ is required.
219 Plain non encrypted connection
220
221 LDAPS connection
222 Enable ldaps connection. It will likely require `Port`_ to be set to
223 a different value (standard LDAPS port is 636). When LDAPS is enabled
224 then `Certificate Checks`_ is required.
225
226 START_TLS on LDAP connection
227 START TLS connection
220
228
221 .. _Certificate Checks:
229 .. _Certificate Checks:
222
230
@@ -32,13 +32,14 b' from pylons import request, response, se'
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from sqlalchemy.exc import DatabaseError
36
35 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.auth_ldap import LdapImportError
40 from rhodecode.lib.exceptions import LdapImportError
39 from rhodecode.model.settings import SettingsModel
40 from rhodecode.model.forms import LdapSettingsForm
41 from rhodecode.model.forms import LdapSettingsForm
41 from sqlalchemy.exc import DatabaseError
42 from rhodecode.model.db import RhodeCodeSettings
42
43
43 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
44
45
@@ -74,10 +75,15 b' class LdapSettingsController(BaseControl'
74 c.search_scope_choices = self.search_scope_choices
75 c.search_scope_choices = self.search_scope_choices
75 c.tls_reqcert_choices = self.tls_reqcert_choices
76 c.tls_reqcert_choices = self.tls_reqcert_choices
76 c.tls_kind_choices = self.tls_kind_choices
77 c.tls_kind_choices = self.tls_kind_choices
78
79 c.search_scope_cur = self.search_scope_default
80 c.tls_reqcert_cur = self.tls_reqcert_default
81 c.tls_kind_cur = self.tls_kind_default
82
77 super(LdapSettingsController, self).__before__()
83 super(LdapSettingsController, self).__before__()
78
84
79 def index(self):
85 def index(self):
80 defaults = SettingsModel().get_ldap_settings()
86 defaults = RhodeCodeSettings.get_ldap_settings()
81 c.search_scope_cur = defaults.get('ldap_search_scope')
87 c.search_scope_cur = defaults.get('ldap_search_scope')
82 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
88 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
83 c.tls_kind_cur = defaults.get('ldap_tls_kind')
89 c.tls_kind_cur = defaults.get('ldap_tls_kind')
@@ -91,7 +97,6 b' class LdapSettingsController(BaseControl'
91 def ldap_settings(self):
97 def ldap_settings(self):
92 """POST ldap create and store ldap settings"""
98 """POST ldap create and store ldap settings"""
93
99
94 settings_model = SettingsModel()
95 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
100 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
96 [x[0] for x in self.search_scope_choices],
101 [x[0] for x in self.search_scope_choices],
97 [x[0] for x in self.tls_kind_choices])()
102 [x[0] for x in self.tls_kind_choices])()
@@ -102,7 +107,7 b' class LdapSettingsController(BaseControl'
102
107
103 for k, v in form_result.items():
108 for k, v in form_result.items():
104 if k.startswith('ldap_'):
109 if k.startswith('ldap_'):
105 setting = settings_model.get(k)
110 setting = RhodeCodeSettings.get_by_name(k)
106 setting.app_settings_value = v
111 setting.app_settings_value = v
107 self.sa.add(setting)
112 self.sa.add(setting)
108
113
@@ -116,14 +121,13 b' class LdapSettingsController(BaseControl'
116 'is missing.'), category='warning')
121 'is missing.'), category='warning')
117
122
118 except formencode.Invalid, errors:
123 except formencode.Invalid, errors:
124 e = errors.error_dict or {}
119
125
120 c.search_scope_cur = self.search_scope_default
121 c.tls_reqcert_cur = self.search_scope_default
122
126
123 return htmlfill.render(
127 return htmlfill.render(
124 render('admin/ldap/ldap.html'),
128 render('admin/ldap/ldap.html'),
125 defaults=errors.value,
129 defaults=errors.value,
126 errors=errors.error_dict or {},
130 errors=e,
127 prefix_error=False,
131 prefix_error=False,
128 encoding="UTF-8")
132 encoding="UTF-8")
129 except Exception:
133 except Exception:
@@ -40,11 +40,11 b' from rhodecode.lib.base import BaseContr'
40 from rhodecode.lib.celerylib import tasks, run_task
40 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
42 set_rhodecode_config, repo_name_slug
42 set_rhodecode_config, repo_name_slug
43 from rhodecode.model.db import RhodeCodeUi, Repository, Group
43 from rhodecode.model.db import RhodeCodeUi, Repository, Group, \
44 RhodeCodeSettings
44 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
45 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
45 ApplicationUiSettingsForm
46 ApplicationUiSettingsForm
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.settings import SettingsModel
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
@@ -68,7 +68,7 b' class SettingsController(BaseController)'
68 """GET /admin/settings: All items in the collection"""
68 """GET /admin/settings: All items in the collection"""
69 # url('admin_settings')
69 # url('admin_settings')
70
70
71 defaults = SettingsModel().get_app_settings()
71 defaults = RhodeCodeSettings.get_app_settings()
72 defaults.update(self.get_hg_ui_settings())
72 defaults.update(self.get_hg_ui_settings())
73 return htmlfill.render(
73 return htmlfill.render(
74 render('admin/settings/settings.html'),
74 render('admin/settings/settings.html'),
@@ -121,18 +121,17 b' class SettingsController(BaseController)'
121 application_form = ApplicationSettingsForm()()
121 application_form = ApplicationSettingsForm()()
122 try:
122 try:
123 form_result = application_form.to_python(dict(request.POST))
123 form_result = application_form.to_python(dict(request.POST))
124 settings_model = SettingsModel()
125
124
126 try:
125 try:
127 hgsettings1 = settings_model.get('title')
126 hgsettings1 = RhodeCodeSettings.get_by_name('title')
128 hgsettings1.app_settings_value = \
127 hgsettings1.app_settings_value = \
129 form_result['rhodecode_title']
128 form_result['rhodecode_title']
130
129
131 hgsettings2 = settings_model.get('realm')
130 hgsettings2 = RhodeCodeSettings.get_by_name('realm')
132 hgsettings2.app_settings_value = \
131 hgsettings2.app_settings_value = \
133 form_result['rhodecode_realm']
132 form_result['rhodecode_realm']
134
133
135 hgsettings3 = settings_model.get('ga_code')
134 hgsettings3 = RhodeCodeSettings.get_by_name('ga_code')
136 hgsettings3.app_settings_value = \
135 hgsettings3.app_settings_value = \
137 form_result['rhodecode_ga_code']
136 form_result['rhodecode_ga_code']
138
137
@@ -48,7 +48,7 b' from rhodecode.lib.auth_ldap import Auth'
48
48
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission
51 from rhodecode.model.db import Permission, RhodeCodeSettings
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
@@ -149,6 +149,7 b' def authenticate(username, password):'
149 :param username: username
149 :param username: username
150 :param password: password
150 :param password: password
151 """
151 """
152
152 user_model = UserModel()
153 user_model = UserModel()
153 user = user_model.get_by_username(username, cache=False)
154 user = user_model.get_by_username(username, cache=False)
154
155
@@ -176,9 +177,7 b' def authenticate(username, password):'
176 log.debug('this user already exists as non ldap')
177 log.debug('this user already exists as non ldap')
177 return False
178 return False
178
179
179 from rhodecode.model.settings import SettingsModel
180 ldap_settings = RhodeCodeSettings.get_ldap_settings()
180 ldap_settings = SettingsModel().get_ldap_settings()
181
182 #======================================================================
181 #======================================================================
183 # FALLBACK TO LDAP AUTH IF ENABLE
182 # FALLBACK TO LDAP AUTH IF ENABLE
184 #======================================================================
183 #======================================================================
@@ -204,13 +203,13 b' def authenticate(username, password):'
204 password)
203 password)
205 log.debug('Got ldap DN response %s', user_dn)
204 log.debug('Got ldap DN response %s', user_dn)
206
205
206 get_ldap_attr = lambda k:ldap_attrs.get(ldap_settings\
207 .get(k), [''])[0]
208
207 user_attrs = {
209 user_attrs = {
208 'name': ldap_attrs.get(ldap_settings\
210 'name': get_ldap_attr('ldap_attr_firstname'),
209 .get('ldap_attr_firstname'), [''])[0],
211 'lastname': get_ldap_attr('ldap_attr_lastname'),
210 'lastname': ldap_attrs.get(ldap_settings\
212 'email': get_ldap_attr('ldap_attr_email'),
211 .get('ldap_attr_lastname'),[''])[0],
212 'email': ldap_attrs.get(ldap_settings\
213 .get('ldap_attr_email'), [''])[0],
214 }
213 }
215
214
216 if user_model.create_ldap(username, password, user_dn,
215 if user_model.create_ldap(username, password, user_dn,
@@ -1,8 +1,15 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # ldap authentication lib
3 rhodecode.controllers.changelog
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 RhodeCode authentication library for LDAP
7
8 :created_on: Created on Nov 17, 2010
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
@@ -15,26 +22,26 b''
15 #
22 #
16 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """
19 Created on Nov 17, 2010
20
25
21 @author: marcink
22 """
23
24 from rhodecode.lib.exceptions import *
25 import logging
26 import logging
26
27
28 from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
29 LdapPasswordError
30
27 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
28
32
33
29 try:
34 try:
30 import ldap
35 import ldap
31 except ImportError:
36 except ImportError:
37 # means that python-ldap is not installed
32 pass
38 pass
33
39
40
34 class AuthLdap(object):
41 class AuthLdap(object):
35
42
36 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
43 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
37 tls_kind = 'PLAIN', tls_reqcert='DEMAND', ldap_version=3,
44 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
38 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
45 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
39 search_scope='SUBTREE',
46 search_scope='SUBTREE',
40 attr_login='uid'):
47 attr_login='uid'):
@@ -64,7 +71,6 b' class AuthLdap(object):'
64 self.SEARCH_SCOPE = ldap.__dict__['SCOPE_' + search_scope]
71 self.SEARCH_SCOPE = ldap.__dict__['SCOPE_' + search_scope]
65 self.attr_login = attr_login
72 self.attr_login = attr_login
66
73
67
68 def authenticate_ldap(self, username, password):
74 def authenticate_ldap(self, username, password):
69 """Authenticate a user via LDAP and return his/her LDAP properties.
75 """Authenticate a user via LDAP and return his/her LDAP properties.
70
76
@@ -102,7 +108,8 b' class AuthLdap(object):'
102 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
108 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
103 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
109 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
104
110
105 filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login, username)
111 filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login,
112 username)
106 log.debug("Authenticating %r filt %s at %s", self.BASE_DN,
113 log.debug("Authenticating %r filt %s at %s", self.BASE_DN,
107 filt, self.LDAP_SERVER)
114 filt, self.LDAP_SERVER)
108 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
115 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
@@ -114,7 +121,8 b' class AuthLdap(object):'
114 for (dn, _attrs) in lobjects:
121 for (dn, _attrs) in lobjects:
115 try:
122 try:
116 server.simple_bind_s(dn, password)
123 server.simple_bind_s(dn, password)
117 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE, '(objectClass=*)')[0][1]
124 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
125 '(objectClass=*)')[0][1]
118 break
126 break
119
127
120 except ldap.INVALID_CREDENTIALS, e:
128 except ldap.INVALID_CREDENTIALS, e:
@@ -130,6 +138,7 b' class AuthLdap(object):'
130 log.debug("LDAP says no such user '%s' (%s)", uid, username)
138 log.debug("LDAP says no such user '%s' (%s)", uid, username)
131 raise LdapUsernameError()
139 raise LdapUsernameError()
132 except ldap.SERVER_DOWN, e:
140 except ldap.SERVER_DOWN, e:
133 raise LdapConnectionError("LDAP can't access authentication server")
141 raise LdapConnectionError("LDAP can't access "
142 "authentication server")
134
143
135 return (dn, attrs)
144 return (dn, attrs)
@@ -44,7 +44,8 b' from vcs.utils.lazy import LazyProperty'
44
44
45 from rhodecode.model import meta
45 from rhodecode.model import meta
46 from rhodecode.model.caching_query import FromCache
46 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 RhodeCodeSettings
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
50
51
@@ -287,8 +288,7 b' def set_rhodecode_config(config):'
287
288
288 :param config:
289 :param config:
289 """
290 """
290 from rhodecode.model.settings import SettingsModel
291 hgsettings = RhodeCodeSettings.get_app_settings()
291 hgsettings = SettingsModel().get_app_settings()
292
292
293 for k, v in hgsettings.items():
293 for k, v in hgsettings.items():
294 config[k] = v
294 config[k] = v
@@ -65,6 +65,11 b' class RhodeCodeSettings(Base):'
65
65
66
66
67 @classmethod
67 @classmethod
68 def get_by_name(cls, ldap_key):
69 return Session.query(cls)\
70 .filter(cls.app_settings_name == ldap_key).scalar()
71
72 @classmethod
68 def get_app_settings(cls, cache=False):
73 def get_app_settings(cls, cache=False):
69
74
70 ret = Session.query(cls)
75 ret = Session.query(cls)
@@ -88,7 +93,7 b' class RhodeCodeSettings(Base):'
88 .all()
93 .all()
89 fd = {}
94 fd = {}
90 for row in ret:
95 for row in ret:
91 fd.update({row.app_settings_name:str2bool(row.app_settings_value)})
96 fd.update({row.app_settings_name:row.app_settings_value})
92 return fd
97 return fd
93
98
94
99
@@ -3,5 +3,19 b' from rhodecode.tests import *'
3 class TestLdapSettingsController(TestController):
3 class TestLdapSettingsController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 response = self.app.get(url(controller='admin/ldap_settings', action='index'))
6 self.log_user()
7 response = self.app.get(url(controller='admin/ldap_settings',
8 action='index'))
7 # Test response...
9 # Test response...
10
11 def test_ldap_save_settings(self):
12 pass
13
14 def test_ldap_error_form(self):
15 pass
16
17 def test_ldap_login(self):
18 pass
19
20 def test_ldap_login_incorrect(self):
21 pass
@@ -1,7 +1,6 b''
1 from rhodecode.lib.auth import get_crypt_password, check_password
1 from rhodecode.lib.auth import get_crypt_password, check_password
2 from rhodecode.model.db import User
2 from rhodecode.model.db import User, RhodeCodeSettings
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4 from rhodecode.model.settings import SettingsModel
5
4
6 class TestAdminSettingsController(TestController):
5 class TestAdminSettingsController(TestController):
7
6
@@ -60,7 +59,7 b' class TestAdminSettingsController(TestCo'
60 ))
59 ))
61
60
62 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
61 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
63 assert SettingsModel(self.sa).get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
62 assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
64
63
65 response = response.follow()
64 response = response.follow()
66 assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code in response.body
65 assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code in response.body
@@ -79,7 +78,7 b' class TestAdminSettingsController(TestCo'
79 ))
78 ))
80
79
81 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
80 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
82 assert SettingsModel(self.sa).get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
81 assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
83
82
84 response = response.follow()
83 response = response.follow()
85 assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code not in response.body
84 assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code not in response.body
@@ -100,7 +99,7 b' class TestAdminSettingsController(TestCo'
100
99
101
100
102 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
101 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
103 assert SettingsModel(self.sa).get_app_settings()['rhodecode_title'] == new_title, 'change not in database'
102 assert RhodeCodeSettings.get_app_settings()['rhodecode_title'] == new_title, 'change not in database'
104
103
105 response = response.follow()
104 response = response.follow()
106 assert """<h1><a href="/">%s</a></h1>""" % new_title in response.body
105 assert """<h1><a href="/">%s</a></h1>""" % new_title in response.body
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now