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