From 473d9abaebdf75e3bc9dc59f012a8faf070b69b7 Mon Sep 17 00:00:00 2001
From: Kirk Gleason <kgleason@bloominsuranceagency.com>
Date: Sun, 15 Apr 2018 13:59:12 -0400
Subject: [PATCH] Rollback README for LDAP plugin to match stable so new patch
can be applied. Only whitespace and capitalization changes are lost.
Added in Active Directory config flags that are required. Adjusted email search so that it returns the entire user object. Made the search field variable.
Updated docs to add in AD specific bits
Converted the LDAP_START_TLS option from string to bool
Added some explanation to all of the LDAP configuration options.
Converted LDAP_ACTIVE_DIRECTORY from bool to string
Converted bool config options to string
Ignore PyCharm artifacts, package-lock, and pytest cache files.
Added in checks for all of the possible LDAP config variables, and tested for them. It is not yet properly handling required values, but it's no different than when I started looking at it.
Updated the docs to reflect the optional config values.
More details on how authenitcation works are in the documentation. The logic of EMAIL_SEARCH_FIELD is more clear.
EMAIL_SEARCH_FIELD defaults to None, and is used to trigger or skip the email lookup
Active Direcotry configuration option is more clearly named.
Can now restrcit ability to LDAP auth based on group membership.
Signed-off-by: Kirk Gleason <kgleason@bloominsuranceagency.com>
Correct capitalization on LDAP in documentation.
---
mediagoblin/plugins/ldap/tools.py | 109 +++++++++++++++++++++++++++---
1 file changed, 101 insertions(+), 8 deletions(-)
diff --git a/mediagoblin/plugins/ldap/tools.py b/mediagoblin/plugins/ldap/tools.py
index 2be2dcd7..4c527208 100644
a
|
b
|
class LDAP(object):
|
27 | 27 | def __init__(self): |
28 | 28 | self.ldap_settings = pluginapi.get_config('mediagoblin.plugins.ldap') |
29 | 29 | |
| 30 | for k, v in six.iteritems(self.ldap_settings): |
| 31 | try: |
| 32 | v['LDAP_SERVER_URI'] |
| 33 | except KeyError: |
| 34 | _log.error('LDAP_SERVER_URI was not defined in the config.') |
| 35 | # Do something here to raise a fatal error |
| 36 | |
| 37 | try: |
| 38 | v['LDAP_START_TLS'] |
| 39 | except KeyError: |
| 40 | _log.info('LDAP_START_TLS is not defined. Assuming false') |
| 41 | self.ldap_settings[k]['LDAP_START_TLS'] = 'false' |
| 42 | |
| 43 | try: |
| 44 | v['LDAP_USER_DN_TEMPLATE'] |
| 45 | except KeyError: |
| 46 | _log.error('LDAP_USER_DN_TEMPLATE ' |
| 47 | 'was not defined in the config') |
| 48 | # Do something here to raise a fatal error |
| 49 | |
| 50 | try: |
| 51 | v['LDAP_IS_ACTIVE_DIRECTORY'] |
| 52 | except KeyError: |
| 53 | _log.info('Active Directory flag was not set. Assuming false') |
| 54 | self.ldap_settings[k]['LDAP_IS_ACTIVE_DIRECTORY'] = 'false' |
| 55 | |
| 56 | try: |
| 57 | v['LDAP_SEARCH_BASE'] |
| 58 | except KeyError: |
| 59 | _log.error('LDAP_SEARCH_BASE was not defined in the config') |
| 60 | # Do something here to raise a fatal error |
| 61 | |
| 62 | try: |
| 63 | v['UID_SEARCH_FIELD'] |
| 64 | except KeyError: |
| 65 | _log.info('UID_SEARCH_FIELD was not defined in the config. ' |
| 66 | 'Assuming ''uid''.') |
| 67 | self.ldap_settings[k]['UID_SEARCH_FIELD'] = 'uid' |
| 68 | |
| 69 | try: |
| 70 | v['EMAIL_SEARCH_FIELD'] |
| 71 | except KeyError: |
| 72 | _log.info('EMAIL_SEARCH_FIELD was not defined in the config. ' |
| 73 | 'Assuming mail lookup is not wanted.') |
| 74 | self.ldap_settings[k]['EMAIL_SEARCH_FIELD'] = None |
| 75 | |
| 76 | try: |
| 77 | v['LDAP_FILTER'] |
| 78 | except KeyError: |
| 79 | _log.info('LDAP_FILTER was not defined in the config. ' |
| 80 | 'Assuming ''(objectClass=person)''') |
| 81 | self.ldap_settings[k]['LDAP_FILTER'] = '(objectClass=person)' |
| 82 | |
| 83 | _log.info(self.ldap_settings) |
| 84 | |
30 | 85 | def _connect(self, server): |
31 | 86 | _log.info('Connecting to {0}.'.format(server['LDAP_SERVER_URI'])) |
32 | 87 | self.conn = ldap.initialize(server['LDAP_SERVER_URI']) |
33 | 88 | |
34 | | if server['LDAP_START_TLS'] == 'true': |
| 89 | if server['LDAP_START_TLS'].lower() == 'true': |
35 | 90 | _log.info('Initiating TLS') |
36 | 91 | self.conn.start_tls_s() |
37 | 92 | |
38 | 93 | def _get_email(self, server, username): |
| 94 | if server['EMAIL_SEARCH_FIELD']: |
| 95 | try: |
| 96 | filter = '{0}={1}'.format(server['UID_SEARCH_FIELD'], username) |
| 97 | attrs = [server['EMAIL_SEARCH_FIELD']] |
| 98 | results = self.conn.search_s(server['LDAP_SEARCH_BASE'], |
| 99 | ldap.SCOPE_SUBTREE, filter, attrs) |
| 100 | |
| 101 | email = results[0][1][server['EMAIL_SEARCH_FIELD']][0] |
| 102 | except KeyError: |
| 103 | email = None |
| 104 | else: |
| 105 | email = None |
| 106 | |
| 107 | return email |
| 108 | |
| 109 | def _validate_account(self, server, username): |
39 | 110 | try: |
| 111 | filter = server['LDAP_FILTER'] |
| 112 | attrs = [server['UID_SEARCH_FIELD']] |
| 113 | |
40 | 114 | results = self.conn.search_s(server['LDAP_SEARCH_BASE'], |
41 | | ldap.SCOPE_SUBTREE, 'uid={0}' |
42 | | .format(username), |
43 | | [server['EMAIL_SEARCH_FIELD']]) |
| 115 | ldap.SCOPE_SUBTREE, filter, attrs) |
| 116 | |
| 117 | valid_account = False |
| 118 | for res in results: |
| 119 | if res[1][server['UID_SEARCH_FIELD']][0] == username: |
| 120 | valid_account = True |
| 121 | break |
44 | 122 | |
45 | | email = results[0][1][server['EMAIL_SEARCH_FIELD']][0] |
46 | 123 | except KeyError: |
47 | | email = None |
| 124 | valid_account = False |
| 125 | except TypeError: |
| 126 | valid_account = False |
48 | 127 | |
49 | | return email |
| 128 | return valid_account |
50 | 129 | |
51 | 130 | def login(self, username, password): |
52 | 131 | for k, v in six.iteritems(self.ldap_settings): |
53 | 132 | try: |
54 | 133 | self._connect(v) |
55 | 134 | user_dn = v['LDAP_USER_DN_TEMPLATE'].format(username=username) |
| 135 | |
| 136 | if v['LDAP_IS_ACTIVE_DIRECTORY'].lower() == 'true': |
| 137 | self.conn.protocol_version = ldap.VERSION3 |
| 138 | self.conn.set_option(ldap.OPT_REFERRALS, 0) |
| 139 | |
| 140 | _log.info('Attempting to bind to {0} as {1}'.format( |
| 141 | v['LDAP_SERVER_URI'], user_dn)) |
56 | 142 | self.conn.simple_bind_s(user_dn, password.encode('utf8')) |
57 | | email = self._get_email(v, username) |
| 143 | |
| 144 | if self._validate_account(v, username): |
| 145 | email = self._get_email(v, username) |
| 146 | else: |
| 147 | return False, None |
| 148 | |
58 | 149 | return username, email |
59 | 150 | |
| 151 | except ValueError, e: |
| 152 | _log.info(e) |
60 | 153 | except ldap.LDAPError, e: |
61 | 154 | _log.info(e) |
62 | 155 | |