2017-06-25 12:29:43 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# This file is part of Radicale Server - Calendar Server
|
|
|
|
# Copyright © 2011 Corentin Le Bail
|
|
|
|
# Copyright © 2011-2013 Guillaume Ayoub
|
|
|
|
# Copyright © 2015 Raoul Thill
|
|
|
|
# Copyright © 2017 Marco Huenseler
|
|
|
|
#
|
|
|
|
# This library is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This library is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
"""
|
|
|
|
LDAP authentication.
|
|
|
|
Authentication based on the ``ldap3`` module
|
|
|
|
(https://github.com/cannatag/ldap3/).
|
|
|
|
"""
|
|
|
|
|
2017-08-04 16:12:38 +00:00
|
|
|
|
2017-06-25 12:29:43 +00:00
|
|
|
import ldap3
|
2017-08-04 15:45:29 +00:00
|
|
|
import ldap3.core.exceptions
|
2017-06-25 12:29:43 +00:00
|
|
|
|
|
|
|
from radicale.auth import BaseAuth
|
|
|
|
|
2017-08-04 16:12:38 +00:00
|
|
|
import radicale_auth_ldap.ldap3imports
|
|
|
|
|
|
|
|
|
2017-06-25 12:29:43 +00:00
|
|
|
class Auth(BaseAuth):
|
|
|
|
def is_authenticated(self, user, password):
|
|
|
|
"""Check if ``user``/``password`` couple is valid."""
|
|
|
|
SERVER = ldap3.Server(self.configuration.get("auth", "ldap_url"))
|
|
|
|
BASE = self.configuration.get("auth", "ldap_base")
|
|
|
|
ATTRIBUTE = self.configuration.get("auth", "ldap_attribute")
|
|
|
|
FILTER = self.configuration.get("auth", "ldap_filter")
|
|
|
|
BINDDN = self.configuration.get("auth", "ldap_binddn")
|
|
|
|
PASSWORD = self.configuration.get("auth", "ldap_password")
|
|
|
|
SCOPE = self.configuration.get("auth", "ldap_scope")
|
Add ldap_support_extended configuration
* Add ldap_support_extended configuration
For samba, extended command is not supported.
To solve, add configuration to call whoami or not.
```
DEBUG:ldap3:EXTENDED:ldap message received via <ldaps://samba:636 - ssl - user: CN=test2,CN=Users,DC=samba,DC=dom - not lazy - bound - open - <local: 172.18.0.0:33916 - remote: 172.18.0.0:636> - tls not started - listening - SyncStrategy - internal decoder>:
<<{'controls': None,
<< 'messageID': 9,
<< 'payload': [(0, False, 10, 2),
<< (0, False, 4, b''),
<< (0,
<< False,
<< 4,
<< b'Extended Operation(1.3.6.1.4.1.4203.1.11.3) not supported'),
<< (2, False, 10, b'1.3.6.1.4.1.4203.1.11.3')],
<< 'protocolOp': 24}
DEBUG:ldap3:PROTOCOL:EXTENDED response <[{'result': 2, 'description': 'protocolError', 'dn': '', 'message': 'Extended Operation(1.3.6.1.4.1.4203.1.11.3) not supported', 'referrals': None, 'responseName': '1.3.6.1.4.1.4203.1.11.3', 'responseValue': b'', 'type': 'extendedResp'}]> received via <ldaps://samba:636 - ssl - user: CN=test2,CN=Users,DC=samba,DC=dom - not lazy - bound - open - <local: 172.18.0.0:33916 - remote: 172.18.0.0:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:done EXTENDED operation, result <False>
```
* Update README.md
2019-05-23 12:35:06 +00:00
|
|
|
SUPPORT_EXTENDED = self.configuration.getboolean("auth", "ldap_support_extended", fallback=True)
|
2017-06-25 12:29:43 +00:00
|
|
|
|
|
|
|
if BINDDN and PASSWORD:
|
|
|
|
conn = ldap3.Connection(SERVER, BINDDN, PASSWORD)
|
|
|
|
else:
|
|
|
|
conn = ldap3.Connection(SERVER)
|
2018-02-07 10:17:44 +00:00
|
|
|
conn.bind()
|
2017-06-25 12:29:43 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
self.logger.debug("LDAP whoami: %s" % conn.extend.standard.who_am_i())
|
|
|
|
except Exception as err:
|
|
|
|
self.logger.debug("LDAP error: %s" % err)
|
|
|
|
|
2017-08-04 16:12:38 +00:00
|
|
|
distinguished_name = "%s=%s" % (ATTRIBUTE, ldap3imports.escape_attribute_value(user))
|
2017-06-25 12:29:43 +00:00
|
|
|
self.logger.debug("LDAP bind for %s in base %s" % (distinguished_name, BASE))
|
|
|
|
|
|
|
|
if FILTER:
|
|
|
|
filter_string = "(&(%s)%s)" % (distinguished_name, FILTER)
|
|
|
|
else:
|
|
|
|
filter_string = distinguished_name
|
|
|
|
self.logger.debug("LDAP filter: %s" % filter_string)
|
|
|
|
|
|
|
|
conn.search(search_base=BASE,
|
|
|
|
search_scope=SCOPE,
|
|
|
|
search_filter=filter_string,
|
|
|
|
attributes=[ATTRIBUTE])
|
|
|
|
|
|
|
|
users = conn.response
|
|
|
|
|
|
|
|
if users:
|
|
|
|
user_dn = users[0]['dn']
|
|
|
|
uid = users[0]['attributes'][ATTRIBUTE]
|
|
|
|
self.logger.debug("LDAP user %s (%s) found" % (uid, user_dn))
|
|
|
|
try:
|
|
|
|
conn = ldap3.Connection(SERVER, user_dn, password)
|
|
|
|
conn.bind()
|
|
|
|
self.logger.debug(conn.result)
|
Add ldap_support_extended configuration
* Add ldap_support_extended configuration
For samba, extended command is not supported.
To solve, add configuration to call whoami or not.
```
DEBUG:ldap3:EXTENDED:ldap message received via <ldaps://samba:636 - ssl - user: CN=test2,CN=Users,DC=samba,DC=dom - not lazy - bound - open - <local: 172.18.0.0:33916 - remote: 172.18.0.0:636> - tls not started - listening - SyncStrategy - internal decoder>:
<<{'controls': None,
<< 'messageID': 9,
<< 'payload': [(0, False, 10, 2),
<< (0, False, 4, b''),
<< (0,
<< False,
<< 4,
<< b'Extended Operation(1.3.6.1.4.1.4203.1.11.3) not supported'),
<< (2, False, 10, b'1.3.6.1.4.1.4203.1.11.3')],
<< 'protocolOp': 24}
DEBUG:ldap3:PROTOCOL:EXTENDED response <[{'result': 2, 'description': 'protocolError', 'dn': '', 'message': 'Extended Operation(1.3.6.1.4.1.4203.1.11.3) not supported', 'referrals': None, 'responseName': '1.3.6.1.4.1.4203.1.11.3', 'responseValue': b'', 'type': 'extendedResp'}]> received via <ldaps://samba:636 - ssl - user: CN=test2,CN=Users,DC=samba,DC=dom - not lazy - bound - open - <local: 172.18.0.0:33916 - remote: 172.18.0.0:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:done EXTENDED operation, result <False>
```
* Update README.md
2019-05-23 12:35:06 +00:00
|
|
|
if SUPPORT_EXTENDED:
|
|
|
|
whoami = conn.extend.standard.who_am_i()
|
|
|
|
self.logger.debug("LDAP whoami: %s" % whoami)
|
|
|
|
else:
|
|
|
|
self.logger.debug("LDAP skip extended: call whoami")
|
|
|
|
whoami = conn.result['result'] == 0
|
2017-06-25 12:29:43 +00:00
|
|
|
if whoami:
|
|
|
|
self.logger.debug("LDAP bind OK")
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
self.logger.debug("LDAP bind failed")
|
|
|
|
return False
|
2017-08-04 15:45:29 +00:00
|
|
|
except ldap3.core.exceptions.LDAPInvalidCredentialsResult:
|
2017-06-25 12:29:43 +00:00
|
|
|
self.logger.debug("LDAP invalid credentials")
|
|
|
|
except Exception as err:
|
|
|
|
self.logger.debug("LDAP error %s" % err)
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
self.logger.debug("LDAP user %s not found" % user)
|
|
|
|
return False
|