##// END OF EJS Templates
ldap: fixed ldaps port example to correct number.
marcink -
r4296:bdb5f3c6 default
parent child Browse files
Show More
@@ -1,160 +1,160 b''
1 1 .. _config-ldap-groups-ref:
2 2
3 3 LDAP/AD With User Groups Sync
4 4 -----------------------------
5 5
6 6 **This plugin is available only in EE Edition.**
7 7
8 8 |RCE| supports LDAP (Lightweight Directory Access Protocol) or
9 9 AD (active Directory) authentication.
10 10 All LDAP versions are currently supported.
11 11
12 12 RhodeCode reads all data defined from plugin and creates corresponding
13 13 accounts on local database after receiving data from LDAP. This is done on
14 14 every user log-in including operations like pushing/pulling/checkout.
15 15 In addition group membership is read from LDAP and following operations are done:
16 16
17 17 - automatic addition of user to |RCE| user group
18 18 - automatic removal of user from any other |RCE| user groups not specified in LDAP.
19 19 The removal is done *only* on groups that are marked to be synced from ldap.
20 20 This setting can be changed in advanced settings on user groups
21 21 - automatic creation of user groups if they aren't yet existing in |RCE|
22 22 - marking user as super-admins if he is a member of any admin group defined in plugin settings
23 23
24 24
25 25 .. important::
26 26
27 27 The email used with your |RCE| super-admin account needs to match the email
28 28 address attached to your admin profile in LDAP. This is because
29 29 within |RCE| the user email needs to be unique, and multiple users
30 30 cannot share an email account.
31 31
32 32 Likewise, if as an admin you also have a user account, the email address
33 33 attached to the user account needs to be different.
34 34
35 35
36 36 LDAP Configuration Steps
37 37 ^^^^^^^^^^^^^^^^^^^^^^^^
38 38
39 39 To configure |LDAP|, use the following steps:
40 40
41 41 1. From the |RCE| interface, select
42 42 :menuselection:`Admin --> Authentication`
43 43 2. Activate the `LDAP + User Groups` plugin and select :guilabel:`Save`
44 44 3. Go to newly available menu option called `LDAP + User Groups` on the left side.
45 45 4. Check the `enabled` check box in the plugin configuration section,
46 46 and fill in the required LDAP information and :guilabel:`Save`, for more details,
47 47 see :ref:`config-ldap-groups-examples`
48 48
49 49 For a more detailed description of LDAP objects, see :ref:`ldap-gloss-ref`:
50 50
51 51 .. _config-ldap-groups-examples:
52 52
53 53 Example LDAP configuration
54 54 ^^^^^^^^^^^^^^^^^^^^^^^^^^
55 55
56 56 Below is example setup that can be used with Active Directory and LDAP server with groups sync::
57 57
58 58 *option*: `enabled` => `True`
59 59 # Enable or disable this authentication plugin.
60 60
61 61 *option*: `cache_ttl` => `360`
62 62 # Amount of seconds to cache the authentication and permissions check response call for this plugin.
63 63 # Useful for expensive calls like LDAP to improve the performance of the system (0 means disabled).
64 64
65 65 *option*: `host` => `192.168.245.143,192.168.1.240`
66 66 # Host[s] of the LDAP Server
67 67 # (e.g., 192.168.2.154, or ldap-server.domain.com.
68 68 # Multiple servers can be specified using commas
69 69
70 70 *option*: `port` => `389`
71 # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL)
71 # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL)
72 72
73 73 *option*: `timeout` => `300`
74 74 # Timeout for LDAP connection
75 75
76 76 *option*: `dn_user` => `Administrator@rhodecode.com`
77 77 # Optional user DN/account to connect to LDAP if authentication is required.
78 78 # e.g., cn=admin,dc=mydomain,dc=com, or uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com
79 79
80 80 *option*: `dn_pass` => `SomeSecret`
81 81 # Password to authenticate for given user DN.
82 82
83 83 *option*: `tls_kind` => `PLAIN`
84 84 # TLS Type
85 85
86 86 *option*: `tls_reqcert` => `NEVER`
87 87 # Require Cert over TLS?. Self-signed and custom certificates can be used when
88 88 # `RhodeCode Certificate` found in admin > settings > system info page is extended.
89 89
90 90 *option*: `tls_cert_file` => ``
91 91 # This specifies the PEM-format file path containing certificates for use in TLS connection.
92 92 # If not specified `TLS Cert dir` will be used
93 93
94 94 *option*: `tls_cert_dir` => `/etc/openldap/cacerts`
95 95 # This specifies the path of a directory that contains individual CA certificates in separate files.
96 96
97 97 *option*: `base_dn` => `dc=rhodecode,dc=com`
98 98 # Base DN to search. Dynamic bind is supported. Add `$login` marker in it to be replaced with current user credentials
99 99 # (e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)
100 100
101 101 *option*: `user_search_base` => `ou=RC-Users`
102 102 # User search base will extend the Base DN
103 103 # (e.g., ou=Users will result in ou=Users,dc=mydomain,dc=com root DN)
104 104
105 105 *option*: `user_search_filter` => ``
106 106 # Filter to narrow results
107 107 # (e.g., (&(objectCategory=Person)(objectClass=user)), or
108 108 # (memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))
109 109
110 110 *option*: `search_scope` => `SUBTREE`
111 111 # How deep to search LDAP. If unsure set to SUBTREE
112 112
113 113 *option*: `attr_login` => `sAMAccountName`
114 114 # LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)
115 115
116 116 *option*: `attr_email` => `mail`
117 117 # LDAP Attribute to map to email address (e.g., mail).
118 118 # Emails are a crucial part of RhodeCode.
119 119 # If possible add a valid email attribute to ldap users.
120 120
121 121 *option*: `attr_firstname` => `givenName`
122 122 # LDAP Attribute to map to first name (e.g., givenName)
123 123
124 124 *option*: `attr_lastname` => `sn`
125 125 # LDAP Attribute to map to last name (e.g., sn)
126 126
127 127 *option*: `group_extraction_type` => `rfc2307bis`
128 128 # With rfc2307, group members are listed by name in the memberUid attribute
129 129 # With rfc2307bis (Microsoft AD compatible) group members are listed by DN and stored in the member attribute
130 130
131 131 *option*: `group_search_base` => `ou=RC-Groups`
132 132 # Group search base will extend the Base DN (e.g. ou=Groups will result in ou=Groups,dc=mydomain,dc=com)
133 133
134 134 *option*: `group_name_attr` => `sAMAccountName`
135 135 # LDAP Attribute to map to group name (e.g., cn, or sAMAccountName)
136 136
137 137 *option*: `user_member_of` => `memberOf`
138 138 # Users Attribute used to fetch the group membership.
139 139 # Use if users have stored group membership inside their attributes
140 140 # (e.g., memberOf, or userMemberOf)
141 141
142 142 *option*: `group_search_filter` => ``
143 143 # Filter to narrow results (e.g., (&(objectCategory=Group)(objectClass=group)), etc)
144 144
145 145 *option*: `group_member_of` => `memberOf`
146 146 # LDAP Attribute used to resolve the parent group (e.g., memberOf)
147 147
148 148 *option*: `admin_groups` => `Admins,Management`
149 149 # A comma separated list of group names that identify users as RhodeCode Administrators (e.g., admins)
150 150
151 151 *option*: `admin_groups_sync` => `full`
152 152 # Way to sync Admin groups.
153 153 # Full means admin flag is set to on or off according to membership in administrator group defined above.
154 154 # On-only means the flag is only set to on, and not turned off once user is no longer a member
155 155
156 156
157 157 .. toctree::
158 158
159 159 ldap-active-directory
160 160 ldap-authentication No newline at end of file
@@ -1,117 +1,117 b''
1 1 .. _config-ldap-ref:
2 2
3 3 LDAP/AD
4 4 -------
5 5
6 6 |RCE| supports LDAP (Lightweight Directory Access Protocol) or
7 7 AD (active Directory) authentication.
8 8 All LDAP versions are currently supported.
9 9
10 10 RhodeCode reads all data defined from plugin and creates corresponding
11 11 accounts on local database after receiving data from LDAP. This is done on
12 12 every user log-in including operations like pushing/pulling/checkout.
13 13
14 14
15 15 .. important::
16 16
17 17 The email used with your |RCE| super-admin account needs to match the email
18 18 address attached to your admin profile in LDAP. This is because
19 19 within |RCE| the user email needs to be unique, and multiple users
20 20 cannot share an email account.
21 21
22 22 Likewise, if as an admin you also have a user account, the email address
23 23 attached to the user account needs to be different.
24 24
25 25
26 26 LDAP Configuration Steps
27 27 ^^^^^^^^^^^^^^^^^^^^^^^^
28 28
29 29 To configure |LDAP|, use the following steps:
30 30
31 31 1. From the |RCE| interface, select
32 32 :menuselection:`Admin --> Authentication`
33 33 2. Activate the `LDAP` plugin and select :guilabel:`Save`
34 34 3. Go to newly available menu option called `LDAP` on the left side.
35 35 4. Check the `enabled` check box in the plugin configuration section,
36 36 and fill in the required LDAP information and :guilabel:`Save`, for more details,
37 37 see :ref:`config-ldap-examples`
38 38
39 39 For a more detailed description of LDAP objects, see :ref:`ldap-gloss-ref`:
40 40
41 41 .. _config-ldap-examples:
42 42
43 43 Example LDAP configuration
44 44 ^^^^^^^^^^^^^^^^^^^^^^^^^^
45 45
46 46 Below is example setup that can be used with Active Directory/LDAP server::
47 47
48 48 *option*: `enabled` => `True`
49 49 # Enable or disable this authentication plugin.
50 50
51 51 *option*: `cache_ttl` => `360`
52 52 # Amount of seconds to cache the authentication and permissions check response call for this plugin.
53 53 # Useful for expensive calls like LDAP to improve the performance of the system (0 means disabled).
54 54
55 55 *option*: `host` => `192.168.245.143,192.168.1.240`
56 56 # Host[s] of the LDAP Server
57 57 # (e.g., 192.168.2.154, or ldap-server.domain.com.
58 58 # Multiple servers can be specified using commas
59 59
60 60 *option*: `port` => `389`
61 # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL)
61 # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL)
62 62
63 63 *option*: `timeout` => `300`
64 64 # Timeout for LDAP connection
65 65
66 66 *option*: `dn_user` => `Administrator@rhodecode.com`
67 67 # Optional user DN/account to connect to LDAP if authentication is required.
68 68 # e.g., cn=admin,dc=mydomain,dc=com, or uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com
69 69
70 70 *option*: `dn_pass` => `SomeSecret`
71 71 # Password to authenticate for given user DN.
72 72
73 73 *option*: `tls_kind` => `PLAIN`
74 74 # TLS Type
75 75
76 76 *option*: `tls_reqcert` => `NEVER`
77 77 # Require Cert over TLS?. Self-signed and custom certificates can be used when
78 78 # `RhodeCode Certificate` found in admin > settings > system info page is extended.
79 79
80 80 *option*: `tls_cert_file` => ``
81 81 # This specifies the PEM-format file path containing certificates for use in TLS connection.
82 82 # If not specified `TLS Cert dir` will be used
83 83
84 84 *option*: `tls_cert_dir` => `/etc/openldap/cacerts`
85 85 # This specifies the path of a directory that contains individual CA certificates in separate files.
86 86
87 87 *option*: `base_dn` => `cn=Rufus Magillacuddy,ou=users,dc=rhodecode,dc=com`
88 88 # Base DN to search. Dynamic bind is supported. Add `$login` marker in it to be replaced with current user credentials
89 89 # (e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)
90 90
91 91 *option*: `filter` => `(objectClass=person)`
92 92 # Filter to narrow results
93 93 # (e.g., (&(objectCategory=Person)(objectClass=user)), or
94 94 # (memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))
95 95
96 96 *option*: `search_scope` => `SUBTREE`
97 97 # How deep to search LDAP. If unsure set to SUBTREE
98 98
99 99 *option*: `attr_login` => `sAMAccountName`
100 100 # LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)
101 101
102 102 *option*: `attr_email` => `mail`
103 103 # LDAP Attribute to map to email address (e.g., mail).
104 104 # Emails are a crucial part of RhodeCode.
105 105 # If possible add a valid email attribute to ldap users.
106 106
107 107 *option*: `attr_firstname` => `givenName`
108 108 # LDAP Attribute to map to first name (e.g., givenName)
109 109
110 110 *option*: `attr_lastname` => `sn`
111 111 # LDAP Attribute to map to last name (e.g., sn)
112 112
113 113
114 114 .. toctree::
115 115
116 116 ldap-active-directory
117 117 ldap-authentication
@@ -1,75 +1,75 b''
1 1 .. _ldap-act-dir-ref:
2 2
3 3 Active Directory
4 4 ----------------
5 5
6 6 |RCE| can use Microsoft Active Directory for user authentication. This is
7 7 done through an LDAP or LDAPS connection to Active Directory. Use the
8 8 following example LDAP configuration setting to set your Active Directory
9 9 authentication::
10 10
11 11
12 12 *option*: `enabled` => `True`
13 13 # Enable or disable this authentication plugin.
14 14
15 15 *option*: `cache_ttl` => `360`
16 16 # Amount of seconds to cache the authentication and permissions check response call for this plugin.
17 17 # Useful for expensive calls like LDAP to improve the performance of the system (0 means disabled).
18 18
19 19 *option*: `host` => `192.168.245.143,192.168.1.240`
20 20 # Host[s] of the LDAP Server
21 21 # (e.g., 192.168.2.154, or ldap-server.domain.com.
22 22 # Multiple servers can be specified using commas
23 23
24 24 *option*: `port` => `389`
25 # Custom port that the LDAP server is listening on. Default value is: 389, use 689 for LDAPS(SSL)
25 # Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS(SSL)
26 26
27 27 *option*: `timeout` => `300`
28 28 # Timeout for LDAP connection
29 29
30 30 *option*: `dn_user` => `Administrator@rhodecode.com`
31 31 # Optional user DN/account to connect to LDAP if authentication is required.
32 32 # e.g., cn=admin,dc=mydomain,dc=com, or uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com
33 33
34 34 *option*: `dn_pass` => `SomeSecret`
35 35 # Password to authenticate for given user DN.
36 36
37 37 *option*: `tls_kind` => `PLAIN`
38 38 # TLS Type
39 39
40 40 *option*: `tls_reqcert` => `NEVER`
41 41 # Require Cert over TLS?. Self-signed and custom certificates can be used when
42 42 # `RhodeCode Certificate` found in admin > settings > system info page is extended.
43 43
44 44 *option*: `tls_cert_file` => ``
45 45 # This specifies the PEM-format file path containing certificates for use in TLS connection.
46 46 # If not specified `TLS Cert dir` will be used
47 47
48 48 *option*: `tls_cert_dir` => `/etc/openldap/cacerts`
49 49 # This specifies the path of a directory that contains individual CA certificates in separate files.
50 50
51 51 *option*: `base_dn` => `OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local`
52 52 # Base DN to search. Dynamic bind is supported. Add `$login` marker in it to be replaced with current user credentials
53 53 # (e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)
54 54
55 55 *option*: `filter` => `(objectClass=person)`
56 56 # Filter to narrow results
57 57 # (e.g., (&(objectCategory=Person)(objectClass=user)), or
58 58 # (memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))
59 59
60 60 *option*: `search_scope` => `SUBTREE`
61 61 # How deep to search LDAP. If unsure set to SUBTREE
62 62
63 63 *option*: `attr_login` => `sAMAccountName`
64 64 # LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)
65 65
66 66 *option*: `attr_email` => `userEmail`
67 67 # LDAP Attribute to map to email address (e.g., mail).
68 68 # Emails are a crucial part of RhodeCode.
69 69 # If possible add a valid email attribute to ldap users.
70 70
71 71 *option*: `attr_firstname` => `user_firstname`
72 72 # LDAP Attribute to map to first name (e.g., givenName)
73 73
74 74 *option*: `attr_lastname` => `user_surname`
75 75 # LDAP Attribute to map to last name (e.g., sn)
@@ -1,535 +1,535 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 RhodeCode authentication plugin for LDAP
23 23 """
24 24
25 25 import logging
26 26 import traceback
27 27
28 28 import colander
29 29 from rhodecode.translation import _
30 30 from rhodecode.authentication.base import (
31 31 RhodeCodeExternalAuthPlugin, AuthLdapBase, hybrid_property)
32 32 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
33 33 from rhodecode.authentication.routes import AuthnPluginResourceBase
34 34 from rhodecode.lib.colander_utils import strip_whitespace
35 35 from rhodecode.lib.exceptions import (
36 36 LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError
37 37 )
38 38 from rhodecode.lib.utils2 import safe_unicode, safe_str
39 39 from rhodecode.model.db import User
40 40 from rhodecode.model.validators import Missing
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44 try:
45 45 import ldap
46 46 except ImportError:
47 47 # means that python-ldap is not installed, we use Missing object to mark
48 48 # ldap lib is Missing
49 49 ldap = Missing
50 50
51 51
52 52 class LdapError(Exception):
53 53 pass
54 54
55 55
56 56 def plugin_factory(plugin_id, *args, **kwargs):
57 57 """
58 58 Factory function that is called during plugin discovery.
59 59 It returns the plugin instance.
60 60 """
61 61 plugin = RhodeCodeAuthPlugin(plugin_id)
62 62 return plugin
63 63
64 64
65 65 class LdapAuthnResource(AuthnPluginResourceBase):
66 66 pass
67 67
68 68
69 69 class AuthLdap(AuthLdapBase):
70 70 default_tls_cert_dir = '/etc/openldap/cacerts'
71 71
72 72 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
73 73 tls_kind='PLAIN', tls_reqcert='DEMAND', tls_cert_file=None,
74 74 tls_cert_dir=None, ldap_version=3,
75 75 search_scope='SUBTREE', attr_login='uid',
76 76 ldap_filter='', timeout=None):
77 77 if ldap == Missing:
78 78 raise LdapImportError("Missing or incompatible ldap library")
79 79
80 80 self.debug = False
81 81 self.timeout = timeout or 60 * 5
82 82 self.ldap_version = ldap_version
83 83 self.ldap_server_type = 'ldap'
84 84
85 85 self.TLS_KIND = tls_kind
86 86
87 87 if self.TLS_KIND == 'LDAPS':
88 port = port or 689
88 port = port or 636
89 89 self.ldap_server_type += 's'
90 90
91 91 OPT_X_TLS_DEMAND = 2
92 92 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert, OPT_X_TLS_DEMAND)
93 93 self.TLS_CERT_FILE = tls_cert_file or ''
94 94 self.TLS_CERT_DIR = tls_cert_dir or self.default_tls_cert_dir
95 95
96 96 # split server into list
97 97 self.SERVER_ADDRESSES = self._get_server_list(server)
98 98 self.LDAP_SERVER_PORT = port
99 99
100 100 # USE FOR READ ONLY BIND TO LDAP SERVER
101 101 self.attr_login = attr_login
102 102
103 103 self.LDAP_BIND_DN = safe_str(bind_dn)
104 104 self.LDAP_BIND_PASS = safe_str(bind_pass)
105 105
106 106 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
107 107 self.BASE_DN = safe_str(base_dn)
108 108 self.LDAP_FILTER = safe_str(ldap_filter)
109 109
110 110 def _get_ldap_conn(self):
111 111
112 112 if self.debug:
113 113 ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
114 114
115 115 if self.TLS_CERT_FILE and hasattr(ldap, 'OPT_X_TLS_CACERTFILE'):
116 116 ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.TLS_CERT_FILE)
117 117
118 118 elif hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
119 119 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.TLS_CERT_DIR)
120 120
121 121 if self.TLS_KIND != 'PLAIN':
122 122 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
123 123
124 124 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
125 125 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
126 126
127 127 # init connection now
128 128 ldap_servers = self._build_servers(
129 129 self.ldap_server_type, self.SERVER_ADDRESSES, self.LDAP_SERVER_PORT)
130 130 log.debug('initializing LDAP connection to:%s', ldap_servers)
131 131 ldap_conn = ldap.initialize(ldap_servers)
132 132 ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
133 133 ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout)
134 134 ldap_conn.timeout = self.timeout
135 135
136 136 if self.ldap_version == 2:
137 137 ldap_conn.protocol = ldap.VERSION2
138 138 else:
139 139 ldap_conn.protocol = ldap.VERSION3
140 140
141 141 if self.TLS_KIND == 'START_TLS':
142 142 ldap_conn.start_tls_s()
143 143
144 144 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
145 145 log.debug('Trying simple_bind with password and given login DN: %r',
146 146 self.LDAP_BIND_DN)
147 147 ldap_conn.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
148 148 log.debug('simple_bind successful')
149 149 return ldap_conn
150 150
151 151 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
152 152 try:
153 153 log.debug('Trying simple bind with %r', dn)
154 154 server.simple_bind_s(dn, safe_str(password))
155 155 _dn, attrs = server.search_ext_s(
156 156 dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0]
157 157
158 158 return attrs
159 159
160 160 except ldap.INVALID_CREDENTIALS:
161 161 log.debug(
162 162 "LDAP rejected password for user '%s': %s, org_exc:",
163 163 username, dn, exc_info=True)
164 164
165 165 def authenticate_ldap(self, username, password):
166 166 """
167 167 Authenticate a user via LDAP and return his/her LDAP properties.
168 168
169 169 Raises AuthenticationError if the credentials are rejected, or
170 170 EnvironmentError if the LDAP server can't be reached.
171 171
172 172 :param username: username
173 173 :param password: password
174 174 """
175 175
176 176 uid = self.get_uid(username, self.SERVER_ADDRESSES)
177 177 user_attrs = {}
178 178 dn = ''
179 179
180 180 self.validate_password(username, password)
181 181 self.validate_username(username)
182 182
183 183 ldap_conn = None
184 184 try:
185 185 ldap_conn = self._get_ldap_conn()
186 186 filter_ = '(&%s(%s=%s))' % (
187 187 self.LDAP_FILTER, self.attr_login, username)
188 188 log.debug("Authenticating %r filter %s", self.BASE_DN, filter_)
189 189
190 190 lobjects = ldap_conn.search_ext_s(
191 191 self.BASE_DN, self.SEARCH_SCOPE, filter_)
192 192
193 193 if not lobjects:
194 194 log.debug("No matching LDAP objects for authentication "
195 195 "of UID:'%s' username:(%s)", uid, username)
196 196 raise ldap.NO_SUCH_OBJECT()
197 197
198 198 log.debug('Found matching ldap object, trying to authenticate')
199 199 for (dn, _attrs) in lobjects:
200 200 if dn is None:
201 201 continue
202 202
203 203 user_attrs = self.fetch_attrs_from_simple_bind(
204 204 ldap_conn, dn, username, password)
205 205 if user_attrs:
206 206 break
207 207 else:
208 208 raise LdapPasswordError(
209 209 'Failed to authenticate user `{}` '
210 210 'with given password'.format(username))
211 211
212 212 except ldap.NO_SUCH_OBJECT:
213 213 log.debug("LDAP says no such user '%s' (%s), org_exc:",
214 214 uid, username, exc_info=True)
215 215 raise LdapUsernameError('Unable to find user')
216 216 except ldap.SERVER_DOWN:
217 217 org_exc = traceback.format_exc()
218 218 raise LdapConnectionError(
219 219 "LDAP can't access authentication "
220 220 "server, org_exc:%s" % org_exc)
221 221 finally:
222 222 if ldap_conn:
223 223 log.debug('ldap: connection release')
224 224 try:
225 225 ldap_conn.unbind_s()
226 226 except Exception:
227 227 # for any reason this can raise exception we must catch it
228 228 # to not crush the server
229 229 pass
230 230
231 231 return dn, user_attrs
232 232
233 233
234 234 class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
235 235 tls_kind_choices = ['PLAIN', 'LDAPS', 'START_TLS']
236 236 tls_reqcert_choices = ['NEVER', 'ALLOW', 'TRY', 'DEMAND', 'HARD']
237 237 search_scope_choices = ['BASE', 'ONELEVEL', 'SUBTREE']
238 238
239 239 host = colander.SchemaNode(
240 240 colander.String(),
241 241 default='',
242 242 description=_('Host[s] of the LDAP Server \n'
243 243 '(e.g., 192.168.2.154, or ldap-server.domain.com.\n '
244 244 'Multiple servers can be specified using commas'),
245 245 preparer=strip_whitespace,
246 246 title=_('LDAP Host'),
247 247 widget='string')
248 248 port = colander.SchemaNode(
249 249 colander.Int(),
250 250 default=389,
251 251 description=_('Custom port that the LDAP server is listening on. '
252 'Default value is: 389, use 689 for LDAPS (SSL)'),
252 'Default value is: 389, use 636 for LDAPS (SSL)'),
253 253 preparer=strip_whitespace,
254 254 title=_('Port'),
255 255 validator=colander.Range(min=0, max=65536),
256 256 widget='int')
257 257
258 258 timeout = colander.SchemaNode(
259 259 colander.Int(),
260 260 default=60 * 5,
261 261 description=_('Timeout for LDAP connection'),
262 262 preparer=strip_whitespace,
263 263 title=_('Connection timeout'),
264 264 validator=colander.Range(min=1),
265 265 widget='int')
266 266
267 267 dn_user = colander.SchemaNode(
268 268 colander.String(),
269 269 default='',
270 270 description=_('Optional user DN/account to connect to LDAP if authentication is required. \n'
271 271 'e.g., cn=admin,dc=mydomain,dc=com, or '
272 272 'uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com'),
273 273 missing='',
274 274 preparer=strip_whitespace,
275 275 title=_('Bind account'),
276 276 widget='string')
277 277 dn_pass = colander.SchemaNode(
278 278 colander.String(),
279 279 default='',
280 280 description=_('Password to authenticate for given user DN.'),
281 281 missing='',
282 282 preparer=strip_whitespace,
283 283 title=_('Bind account password'),
284 284 widget='password')
285 285 tls_kind = colander.SchemaNode(
286 286 colander.String(),
287 287 default=tls_kind_choices[0],
288 288 description=_('TLS Type'),
289 289 title=_('Connection Security'),
290 290 validator=colander.OneOf(tls_kind_choices),
291 291 widget='select')
292 292 tls_reqcert = colander.SchemaNode(
293 293 colander.String(),
294 294 default=tls_reqcert_choices[0],
295 295 description=_('Require Cert over TLS?. Self-signed and custom '
296 296 'certificates can be used when\n `RhodeCode Certificate` '
297 297 'found in admin > settings > system info page is extended.'),
298 298 title=_('Certificate Checks'),
299 299 validator=colander.OneOf(tls_reqcert_choices),
300 300 widget='select')
301 301 tls_cert_file = colander.SchemaNode(
302 302 colander.String(),
303 303 default='',
304 304 description=_('This specifies the PEM-format file path containing '
305 305 'certificates for use in TLS connection.\n'
306 306 'If not specified `TLS Cert dir` will be used'),
307 307 title=_('TLS Cert file'),
308 308 missing='',
309 309 widget='string')
310 310 tls_cert_dir = colander.SchemaNode(
311 311 colander.String(),
312 312 default=AuthLdap.default_tls_cert_dir,
313 313 description=_('This specifies the path of a directory that contains individual '
314 314 'CA certificates in separate files.'),
315 315 title=_('TLS Cert dir'),
316 316 widget='string')
317 317 base_dn = colander.SchemaNode(
318 318 colander.String(),
319 319 default='',
320 320 description=_('Base DN to search. Dynamic bind is supported. Add `$login` marker '
321 321 'in it to be replaced with current user username \n'
322 322 '(e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)'),
323 323 missing='',
324 324 preparer=strip_whitespace,
325 325 title=_('Base DN'),
326 326 widget='string')
327 327 filter = colander.SchemaNode(
328 328 colander.String(),
329 329 default='',
330 330 description=_('Filter to narrow results \n'
331 331 '(e.g., (&(objectCategory=Person)(objectClass=user)), or \n'
332 332 '(memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))'),
333 333 missing='',
334 334 preparer=strip_whitespace,
335 335 title=_('LDAP Search Filter'),
336 336 widget='string')
337 337
338 338 search_scope = colander.SchemaNode(
339 339 colander.String(),
340 340 default=search_scope_choices[2],
341 341 description=_('How deep to search LDAP. If unsure set to SUBTREE'),
342 342 title=_('LDAP Search Scope'),
343 343 validator=colander.OneOf(search_scope_choices),
344 344 widget='select')
345 345 attr_login = colander.SchemaNode(
346 346 colander.String(),
347 347 default='uid',
348 348 description=_('LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)'),
349 349 preparer=strip_whitespace,
350 350 title=_('Login Attribute'),
351 351 missing_msg=_('The LDAP Login attribute of the CN must be specified'),
352 352 widget='string')
353 353 attr_email = colander.SchemaNode(
354 354 colander.String(),
355 355 default='',
356 356 description=_('LDAP Attribute to map to email address (e.g., mail).\n'
357 357 'Emails are a crucial part of RhodeCode. \n'
358 358 'If possible add a valid email attribute to ldap users.'),
359 359 missing='',
360 360 preparer=strip_whitespace,
361 361 title=_('Email Attribute'),
362 362 widget='string')
363 363 attr_firstname = colander.SchemaNode(
364 364 colander.String(),
365 365 default='',
366 366 description=_('LDAP Attribute to map to first name (e.g., givenName)'),
367 367 missing='',
368 368 preparer=strip_whitespace,
369 369 title=_('First Name Attribute'),
370 370 widget='string')
371 371 attr_lastname = colander.SchemaNode(
372 372 colander.String(),
373 373 default='',
374 374 description=_('LDAP Attribute to map to last name (e.g., sn)'),
375 375 missing='',
376 376 preparer=strip_whitespace,
377 377 title=_('Last Name Attribute'),
378 378 widget='string')
379 379
380 380
381 381 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
382 382 uid = 'ldap'
383 383 # used to define dynamic binding in the
384 384 DYNAMIC_BIND_VAR = '$login'
385 385 _settings_unsafe_keys = ['dn_pass']
386 386
387 387 def includeme(self, config):
388 388 config.add_authn_plugin(self)
389 389 config.add_authn_resource(self.get_id(), LdapAuthnResource(self))
390 390 config.add_view(
391 391 'rhodecode.authentication.views.AuthnPluginViewBase',
392 392 attr='settings_get',
393 393 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
394 394 request_method='GET',
395 395 route_name='auth_home',
396 396 context=LdapAuthnResource)
397 397 config.add_view(
398 398 'rhodecode.authentication.views.AuthnPluginViewBase',
399 399 attr='settings_post',
400 400 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
401 401 request_method='POST',
402 402 route_name='auth_home',
403 403 context=LdapAuthnResource)
404 404
405 405 def get_settings_schema(self):
406 406 return LdapSettingsSchema()
407 407
408 408 def get_display_name(self):
409 409 return _('LDAP')
410 410
411 411 @classmethod
412 412 def docs(cls):
413 413 return "https://docs.rhodecode.com/RhodeCode-Enterprise/auth/auth-ldap.html"
414 414
415 415 @hybrid_property
416 416 def name(self):
417 417 return u"ldap"
418 418
419 419 def use_fake_password(self):
420 420 return True
421 421
422 422 def user_activation_state(self):
423 423 def_user_perms = User.get_default_user().AuthUser().permissions['global']
424 424 return 'hg.extern_activate.auto' in def_user_perms
425 425
426 426 def try_dynamic_binding(self, username, password, current_args):
427 427 """
428 428 Detects marker inside our original bind, and uses dynamic auth if
429 429 present
430 430 """
431 431
432 432 org_bind = current_args['bind_dn']
433 433 passwd = current_args['bind_pass']
434 434
435 435 def has_bind_marker(username):
436 436 if self.DYNAMIC_BIND_VAR in username:
437 437 return True
438 438
439 439 # we only passed in user with "special" variable
440 440 if org_bind and has_bind_marker(org_bind) and not passwd:
441 441 log.debug('Using dynamic user/password binding for ldap '
442 442 'authentication. Replacing `%s` with username',
443 443 self.DYNAMIC_BIND_VAR)
444 444 current_args['bind_dn'] = org_bind.replace(
445 445 self.DYNAMIC_BIND_VAR, username)
446 446 current_args['bind_pass'] = password
447 447
448 448 return current_args
449 449
450 450 def auth(self, userobj, username, password, settings, **kwargs):
451 451 """
452 452 Given a user object (which may be null), username, a plaintext password,
453 453 and a settings object (containing all the keys needed as listed in
454 454 settings()), authenticate this user's login attempt.
455 455
456 456 Return None on failure. On success, return a dictionary of the form:
457 457
458 458 see: RhodeCodeAuthPluginBase.auth_func_attrs
459 459 This is later validated for correctness
460 460 """
461 461
462 462 if not username or not password:
463 463 log.debug('Empty username or password skipping...')
464 464 return None
465 465
466 466 ldap_args = {
467 467 'server': settings.get('host', ''),
468 468 'base_dn': settings.get('base_dn', ''),
469 469 'port': settings.get('port'),
470 470 'bind_dn': settings.get('dn_user'),
471 471 'bind_pass': settings.get('dn_pass'),
472 472 'tls_kind': settings.get('tls_kind'),
473 473 'tls_reqcert': settings.get('tls_reqcert'),
474 474 'tls_cert_file': settings.get('tls_cert_file'),
475 475 'tls_cert_dir': settings.get('tls_cert_dir'),
476 476 'search_scope': settings.get('search_scope'),
477 477 'attr_login': settings.get('attr_login'),
478 478 'ldap_version': 3,
479 479 'ldap_filter': settings.get('filter'),
480 480 'timeout': settings.get('timeout')
481 481 }
482 482
483 483 ldap_attrs = self.try_dynamic_binding(username, password, ldap_args)
484 484
485 485 log.debug('Checking for ldap authentication.')
486 486
487 487 try:
488 488 aldap = AuthLdap(**ldap_args)
489 489 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
490 490 log.debug('Got ldap DN response %s', user_dn)
491 491
492 492 def get_ldap_attr(k):
493 493 return ldap_attrs.get(settings.get(k), [''])[0]
494 494
495 495 # old attrs fetched from RhodeCode database
496 496 admin = getattr(userobj, 'admin', False)
497 497 active = getattr(userobj, 'active', True)
498 498 email = getattr(userobj, 'email', '')
499 499 username = getattr(userobj, 'username', username)
500 500 firstname = getattr(userobj, 'firstname', '')
501 501 lastname = getattr(userobj, 'lastname', '')
502 502 extern_type = getattr(userobj, 'extern_type', '')
503 503
504 504 groups = []
505 505
506 506 user_attrs = {
507 507 'username': username,
508 508 'firstname': safe_unicode(get_ldap_attr('attr_firstname') or firstname),
509 509 'lastname': safe_unicode(get_ldap_attr('attr_lastname') or lastname),
510 510 'groups': groups,
511 511 'user_group_sync': False,
512 512 'email': get_ldap_attr('attr_email') or email,
513 513 'admin': admin,
514 514 'active': active,
515 515 'active_from_extern': None,
516 516 'extern_name': user_dn,
517 517 'extern_type': extern_type,
518 518 }
519 519
520 520 log.debug('ldap user: %s', user_attrs)
521 521 log.info('user `%s` authenticated correctly', user_attrs['username'])
522 522
523 523 return user_attrs
524 524
525 525 except (LdapUsernameError, LdapPasswordError, LdapImportError):
526 526 log.exception("LDAP related exception")
527 527 return None
528 528 except (Exception,):
529 529 log.exception("Other exception")
530 530 return None
531 531
532 532
533 533 def includeme(config):
534 534 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
535 535 plugin_factory(plugin_id).includeme(config)
General Comments 0
You need to be logged in to leave comments. Login now