| | 86 | |
| | 87 | class LDAPAuth(AuthBase): |
| | 88 | implements(IAuth) |
| | 89 | """Implement a synchronous authentication with an LDAP directory.""" |
| | 90 | |
| | 91 | basedn = "" |
| | 92 | """Base DN (Distinguished Name): the root of the LDAP directory tree |
| | 93 | |
| | 94 | e.g.: ou=people,dc=subdomain,dc=company,dc=com""" |
| | 95 | |
| | 96 | binddn = "" |
| | 97 | """The bind DN is the user on the external LDAP server permitted to |
| | 98 | search the LDAP directory. You can leave this empty.""" |
| | 99 | |
| | 100 | passwd = "" |
| | 101 | """Password required to query the LDAP server. Leave this empty if |
| | 102 | you can query the server without password.""" |
| | 103 | |
| | 104 | host = "" |
| | 105 | """Hostname of the LDAP server""" |
| | 106 | |
| | 107 | search = "" |
| | 108 | """Template string to use to search the user trying to login in the |
| | 109 | LDAP directory""" |
| | 110 | |
| | 111 | def __init__(self, host, basedn, binddn="", passwd="", |
| | 112 | search="(uid=%s)"): |
| | 113 | """Authenticate users against the LDAP server on C{host}. |
| | 114 | |
| | 115 | The arguments are documented above.""" |
| | 116 | self.host = host |
| | 117 | self.basedn = basedn |
| | 118 | self.binddn = binddn |
| | 119 | self.passwd = passwd |
| | 120 | self.search = search |
| | 121 | |
| | 122 | self.search_conn = None |
| | 123 | self.connect() |
| | 124 | |
| | 125 | def connect(self): |
| | 126 | """Setup the connections with the LDAP server.""" |
| | 127 | import ldap |
| | 128 | # Close existing connections |
| | 129 | if self.search_conn: |
| | 130 | self.search_conn.unbind() |
| | 131 | # Connection used to locate the users in the LDAP DB. |
| | 132 | self.search_conn = ldap.open(self.host) |
| | 133 | self.search_conn.bind_s(self.binddn, self.passwd, |
| | 134 | ldap.AUTH_SIMPLE) |
| | 135 | |
| | 136 | def authenticate(self, login, password): |
| | 137 | """Authenticate the C{login} / C{password} with the LDAP server.""" |
| | 138 | import ldap |
| | 139 | # Python-LDAP raises all sorts of exceptions to express various |
| | 140 | # failures, let's catch them all here and assume that if |
| | 141 | # anything goes wrong, the authentication failed. |
| | 142 | try: |
| | 143 | res = self._authenticate(login, password) |
| | 144 | if res: |
| | 145 | self.err = "" |
| | 146 | return res |
| | 147 | except ldap.LDAPError, e: |
| | 148 | self.err = "LDAP error: " + str(e) |
| | 149 | return False |
| | 150 | except: |
| | 151 | self.err = "unknown error" |
| | 152 | return False |
| | 153 | |
| | 154 | def _authenticate(self, login, password): |
| | 155 | import ldap |
| | 156 | # Search the login in the LDAP DB |
| | 157 | try: |
| | 158 | result = self.search_conn.search_s(self.basedn, |
| | 159 | ldap.SCOPE_SUBTREE, |
| | 160 | self.search % login, |
| | 161 | ['objectclass'], 1) |
| | 162 | except ldap.SERVER_DOWN: |
| | 163 | self.err = "LDAP server seems down" |
| | 164 | # Try to reconnect... |
| | 165 | self.connect() |
| | 166 | # FIXME: Check that this can't lead to an infinite recursion |
| | 167 | return self.authenticate(login, password) |
| | 168 | |
| | 169 | # Make sure we found a single user in the LDAP DB |
| | 170 | if not result or len(result) != 1: |
| | 171 | self.err = "user not found in the LDAP DB" |
| | 172 | return False |
| | 173 | |
| | 174 | # Connection used to authenticate users with the LDAP DB. |
| | 175 | auth_conn = ldap.open(self.host) |
| | 176 | # DN associated to this user |
| | 177 | ldap_dn = result[0][0] |
| | 178 | #log.msg('using ldap_dn = ' + ldap_dn) |
| | 179 | # Authenticate the user |
| | 180 | try: |
| | 181 | auth_conn.bind_s(ldap_dn, password, ldap.AUTH_SIMPLE) |
| | 182 | except ldap.INVALID_CREDENTIALS: |
| | 183 | self.err = "invalid credentials" |
| | 184 | return False |
| | 185 | auth_conn.unbind() |
| | 186 | return True |