Compare commits

..

No commits in common. "master" and "2-final" have entirely different histories.

8 changed files with 35 additions and 66 deletions

View File

@ -1,12 +1,12 @@
# What is this?
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.
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.
# How to configure
You will need to set a few options inside your radicale config file. Example:
```
[auth]
type = radicale3_auth_ldap
type = radicale_auth_ldap
# LDAP server URL, with protocol and port
ldap_url = ldap://ldap:389

View File

@ -30,41 +30,12 @@ import ldap3
import ldap3.core.exceptions
from radicale.auth import BaseAuth
from radicale.log import logger
import radicale3_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):
def __init__(self, configuration):
super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
def login(self, user, password):
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")
@ -73,7 +44,7 @@ class Auth(BaseAuth):
BINDDN = self.configuration.get("auth", "ldap_binddn")
PASSWORD = self.configuration.get("auth", "ldap_password")
SCOPE = self.configuration.get("auth", "ldap_scope")
SUPPORT_EXTENDED = self.configuration.get("auth", "ldap_support_extended")
SUPPORT_EXTENDED = self.configuration.getboolean("auth", "ldap_support_extended", fallback=True)
if BINDDN and PASSWORD:
conn = ldap3.Connection(SERVER, BINDDN, PASSWORD)
@ -82,18 +53,18 @@ class Auth(BaseAuth):
conn.bind()
try:
logger.debug("LDAP whoami: %s" % conn.extend.standard.who_am_i())
self.logger.debug("LDAP whoami: %s" % conn.extend.standard.who_am_i())
except Exception as err:
logger.debug("LDAP error: %s" % err)
self.logger.debug("LDAP error: %s" % err)
distinguished_name = "%s=%s" % (ATTRIBUTE, ldap3imports.escape_attribute_value(user))
logger.debug("LDAP bind for %s in base %s" % (distinguished_name, BASE))
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
logger.debug("LDAP filter: %s" % filter_string)
self.logger.debug("LDAP filter: %s" % filter_string)
conn.search(search_base=BASE,
search_scope=SCOPE,
@ -105,28 +76,28 @@ class Auth(BaseAuth):
if users:
user_dn = users[0]['dn']
uid = users[0]['attributes'][ATTRIBUTE]
logger.debug("LDAP user %s (%s) found" % (uid, user_dn))
self.logger.debug("LDAP user %s (%s) found" % (uid, user_dn))
try:
conn = ldap3.Connection(SERVER, user_dn, password)
conn.bind()
logger.debug(conn.result)
self.logger.debug(conn.result)
if SUPPORT_EXTENDED:
whoami = conn.extend.standard.who_am_i()
logger.debug("LDAP whoami: %s" % whoami)
self.logger.debug("LDAP whoami: %s" % whoami)
else:
logger.debug("LDAP skip extended: call whoami")
self.logger.debug("LDAP skip extended: call whoami")
whoami = conn.result['result'] == 0
if whoami:
logger.debug("LDAP bind OK")
return uid[0]
self.logger.debug("LDAP bind OK")
return True
else:
logger.debug("LDAP bind failed")
return ""
self.logger.debug("LDAP bind failed")
return False
except ldap3.core.exceptions.LDAPInvalidCredentialsResult:
logger.debug("LDAP invalid credentials")
self.logger.debug("LDAP invalid credentials")
except Exception as err:
logger.debug("LDAP error %s" % err)
return ""
self.logger.debug("LDAP error %s" % err)
return False
else:
logger.debug("LDAP user %s not found" % user)
return ""
self.logger.debug("LDAP user %s not found" % user)
return False

View File

@ -3,10 +3,10 @@
from setuptools import setup
setup(
name="radicale3-auth-ldap",
version="3.0",
description="LDAP Authentication Plugin for Radicale 3",
name="radicale-auth-ldap",
version="0.1",
description="LDAP Authentication Plugin for Radicale 2",
author="Raoul Thill",
license="GNU GPL v3",
install_requires=["radicale >= 3.0", "ldap3 >= 2.3"],
packages=["radicale3_auth_ldap"])
install_requires=["radicale >= 2.0", "ldap3 >= 2.3"],
packages=["radicale_auth_ldap"])

View File

@ -2,7 +2,7 @@
import unittest
import radicale3_auth_ldap
import radicale_auth_ldap
class References(unittest.TestCase):

View File

@ -8,8 +8,7 @@ TEST_CONFIGURATION = {
'ldap_filter': '(objectClass=person)',
'ldap_binddn': 'cn=xxx,dc=xxx,dc=xx',
'ldap_password': '',
'ldap_scope': 'LEVEL',
'ldap_support_extended': True
'ldap_scope': 'LEVEL'
}
}

View File

@ -3,21 +3,23 @@
import logging
import unittest
import radicale3_auth_ldap
import radicale_auth_ldap
from test.configuration import TEST_CONFIGURATION, VALID_USER, VALID_PASS
from test.util import ConfigMock
class Authentication(unittest.TestCase):
configuration = None
logger = None
@classmethod
def setUpClass(cls):
cls.configuration = ConfigMock(TEST_CONFIGURATION)
cls.logger = logging.getLogger(__name__)
def test_authentication_works(self):
auth = radicale3_auth_ldap.Auth(self.__class__.configuration)
self.assertTrue(auth.login(VALID_USER, VALID_PASS))
auth = radicale_auth_ldap.Auth(self.__class__.configuration, self.__class__.logger)
self.assertTrue(auth.is_authenticated(VALID_USER, VALID_PASS))
if __name__ == '__main__':

View File

@ -11,6 +11,3 @@ class ConfigMock:
def get(self, a, b):
return self.configuration[a][b]
def copy(self, c):
return self