Rename to radicale3_auth_ldap

This commit is contained in:
Julien Dupouy
2021-04-07 01:09:06 -03:00
parent 0e64ecc229
commit 5ca21d4f46
6 changed files with 8 additions and 8 deletions

View File

@@ -0,0 +1,132 @@
# -*- 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/).
"""
import ldap3
import ldap3.core.exceptions
from radicale.auth import BaseAuth
from radicale.log import logger
import radicale3_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):
"""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")
SUPPORT_EXTENDED = self.configuration.get("auth", "ldap_support_extended")
if BINDDN and PASSWORD:
conn = ldap3.Connection(SERVER, BINDDN, PASSWORD)
else:
conn = ldap3.Connection(SERVER)
conn.bind()
try:
logger.debug("LDAP whoami: %s" % conn.extend.standard.who_am_i())
except Exception as err:
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))
if FILTER:
filter_string = "(&(%s)%s)" % (distinguished_name, FILTER)
else:
filter_string = distinguished_name
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]
logger.debug("LDAP user %s (%s) found" % (uid, user_dn))
try:
conn = ldap3.Connection(SERVER, user_dn, password)
conn.bind()
logger.debug(conn.result)
if SUPPORT_EXTENDED:
whoami = conn.extend.standard.who_am_i()
logger.debug("LDAP whoami: %s" % whoami)
else:
logger.debug("LDAP skip extended: call whoami")
whoami = conn.result['result'] == 0
if whoami:
logger.debug("LDAP bind OK")
return uid[0]
else:
logger.debug("LDAP bind failed")
return ""
except ldap3.core.exceptions.LDAPInvalidCredentialsResult:
logger.debug("LDAP invalid credentials")
except Exception as err:
logger.debug("LDAP error %s" % err)
return ""
else:
logger.debug("LDAP user %s not found" % user)
return ""

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
"""
This code was imported from ldap3, because upstream obviously deprecated this.
Until I come around finding a better solution, this will stay here.
All this code was written by people from the ldap3 project and keeps the original license, of course.
Original header below:
# Created on 2014.09.08
#
# Author: Giovanni Cannata
#
# Copyright 2014, 2015, 2016 Giovanni Cannata
#
# This file is part of ldap3.
#
# ldap3 is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ldap3 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ldap3 in the COPYING and COPYING.LESSER files.
# If not, see <http://www.gnu.org/licenses/>.
"""
from string import hexdigits
STATE_ANY = 0
STATE_ESCAPE = 1
STATE_ESCAPE_HEX = 2
def escape_attribute_value(attribute_value):
if not attribute_value:
return ''
if attribute_value[0] == '#': # with leading SHARP only pairs of hex characters are valid
valid_hex = True
if len(attribute_value) % 2 == 0: # string must be # + HEX HEX (an odd number of chars)
valid_hex = False
if valid_hex:
for c in attribute_value:
if c not in hexdigits: # allowed only hex digits as per RFC 4514
valid_hex = False
break
if valid_hex:
return attribute_value
state = STATE_ANY
escaped = ''
tmp_buffer = ''
for c in attribute_value:
if state == STATE_ANY:
if c == '\\':
state = STATE_ESCAPE
elif c in '"#+,;<=>\00':
escaped += '\\' + c
else:
escaped += c
elif state == STATE_ESCAPE:
if c in hexdigits:
tmp_buffer = c
state = STATE_ESCAPE_HEX
elif c in ' "#+,;<=>\\\00':
escaped += '\\' + c
state = STATE_ANY
else:
escaped += '\\\\' + c
elif state == STATE_ESCAPE_HEX:
if c in hexdigits:
escaped += '\\' + tmp_buffer + c
else:
escaped += '\\\\' + tmp_buffer + c
tmp_buffer = ''
state = STATE_ANY
# final state
if state == STATE_ESCAPE:
escaped += '\\\\'
elif state == STATE_ESCAPE_HEX:
escaped += '\\\\' + tmp_buffer
if escaped[0] == ' ': # leading SPACE must be escaped
escaped = '\\' + escaped
if escaped[-1] == ' ' and len(escaped) > 1 and escaped[-2] != '\\': # trailing SPACE must be escaped
escaped = escaped[:-1] + '\\ '
return escaped