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