##// END OF EJS Templates
implements #60, ldap configuration and authentication....
marcink -
r705:9e9f1b91 beta
parent child Browse files
Show More
@@ -79,6 +79,8 b' def make_map(config):'
79 79
80 80 #ADMIN PERMISSIONS REST ROUTES
81 81 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
82 map.connect('permissions_ldap', '/_admin/permissions_ldap', controller='admin/permissions', action='ldap')
83
82 84
83 85 #ADMIN SETTINGS REST ROUTES
84 86 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
@@ -29,9 +29,11 b' from pylons.controllers.util import abor'
29 29 from pylons.i18n.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth_ldap import LdapImportError
32 33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.model.forms import UserForm, DefaultPermissionsForm
34 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
34 35 from rhodecode.model.permission import PermissionModel
36 from rhodecode.model.settings import SettingsModel
35 37 from rhodecode.model.user import UserModel
36 38 import formencode
37 39 import logging
@@ -99,17 +101,19 b' class PermissionsController(BaseControll'
99 101 form_result = _form.to_python(dict(request.POST))
100 102 form_result.update({'perm_user_name':id})
101 103 permission_model.update(form_result)
102 h.flash(_('Default permissions updated succesfully'),
104 h.flash(_('Default permissions updated successfully'),
103 105 category='success')
104 106
105 107 except formencode.Invalid, errors:
106 108 c.perms_choices = self.perms_choices
107 109 c.register_choices = self.register_choices
108 110 c.create_choices = self.create_choices
111 defaults = errors.value
112 defaults.update(SettingsModel().get_ldap_settings())
109 113
110 114 return htmlfill.render(
111 115 render('admin/permissions/permissions.html'),
112 defaults=errors.value,
116 defaults=defaults,
113 117 errors=errors.error_dict or {},
114 118 prefix_error=False,
115 119 encoding="UTF-8")
@@ -146,6 +150,7 b' class PermissionsController(BaseControll'
146 150 default_user = UserModel().get_by_username('default')
147 151 defaults = {'_method':'put',
148 152 'anonymous':default_user.active}
153 defaults.update(SettingsModel().get_ldap_settings())
149 154 for p in default_user.user_perms:
150 155 if p.permission.permission_name.startswith('repository.'):
151 156 defaults['default_perm'] = p.permission.permission_name
@@ -163,3 +168,50 b' class PermissionsController(BaseControll'
163 168 force_defaults=True,)
164 169 else:
165 170 return redirect(url('admin_home'))
171
172
173 def ldap(self, id_user='default'):
174 """
175 POST ldap create and store ldap settings
176 """
177
178 settings_model = SettingsModel()
179 _form = LdapSettingsForm()()
180
181 try:
182 form_result = _form.to_python(dict(request.POST))
183 try:
184
185 for k, v in form_result.items():
186 if k.startswith('ldap_'):
187 setting = settings_model.get(k)
188 setting.app_settings_value = v
189 self.sa.add(setting)
190
191 self.sa.commit()
192 h.flash(_('Ldap settings updated successfully'),
193 category='success')
194 except:
195 raise
196 except LdapImportError:
197 h.flash(_('Unable to activate ldap. The "ldap-python" library '
198 'is missing.'),
199 category='warning')
200
201 except formencode.Invalid, errors:
202 c.perms_choices = self.perms_choices
203 c.register_choices = self.register_choices
204 c.create_choices = self.create_choices
205
206 return htmlfill.render(
207 render('admin/permissions/permissions.html'),
208 defaults=errors.value,
209 errors=errors.error_dict or {},
210 prefix_error=False,
211 encoding="UTF-8")
212 except Exception:
213 log.error(traceback.format_exc())
214 h.flash(_('error occured during update of ldap settings'),
215 category='error')
216
217 return redirect(url('edit_permission', id=id_user))
@@ -31,14 +31,15 b' from rhodecode.lib import helpers as h'
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 32 HasPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.celerylib import tasks, run_task
34 35 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 36 set_rhodecode_config, get_hg_settings, get_hg_ui_settings
36 from rhodecode.model.db import RhodeCodeSettings, RhodeCodeUi, Repository
37 from rhodecode.model.db import RhodeCodeUi, Repository
37 38 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 39 ApplicationUiSettingsForm
39 40 from rhodecode.model.scm import ScmModel
41 from rhodecode.model.settings import SettingsModel
40 42 from rhodecode.model.user import UserModel
41 from rhodecode.lib.celerylib import tasks, run_task
42 43 from sqlalchemy import func
43 44 import formencode
44 45 import logging
@@ -118,18 +119,12 b' class SettingsController(BaseController)'
118 119 application_form = ApplicationSettingsForm()()
119 120 try:
120 121 form_result = application_form.to_python(dict(request.POST))
121
122 settings_model = SettingsModel()
122 123 try:
123 hgsettings1 = self.sa.query(RhodeCodeSettings)\
124 .filter(RhodeCodeSettings.app_settings_name \
125 == 'title').one()
126
124 hgsettings1 = settings_model.get('title')
127 125 hgsettings1.app_settings_value = form_result['rhodecode_title']
128 126
129 hgsettings2 = self.sa.query(RhodeCodeSettings)\
130 .filter(RhodeCodeSettings.app_settings_name \
131 == 'realm').one()
132
127 hgsettings2 = settings_model('realm')
133 128 hgsettings2.app_settings_value = form_result['rhodecode_realm']
134 129
135 130
@@ -25,6 +25,7 b' Created on April 4, 2010'
25 25 from pylons import config, session, url, request
26 26 from pylons.controllers.util import abort, redirect
27 27 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.auth_ldap import AuthLdap, UsernameError, PasswordError
28 29 from rhodecode.model import meta
29 30 from rhodecode.model.user import UserModel
30 31 from rhodecode.model.caching_query import FromCache
@@ -34,6 +35,7 b' import bcrypt'
34 35 from decorator import decorator
35 36 import logging
36 37 import random
38 import traceback
37 39
38 40 log = logging.getLogger(__name__)
39 41
@@ -74,17 +76,18 b' def check_password(password, hashed):'
74 76
75 77 def authfunc(environ, username, password):
76 78 """
77 Authentication function used in Mercurial/Git/ and access controll,
79 Authentication function used in Mercurial/Git/ and access control,
78 80 firstly checks for db authentication then if ldap is enabled for ldap
79 authentication
81 authentication, also creates ldap user if not in database
82
80 83 :param environ: needed only for using in Basic auth, can be None
81 84 :param username: username
82 85 :param password: password
83 86 """
87 user_model = UserModel()
88 user = user_model.get_by_username(username, cache=False)
84 89
85 user = UserModel().get_by_username(username, cache=False)
86
87 if user:
90 if user is not None and user.is_ldap is False:
88 91 if user.active:
89 92
90 93 if user.username == 'default' and user.active:
@@ -97,6 +100,40 b' def authfunc(environ, username, password'
97 100 else:
98 101 log.error('user %s is disabled', username)
99 102
103
104 else:
105 from rhodecode.model.settings import SettingsModel
106 ldap_settings = SettingsModel().get_ldap_settings()
107
108 #======================================================================
109 # FALLBACK TO LDAP AUTH IN ENABLE
110 #======================================================================
111 if ldap_settings.get('ldap_active', False):
112 kwargs = {
113 'server':ldap_settings.get('ldap_host', ''),
114 'base_dn':ldap_settings.get('ldap_base_dn', ''),
115 'port':ldap_settings.get('ldap_port'),
116 'bind_dn':ldap_settings.get('ldap_dn_user'),
117 'bind_pass':ldap_settings.get('ldap_dn_pass'),
118 'use_ldaps':ldap_settings.get('ldap_ldaps'),
119 'ldap_version':3,
120 }
121 log.debug('Checking for ldap authentication')
122 try:
123 aldap = AuthLdap(**kwargs)
124 res = aldap.authenticate_ldap(username, password)
125
126 authenticated = res[1]['uid'][0] == username
127
128 if authenticated and user_model.create_ldap(username, password):
129 log.info('created new ldap user')
130
131 return authenticated
132 except (UsernameError, PasswordError):
133 return False
134 except:
135 log.error(traceback.format_exc())
136 return False
100 137 return False
101 138
102 139 class AuthUser(object):
@@ -1,7 +1,3 b''
1 import logging
2 logging.basicConfig(level=logging.DEBUG)
3 log = logging.getLogger('ldap')
4
5 1 #==============================================================================
6 2 # LDAP
7 3 #Name = Just a description for the auth modes page
@@ -11,76 +7,87 b" log = logging.getLogger('ldap')"
11 7 #Account = DepartmentName\UserName (or UserName@MyDomain depending on AD server)
12 8 #Password = <password>
13 9 #Base DN = DC=DepartmentName,DC=OrganizationName,DC=local
14 #
15 #On-the-fly user creation = yes
16 #Attributes
17 # Login = sAMAccountName
18 # Firstname = givenName
19 # Lastname = sN
20 # Email = mail
21 10
22 11 #==============================================================================
23 class UsernameError(Exception):pass
24 class PasswordError(Exception):pass
12
13 from rhodecode.lib.exceptions import LdapImportError, UsernameError, \
14 PasswordError, ConnectionError
15 import logging
16
17 log = logging.getLogger(__name__)
25 18
26 LDAP_USE_LDAPS = False
27 ldap_server_type = 'ldap'
28 LDAP_SERVER_ADDRESS = 'myldap.com'
29 LDAP_SERVER_PORT = '389'
19 try:
20 import ldap
21 except ImportError:
22 pass
30 23
31 #USE FOR READ ONLY BIND TO LDAP SERVER
32 LDAP_BIND_DN = ''
33 LDAP_BIND_PASS = ''
24 class AuthLdap(object):
34 25
35 if LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
36 LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
37 LDAP_SERVER_ADDRESS,
38 LDAP_SERVER_PORT)
39
40 BASE_DN = "ou=people,dc=server,dc=com"
41 AUTH_DN = "uid=%s,%s"
26 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
27 use_ldaps=False, ldap_version=3):
28 self.ldap_version = ldap_version
29 if use_ldaps:
30 port = port or 689
31 self.LDAP_USE_LDAPS = use_ldaps
32 self.LDAP_SERVER_ADDRESS = server
33 self.LDAP_SERVER_PORT = port
42 34
43 def authenticate_ldap(username, password):
44 """Authenticate a user via LDAP and return his/her LDAP properties.
35 #USE FOR READ ONLY BIND TO LDAP SERVER
36 self.LDAP_BIND_DN = bind_dn
37 self.LDAP_BIND_PASS = bind_pass
45 38
46 Raises AuthenticationError if the credentials are rejected, or
47 EnvironmentError if the LDAP server can't be reached.
48 """
49 try:
50 import ldap
51 except ImportError:
52 raise Exception('Could not import ldap make sure You install python-ldap')
39 ldap_server_type = 'ldap'
40 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
41 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
42 self.LDAP_SERVER_ADDRESS,
43 self.LDAP_SERVER_PORT)
44
45 self.BASE_DN = base_dn
46 self.AUTH_DN = "uid=%s,%s"
53 47
54 from rhodecode.lib.helpers import chop_at
55
56 uid = chop_at(username, "@%s" % LDAP_SERVER_ADDRESS)
57 dn = AUTH_DN % (uid, BASE_DN)
58 log.debug("Authenticating %r at %s", dn, LDAP_SERVER)
59 if "," in username:
60 raise UsernameError("invalid character in username: ,")
61 try:
62 #ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
63 server = ldap.initialize(LDAP_SERVER)
64 server.protocol = ldap.VERSION3
65
66 if LDAP_BIND_DN and LDAP_BIND_PASS:
67 server.simple_bind_s(AUTH_DN % (LDAP_BIND_DN,
68 LDAP_BIND_PASS),
69 password)
48 def authenticate_ldap(self, username, password):
49 """Authenticate a user via LDAP and return his/her LDAP properties.
50
51 Raises AuthenticationError if the credentials are rejected, or
52 EnvironmentError if the LDAP server can't be reached.
70 53
71 server.simple_bind_s(dn, password)
72 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
73 if not properties:
74 raise ldap.NO_SUCH_OBJECT()
75 except ldap.NO_SUCH_OBJECT, e:
76 log.debug("LDAP says no such user '%s' (%s)", uid, username)
77 raise UsernameError()
78 except ldap.INVALID_CREDENTIALS, e:
79 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
80 raise PasswordError()
81 except ldap.SERVER_DOWN, e:
82 raise EnvironmentError("can't access authentication server")
83 return properties
54 :param username: username
55 :param password: password
56 """
57
58 from rhodecode.lib.helpers import chop_at
59
60 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
61 dn = self.AUTH_DN % (uid, self.BASE_DN)
62 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
63 if "," in username:
64 raise UsernameError("invalid character in username: ,")
65 try:
66 ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
67 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
68 server = ldap.initialize(self.LDAP_SERVER)
69 if self.ldap_version == 2:
70 server.protocol = ldap.VERSION2
71 else:
72 server.protocol = ldap.VERSION3
84 73
74 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
75 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
76 self.BASE_DN),
77 self.LDAP_BIND_PASS)
85 78
86 print authenticate_ldap('test', 'test')
79 server.simple_bind_s(dn, password)
80 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
81 if not properties:
82 raise ldap.NO_SUCH_OBJECT()
83 except ldap.NO_SUCH_OBJECT, e:
84 log.debug("LDAP says no such user '%s' (%s)", uid, username)
85 raise UsernameError()
86 except ldap.INVALID_CREDENTIALS, e:
87 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
88 raise PasswordError()
89 except ldap.SERVER_DOWN, e:
90 raise ConnectionError("LDAP can't access authentication server")
91
92 return properties[0]
93
@@ -25,6 +25,7 b' from formencode.validators import Unicod'
25 25 from pylons import session
26 26 from pylons.i18n.translation import _
27 27 from rhodecode.lib.auth import authfunc, get_crypt_password
28 from rhodecode.lib.exceptions import LdapImportError
28 29 from rhodecode.model import meta
29 30 from rhodecode.model.user import UserModel
30 31 from rhodecode.model.repo import RepoModel
@@ -82,7 +83,7 b' class ValidAuth(formencode.validators.Fa'
82 83 messages = {
83 84 'invalid_password':_('invalid password'),
84 85 'invalid_login':_('invalid user name'),
85 'disabled_account':_('Your acccount is disabled')
86 'disabled_account':_('Your account is disabled')
86 87
87 88 }
88 89 #error mapping
@@ -236,6 +237,16 b' class ValidSystemEmail(formencode.valida'
236 237
237 238 return value
238 239
240 class LdapLibValidator(formencode.validators.FancyValidator):
241
242 def to_python(self, value, state):
243
244 try:
245 import ldap
246 except ImportError:
247 raise LdapImportError
248 return value
249
239 250 #===============================================================================
240 251 # FORMS
241 252 #===============================================================================
@@ -352,10 +363,26 b' def DefaultPermissionsForm(perms_choices'
352 363 class _DefaultPermissionsForm(formencode.Schema):
353 364 allow_extra_fields = True
354 365 filter_extra_fields = True
355 overwrite_default = OneOf(['true', 'false'], if_missing='false')
366 overwrite_default = StringBoolean(if_missing=False)
356 367 anonymous = OneOf(['True', 'False'], if_missing=False)
357 368 default_perm = OneOf(perms_choices)
358 369 default_register = OneOf(register_choices)
359 370 default_create = OneOf(create_choices)
360 371
361 372 return _DefaultPermissionsForm
373
374
375 def LdapSettingsForm():
376 class _LdapSettingsForm(formencode.Schema):
377 allow_extra_fields = True
378 filter_extra_fields = True
379 pre_validators = [LdapLibValidator]
380 ldap_active = StringBoolean(if_missing=False)
381 ldap_host = UnicodeString(strip=True,)
382 ldap_port = Number(strip=True,)
383 ldap_ldaps = StringBoolean(if_missing=False)
384 ldap_dn_user = UnicodeString(strip=True,)
385 ldap_dn_pass = UnicodeString(strip=True,)
386 ldap_base_dn = UnicodeString(strip=True,)
387
388 return _LdapSettingsForm
@@ -96,8 +96,3 b' class PermissionModel(object):'
96 96 log.error(traceback.format_exc())
97 97 self.sa.rollback()
98 98 raise
99
100
101
102
103
@@ -51,6 +51,17 b' class SettingsModel(object):'
51 51
52 52
53 53 def get_ldap_settings(self):
54 """
55 Returns ldap settings from database
56 :returns:
57 ldap_active
58 ldap_host
59 ldap_port
60 ldap_ldaps
61 ldap_dn_user
62 ldap_dn_pass
63 ldap_base_dn
64 """
54 65
55 66 r = self.sa.query(RhodeCodeSettings)\
56 67 .filter(RhodeCodeSettings.app_settings_name\
@@ -68,6 +68,36 b' class UserModel(object):'
68 68 self.sa.rollback()
69 69 raise
70 70
71 def create_ldap(self, username, password):
72 """
73 Checks if user is in database, if not creates this user marked
74 as ldap user
75 :param username:
76 :param password:
77 """
78
79 if self.get_by_username(username) is None:
80 try:
81 new_user = User()
82 new_user.username = username
83 new_user.password = password
84 new_user.email = '%s@ldap.server' % username
85 new_user.active = True
86 new_user.is_ldap = True
87 new_user.name = '%s@ldap' % username
88 new_user.lastname = ''
89
90
91 self.sa.add(new_user)
92 self.sa.commit()
93 return True
94 except:
95 log.error(traceback.format_exc())
96 self.sa.rollback()
97 raise
98
99 return False
100
71 101 def create_registration(self, form_data):
72 102 from rhodecode.lib.celerylib import tasks, run_task
73 103 try:
General Comments 0
You need to be logged in to leave comments. Login now