mirror of
https://github.com/jdupouy/radicale3-auth-ldap.git
synced 2024-10-27 19:14:00 +00:00
Update to support Radicale 3.x
This commit is contained in:
parent
a396d8da41
commit
0e64ecc229
@ -1,5 +1,5 @@
|
|||||||
# What is this?
|
# What is this?
|
||||||
This is an authentication plugin for Radicale 2. It adds an LDAP authentication backend which can be used for authenticating users against an LDAP server.
|
This is an authentication plugin for Radicale 3. It adds an LDAP authentication backend which can be used for authenticating users against an LDAP server.
|
||||||
|
|
||||||
# How to configure
|
# How to configure
|
||||||
You will need to set a few options inside your radicale config file. Example:
|
You will need to set a few options inside your radicale config file. Example:
|
||||||
|
@ -30,12 +30,41 @@ import ldap3
|
|||||||
import ldap3.core.exceptions
|
import ldap3.core.exceptions
|
||||||
|
|
||||||
from radicale.auth import BaseAuth
|
from radicale.auth import BaseAuth
|
||||||
|
from radicale.log import logger
|
||||||
|
|
||||||
import radicale_auth_ldap.ldap3imports
|
import radicale_auth_ldap.ldap3imports
|
||||||
|
|
||||||
|
PLUGIN_CONFIG_SCHEMA = {"auth": {
|
||||||
|
"ldap_url": {
|
||||||
|
"value": "ldap://localhost:389",
|
||||||
|
"type": str},
|
||||||
|
"ldap_base": {
|
||||||
|
"value": "ou=Users",
|
||||||
|
"type": str},
|
||||||
|
"ldap_attribute": {
|
||||||
|
"value": "uid",
|
||||||
|
"type": str},
|
||||||
|
"ldap_filter": {
|
||||||
|
"value": "(objectClass=*)",
|
||||||
|
"type": str},
|
||||||
|
"ldap_binddn": {
|
||||||
|
"value": None,
|
||||||
|
"type": str},
|
||||||
|
"ldap_password": {
|
||||||
|
"value": None,
|
||||||
|
"type": str},
|
||||||
|
"ldap_scope": {
|
||||||
|
"value": "SUBTREE",
|
||||||
|
"type": str},
|
||||||
|
"ldap_support_extended": {
|
||||||
|
"value": True,
|
||||||
|
"type": bool}}}
|
||||||
|
|
||||||
class Auth(BaseAuth):
|
class Auth(BaseAuth):
|
||||||
def is_authenticated(self, user, password):
|
def __init__(self, configuration):
|
||||||
|
super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
|
||||||
|
|
||||||
|
def login(self, user, password):
|
||||||
"""Check if ``user``/``password`` couple is valid."""
|
"""Check if ``user``/``password`` couple is valid."""
|
||||||
SERVER = ldap3.Server(self.configuration.get("auth", "ldap_url"))
|
SERVER = ldap3.Server(self.configuration.get("auth", "ldap_url"))
|
||||||
BASE = self.configuration.get("auth", "ldap_base")
|
BASE = self.configuration.get("auth", "ldap_base")
|
||||||
@ -44,7 +73,7 @@ class Auth(BaseAuth):
|
|||||||
BINDDN = self.configuration.get("auth", "ldap_binddn")
|
BINDDN = self.configuration.get("auth", "ldap_binddn")
|
||||||
PASSWORD = self.configuration.get("auth", "ldap_password")
|
PASSWORD = self.configuration.get("auth", "ldap_password")
|
||||||
SCOPE = self.configuration.get("auth", "ldap_scope")
|
SCOPE = self.configuration.get("auth", "ldap_scope")
|
||||||
SUPPORT_EXTENDED = self.configuration.getboolean("auth", "ldap_support_extended", fallback=True)
|
SUPPORT_EXTENDED = self.configuration.get("auth", "ldap_support_extended")
|
||||||
|
|
||||||
if BINDDN and PASSWORD:
|
if BINDDN and PASSWORD:
|
||||||
conn = ldap3.Connection(SERVER, BINDDN, PASSWORD)
|
conn = ldap3.Connection(SERVER, BINDDN, PASSWORD)
|
||||||
@ -53,18 +82,18 @@ class Auth(BaseAuth):
|
|||||||
conn.bind()
|
conn.bind()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.logger.debug("LDAP whoami: %s" % conn.extend.standard.who_am_i())
|
logger.debug("LDAP whoami: %s" % conn.extend.standard.who_am_i())
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.logger.debug("LDAP error: %s" % err)
|
logger.debug("LDAP error: %s" % err)
|
||||||
|
|
||||||
distinguished_name = "%s=%s" % (ATTRIBUTE, ldap3imports.escape_attribute_value(user))
|
distinguished_name = "%s=%s" % (ATTRIBUTE, ldap3imports.escape_attribute_value(user))
|
||||||
self.logger.debug("LDAP bind for %s in base %s" % (distinguished_name, BASE))
|
logger.debug("LDAP bind for %s in base %s" % (distinguished_name, BASE))
|
||||||
|
|
||||||
if FILTER:
|
if FILTER:
|
||||||
filter_string = "(&(%s)%s)" % (distinguished_name, FILTER)
|
filter_string = "(&(%s)%s)" % (distinguished_name, FILTER)
|
||||||
else:
|
else:
|
||||||
filter_string = distinguished_name
|
filter_string = distinguished_name
|
||||||
self.logger.debug("LDAP filter: %s" % filter_string)
|
logger.debug("LDAP filter: %s" % filter_string)
|
||||||
|
|
||||||
conn.search(search_base=BASE,
|
conn.search(search_base=BASE,
|
||||||
search_scope=SCOPE,
|
search_scope=SCOPE,
|
||||||
@ -76,28 +105,28 @@ class Auth(BaseAuth):
|
|||||||
if users:
|
if users:
|
||||||
user_dn = users[0]['dn']
|
user_dn = users[0]['dn']
|
||||||
uid = users[0]['attributes'][ATTRIBUTE]
|
uid = users[0]['attributes'][ATTRIBUTE]
|
||||||
self.logger.debug("LDAP user %s (%s) found" % (uid, user_dn))
|
logger.debug("LDAP user %s (%s) found" % (uid, user_dn))
|
||||||
try:
|
try:
|
||||||
conn = ldap3.Connection(SERVER, user_dn, password)
|
conn = ldap3.Connection(SERVER, user_dn, password)
|
||||||
conn.bind()
|
conn.bind()
|
||||||
self.logger.debug(conn.result)
|
logger.debug(conn.result)
|
||||||
if SUPPORT_EXTENDED:
|
if SUPPORT_EXTENDED:
|
||||||
whoami = conn.extend.standard.who_am_i()
|
whoami = conn.extend.standard.who_am_i()
|
||||||
self.logger.debug("LDAP whoami: %s" % whoami)
|
logger.debug("LDAP whoami: %s" % whoami)
|
||||||
else:
|
else:
|
||||||
self.logger.debug("LDAP skip extended: call whoami")
|
logger.debug("LDAP skip extended: call whoami")
|
||||||
whoami = conn.result['result'] == 0
|
whoami = conn.result['result'] == 0
|
||||||
if whoami:
|
if whoami:
|
||||||
self.logger.debug("LDAP bind OK")
|
logger.debug("LDAP bind OK")
|
||||||
return True
|
return uid[0]
|
||||||
else:
|
else:
|
||||||
self.logger.debug("LDAP bind failed")
|
logger.debug("LDAP bind failed")
|
||||||
return False
|
return ""
|
||||||
except ldap3.core.exceptions.LDAPInvalidCredentialsResult:
|
except ldap3.core.exceptions.LDAPInvalidCredentialsResult:
|
||||||
self.logger.debug("LDAP invalid credentials")
|
logger.debug("LDAP invalid credentials")
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.logger.debug("LDAP error %s" % err)
|
logger.debug("LDAP error %s" % err)
|
||||||
return False
|
return ""
|
||||||
else:
|
else:
|
||||||
self.logger.debug("LDAP user %s not found" % user)
|
logger.debug("LDAP user %s not found" % user)
|
||||||
return False
|
return ""
|
||||||
|
6
setup.py
6
setup.py
@ -4,9 +4,9 @@ from setuptools import setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="radicale-auth-ldap",
|
name="radicale-auth-ldap",
|
||||||
version="0.1",
|
version="3.0",
|
||||||
description="LDAP Authentication Plugin for Radicale 2",
|
description="LDAP Authentication Plugin for Radicale 3",
|
||||||
author="Raoul Thill",
|
author="Raoul Thill",
|
||||||
license="GNU GPL v3",
|
license="GNU GPL v3",
|
||||||
install_requires=["radicale >= 2.0", "ldap3 >= 2.3"],
|
install_requires=["radicale >= 3.0", "ldap3 >= 2.3"],
|
||||||
packages=["radicale_auth_ldap"])
|
packages=["radicale_auth_ldap"])
|
||||||
|
@ -8,7 +8,8 @@ TEST_CONFIGURATION = {
|
|||||||
'ldap_filter': '(objectClass=person)',
|
'ldap_filter': '(objectClass=person)',
|
||||||
'ldap_binddn': 'cn=xxx,dc=xxx,dc=xx',
|
'ldap_binddn': 'cn=xxx,dc=xxx,dc=xx',
|
||||||
'ldap_password': '',
|
'ldap_password': '',
|
||||||
'ldap_scope': 'LEVEL'
|
'ldap_scope': 'LEVEL',
|
||||||
|
'ldap_support_extended': True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,16 +10,14 @@ from test.util import ConfigMock
|
|||||||
|
|
||||||
class Authentication(unittest.TestCase):
|
class Authentication(unittest.TestCase):
|
||||||
configuration = None
|
configuration = None
|
||||||
logger = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.configuration = ConfigMock(TEST_CONFIGURATION)
|
cls.configuration = ConfigMock(TEST_CONFIGURATION)
|
||||||
cls.logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def test_authentication_works(self):
|
def test_authentication_works(self):
|
||||||
auth = radicale_auth_ldap.Auth(self.__class__.configuration, self.__class__.logger)
|
auth = radicale_auth_ldap.Auth(self.__class__.configuration)
|
||||||
self.assertTrue(auth.is_authenticated(VALID_USER, VALID_PASS))
|
self.assertTrue(auth.login(VALID_USER, VALID_PASS))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -11,3 +11,6 @@ class ConfigMock:
|
|||||||
|
|
||||||
def get(self, a, b):
|
def get(self, a, b):
|
||||||
return self.configuration[a][b]
|
return self.configuration[a][b]
|
||||||
|
|
||||||
|
def copy(self, c):
|
||||||
|
return self
|
||||||
|
Loading…
Reference in New Issue
Block a user