2019-07-13 18:45:48 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
|
|
|
# Copyright (C) 2018-2019 OzzieIsaacs, pwr
|
|
|
|
#
|
|
|
|
# This program 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 program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import base64
|
|
|
|
|
|
|
|
from flask_simpleldap import LDAP, LDAPException
|
2020-11-20 18:47:50 +00:00
|
|
|
from flask_simpleldap import ldap as pyLDAP
|
2023-03-26 09:29:54 +00:00
|
|
|
from flask import current_app
|
2019-07-13 18:45:48 +00:00
|
|
|
from .. import constants, logger
|
|
|
|
|
2020-04-13 20:23:58 +00:00
|
|
|
try:
|
|
|
|
from ldap.pkginfo import __version__ as ldapVersion
|
|
|
|
except ImportError:
|
|
|
|
pass
|
2019-07-13 18:45:48 +00:00
|
|
|
|
|
|
|
log = logger.create()
|
|
|
|
|
2024-06-18 18:13:26 +00:00
|
|
|
|
2023-03-26 11:17:02 +00:00
|
|
|
class LDAPLogger(object):
|
|
|
|
|
2024-06-18 18:13:26 +00:00
|
|
|
@staticmethod
|
|
|
|
def write(message):
|
2023-03-26 11:17:02 +00:00
|
|
|
try:
|
|
|
|
log.debug(message.strip("\n").replace("\n", ""))
|
|
|
|
except Exception:
|
|
|
|
log.debug("Logging Error")
|
|
|
|
|
2019-07-13 18:45:48 +00:00
|
|
|
|
2023-03-26 09:29:54 +00:00
|
|
|
class mySimpleLDap(LDAP):
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def init_app(app):
|
|
|
|
super(mySimpleLDap, mySimpleLDap).init_app(app)
|
|
|
|
app.config.setdefault('LDAP_LOGLEVEL', 0)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def initialize(self):
|
|
|
|
"""Initialize a connection to the LDAP server.
|
|
|
|
|
|
|
|
:return: LDAP connection object.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
log_level = 2 if current_app.config['LDAP_LOGLEVEL'] == logger.logging.DEBUG else 0
|
|
|
|
conn = pyLDAP.initialize('{0}://{1}:{2}'.format(
|
|
|
|
current_app.config['LDAP_SCHEMA'],
|
|
|
|
current_app.config['LDAP_HOST'],
|
2023-03-26 11:17:02 +00:00
|
|
|
current_app.config['LDAP_PORT']), trace_level=log_level, trace_file=LDAPLogger())
|
2023-03-26 09:29:54 +00:00
|
|
|
conn.set_option(pyLDAP.OPT_NETWORK_TIMEOUT,
|
|
|
|
current_app.config['LDAP_TIMEOUT'])
|
|
|
|
conn = self._set_custom_options(conn)
|
|
|
|
conn.protocol_version = pyLDAP.VERSION3
|
|
|
|
if current_app.config['LDAP_USE_TLS']:
|
|
|
|
conn.start_tls_s()
|
|
|
|
return conn
|
|
|
|
except pyLDAP.LDAPError as e:
|
|
|
|
raise LDAPException(self.error(e.args))
|
|
|
|
|
|
|
|
|
|
|
|
_ldap = mySimpleLDap()
|
|
|
|
|
2024-06-18 18:13:26 +00:00
|
|
|
|
2019-07-13 18:45:48 +00:00
|
|
|
def init_app(app, config):
|
|
|
|
if config.config_login_type != constants.LOGIN_LDAP:
|
|
|
|
return
|
|
|
|
|
|
|
|
app.config['LDAP_HOST'] = config.config_ldap_provider_url
|
|
|
|
app.config['LDAP_PORT'] = config.config_ldap_port
|
2020-11-22 10:19:14 +00:00
|
|
|
app.config['LDAP_CUSTOM_OPTIONS'] = {pyLDAP.OPT_REFERRALS: 0}
|
2020-04-13 20:23:58 +00:00
|
|
|
if config.config_ldap_encryption == 2:
|
2020-04-05 15:31:41 +00:00
|
|
|
app.config['LDAP_SCHEMA'] = 'ldaps'
|
|
|
|
else:
|
|
|
|
app.config['LDAP_SCHEMA'] = 'ldap'
|
2020-04-17 16:16:21 +00:00
|
|
|
if config.config_ldap_authentication > constants.LDAP_AUTH_ANONYMOUS:
|
|
|
|
if config.config_ldap_authentication > constants.LDAP_AUTH_UNAUTHENTICATE:
|
2022-07-02 15:45:24 +00:00
|
|
|
if config.config_ldap_serv_password_e is None:
|
|
|
|
config.config_ldap_serv_password_e = ''
|
|
|
|
app.config['LDAP_PASSWORD'] = config.config_ldap_serv_password_e
|
2020-04-17 16:16:21 +00:00
|
|
|
else:
|
2022-07-02 15:45:24 +00:00
|
|
|
app.config['LDAP_PASSWORD'] = ""
|
2020-04-17 16:16:21 +00:00
|
|
|
app.config['LDAP_USERNAME'] = config.config_ldap_serv_username
|
|
|
|
else:
|
2020-04-18 09:47:44 +00:00
|
|
|
app.config['LDAP_USERNAME'] = ""
|
2022-07-02 15:45:24 +00:00
|
|
|
app.config['LDAP_PASSWORD'] = ""
|
2020-04-13 20:23:58 +00:00
|
|
|
if bool(config.config_ldap_cert_path):
|
2020-11-22 07:17:00 +00:00
|
|
|
app.config['LDAP_CUSTOM_OPTIONS'].update({
|
2020-11-20 18:47:50 +00:00
|
|
|
pyLDAP.OPT_X_TLS_REQUIRE_CERT: pyLDAP.OPT_X_TLS_DEMAND,
|
|
|
|
pyLDAP.OPT_X_TLS_CACERTFILE: config.config_ldap_cacert_path,
|
|
|
|
pyLDAP.OPT_X_TLS_CERTFILE: config.config_ldap_cert_path,
|
|
|
|
pyLDAP.OPT_X_TLS_KEYFILE: config.config_ldap_key_path,
|
|
|
|
pyLDAP.OPT_X_TLS_NEWCTX: 0
|
2020-11-22 07:17:00 +00:00
|
|
|
})
|
2020-11-20 18:47:50 +00:00
|
|
|
|
2019-07-13 18:45:48 +00:00
|
|
|
app.config['LDAP_BASE_DN'] = config.config_ldap_dn
|
|
|
|
app.config['LDAP_USER_OBJECT_FILTER'] = config.config_ldap_user_object
|
2020-04-05 15:31:41 +00:00
|
|
|
|
|
|
|
app.config['LDAP_USE_TLS'] = bool(config.config_ldap_encryption == 1)
|
|
|
|
app.config['LDAP_USE_SSL'] = bool(config.config_ldap_encryption == 2)
|
2019-07-13 18:45:48 +00:00
|
|
|
app.config['LDAP_OPENLDAP'] = bool(config.config_ldap_openldap)
|
2019-12-28 06:12:18 +00:00
|
|
|
app.config['LDAP_GROUP_OBJECT_FILTER'] = config.config_ldap_group_object_filter
|
|
|
|
app.config['LDAP_GROUP_MEMBERS_FIELD'] = config.config_ldap_group_members_field
|
2023-03-26 09:29:54 +00:00
|
|
|
app.config['LDAP_LOGLEVEL'] = config.config_log_level
|
2020-07-23 17:23:57 +00:00
|
|
|
try:
|
|
|
|
_ldap.init_app(app)
|
2020-11-20 18:47:50 +00:00
|
|
|
except ValueError:
|
|
|
|
if bool(config.config_ldap_cert_path):
|
2020-11-22 07:17:00 +00:00
|
|
|
app.config['LDAP_CUSTOM_OPTIONS'].pop(pyLDAP.OPT_X_TLS_NEWCTX)
|
2020-11-20 18:47:50 +00:00
|
|
|
try:
|
|
|
|
_ldap.init_app(app)
|
|
|
|
except RuntimeError as e:
|
|
|
|
log.error(e)
|
2020-07-23 17:23:57 +00:00
|
|
|
except RuntimeError as e:
|
|
|
|
log.error(e)
|
2019-07-13 18:45:48 +00:00
|
|
|
|
|
|
|
|
2024-06-18 18:13:26 +00:00
|
|
|
def get_object_details(user=None, query_filter=None):
|
2020-12-03 17:51:03 +00:00
|
|
|
return _ldap.get_object_details(user, query_filter=query_filter)
|
2019-12-28 06:12:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
def bind():
|
|
|
|
return _ldap.bind()
|
|
|
|
|
|
|
|
|
|
|
|
def get_group_members(group):
|
|
|
|
return _ldap.get_group_members(group)
|
|
|
|
|
2019-07-13 18:45:48 +00:00
|
|
|
|
|
|
|
def basic_auth_required(func):
|
|
|
|
return _ldap.basic_auth_required(func)
|
|
|
|
|
|
|
|
|
|
|
|
def bind_user(username, password):
|
|
|
|
'''Attempts a LDAP login.
|
|
|
|
|
|
|
|
:returns: True if login succeeded, False if login failed, None if server unavailable.
|
|
|
|
'''
|
|
|
|
try:
|
2020-04-13 20:23:58 +00:00
|
|
|
if _ldap.get_object_details(username):
|
|
|
|
result = _ldap.bind_user(username, password)
|
|
|
|
log.debug("LDAP login '%s': %r", username, result)
|
|
|
|
return result is not None, None
|
|
|
|
return None, None # User not found
|
2020-04-18 09:47:44 +00:00
|
|
|
except (TypeError, AttributeError, KeyError) as ex:
|
2020-04-13 20:23:58 +00:00
|
|
|
error = ("LDAP bind_user: %s" % ex)
|
|
|
|
return None, error
|
2019-07-13 18:45:48 +00:00
|
|
|
except LDAPException as ex:
|
|
|
|
if ex.message == 'Invalid credentials':
|
2020-12-03 13:22:37 +00:00
|
|
|
error = "LDAP admin login failed"
|
2020-04-13 20:23:58 +00:00
|
|
|
return None, error
|
2019-07-13 18:45:48 +00:00
|
|
|
if ex.message == "Can't contact LDAP server":
|
2020-04-13 20:23:58 +00:00
|
|
|
# log.warning('LDAP Server down: %s', ex)
|
|
|
|
error = ('LDAP Server down: %s' % ex)
|
|
|
|
return None, error
|
2019-07-13 18:45:48 +00:00
|
|
|
else:
|
2020-04-13 20:23:58 +00:00
|
|
|
error = ('LDAP Server error: %s' % ex.message)
|
|
|
|
return None, error
|