Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ldap.py317 linesDownload Raw Back to authenticate
1##########################################################################2#3# pgAdmin 4 - PostgreSQL Tools4#5# Copyright (C) 2013 - 2024, The pgAdmin Development Team6# This software is released under the PostgreSQL Licence7#8##########################################################################9 10"""A blueprint module implementing the ldap authentication."""11 12import ssl13import config14from ldap3 import Connection, Server, Tls, ALL, ALL_ATTRIBUTES, ANONYMOUS,\15    SIMPLE, AUTO_BIND_TLS_BEFORE_BIND, AUTO_BIND_NO_TLS, set_config_parameter16from ldap3.core.exceptions import LDAPSocketOpenError, LDAPBindError,\17    LDAPInvalidScopeError, LDAPAttributeError, LDAPInvalidFilterError,\18    LDAPStartTLSError, LDAPSSLConfigurationError19from flask_babel import gettext20from urllib.parse import urlparse21 22from .internal import BaseAuthentication23from pgadmin.model import User, ServerGroup, db, Role24from flask import current_app25from pgadmin.tools.user_management import create_user26from pgadmin.utils.constants import LDAP27from sqlalchemy import func28from flask_security import login_user29 30ERROR_SEARCHING_LDAP_DIRECTORY = gettext(31    "Error searching the LDAP directory: {}")32 33ERROR_CONNECTING_LDAP_SERVER = gettext(34    "Error connecting to the LDAP server: {}\n")35 36if config.LDAP_IGNORE_MALFORMED_SCHEMA:37    set_config_parameter('IGNORE_MALFORMED_SCHEMA',38                         config.LDAP_IGNORE_MALFORMED_SCHEMA)39 40 41class LDAPAuthentication(BaseAuthentication):42    """Ldap Authentication Class"""43 44    def get_source_name(self):45        return LDAP46 47    def get_friendly_name(self):48        return gettext("ldap")49 50    def authenticate(self, form):51        self.username = form.data['email']52        self.password = form.data['password']53        self.dedicated_user = True54        self.start_tls = False55        user_email = None56 57        # Check the dedicated ldap user58        self.bind_user = getattr(config, 'LDAP_BIND_USER', None)59        self.bind_pass = getattr(config, 'LDAP_BIND_PASSWORD', None)60 61        # Check for the anonymous binding62        self.anonymous_bind = getattr(config, 'LDAP_ANONYMOUS_BIND', False)63 64        if self.bind_user and not self.bind_pass:65            return False, gettext(66                "LDAP configuration error: Set the bind password.")67 68        # if no dedicated ldap user is configured then use the login69        # username and password70        if not self.bind_user and not self.bind_pass and\71                self.anonymous_bind is False:72 73            user_dn = config.LDAP_BIND_FORMAT\74                .format(75                    LDAP_USERNAME=self.username,76                    LDAP_BASE_DN=config.LDAP_BASE_DN,77                    LDAP_USERNAME_ATTRIBUTE=config.LDAP_USERNAME_ATTRIBUTE78                )79 80            self.bind_user = user_dn81            self.bind_pass = self.password82            self.dedicated_user = False83 84        # Connect ldap server85        status, msg = self.connect()86 87        if not status:88            return status, msg89 90        status, ldap_user = self.search_ldap_user()91 92        if not status:93            return status, ldap_user94 95        # If dedicated user is configured96        if self.dedicated_user:97            # Get the user DN from the user ldap entry98            self.bind_user = ldap_user.entry_dn99            self.bind_pass = self.password100            self.anonymous_bind = False101            status, msg = self.connect()102 103            if not status:104                return status, msg105 106        if 'mail' in ldap_user:107            mail = ldap_user['mail'].value108            if isinstance(mail, list) and len(mail) > 0:109                user_email = mail[0]110            else:111                user_email = ldap_user['mail'].value112 113        return self.__auto_create_user(user_email)114 115    def connect(self):116        """Setup the connection to the LDAP server and authenticate the user.117        """118        status, server = self._configure_server()119 120        if not status:121            return status, server122 123        auto_bind = AUTO_BIND_TLS_BEFORE_BIND if self.start_tls \124            else AUTO_BIND_NO_TLS125 126        # Create the connection127        try:128            if self.anonymous_bind:129                self.conn = Connection(server,130                                       auto_bind=auto_bind,131                                       authentication=ANONYMOUS132                                       )133            else:134                self.conn = Connection(server,135                                       user=self.bind_user,136                                       password=self.bind_pass,137                                       auto_bind=auto_bind,138                                       authentication=SIMPLE139                                       )140 141        except LDAPSocketOpenError as e:142            current_app.logger.exception(143                ERROR_CONNECTING_LDAP_SERVER.format(e))144            return False, ERROR_CONNECTING_LDAP_SERVER.format(e.args[0])145        except LDAPBindError as e:146            current_app.logger.exception(147                "Error binding to the LDAP server.")148            return False, gettext("Error binding to the LDAP server: {}\n".149                                  format(e.args[0]))150        except LDAPStartTLSError as e:151            current_app.logger.exception(152                "Error starting TLS: {}\n".format(e))153            return False, gettext("Error starting TLS: {}\n"154                                  ).format(e.args[0])155        except Exception as e:156            current_app.logger.exception(157                ERROR_CONNECTING_LDAP_SERVER.format(e))158            return False, ERROR_CONNECTING_LDAP_SERVER.format(e.args[0])159 160        return True, None161 162    def login(self, form):163        user = getattr(form, 'user', None)164        if user is None:165            if config.LDAP_DN_CASE_SENSITIVE:166                user = User.query.filter_by(username=self.username).first()167            else:168                user = User.query.filter(169                    func.lower(User.username) == func.lower(170                        self.username)).first()171 172        if user is None:173            current_app.logger.exception(174                self.messages('USER_DOES_NOT_EXIST'))175            return False, self.messages('USER_DOES_NOT_EXIST')176 177        # Login user through flask_security178        status = login_user(user)179        if not status:180            current_app.logger.exception(self.messages('LOGIN_FAILED'))181            return False, self.messages('LOGIN_FAILED')182        current_app.logger.info(183            "LDAP user {0} logged in.".format(user))184        return True, None185 186    def __auto_create_user(self, user_email):187        """Add the ldap user to the internal SQLite database."""188        if config.LDAP_AUTO_CREATE_USER:189            if config.LDAP_DN_CASE_SENSITIVE:190                user = User.query.filter_by(username=self.username).first()191            else:192                user = User.query.filter(193                    func.lower(User.username) == func.lower(194                        self.username)).first()195 196            if user is None:197                create_msg = ("Creating user {0} with email {1} "198                              "from auth source LDAP.")199                current_app.logger.info(create_msg.format(self.username,200                                                          user_email))201                return create_user({202                    'username': self.username,203                    'email': user_email,204                    'role': 2,205                    'active': True,206                    'auth_source': LDAP207                })208 209        return True, None210 211    def __configure_tls(self):212        ca_cert_file = getattr(config, 'LDAP_CA_CERT_FILE', None)213        cert_file = getattr(config, 'LDAP_CERT_FILE', None)214        key_file = getattr(config, 'LDAP_KEY_FILE', None)215        cert_validate = ssl.CERT_NONE216 217        if ca_cert_file and cert_file and key_file:218            cert_validate = ssl.CERT_REQUIRED219 220        try:221            tls = Tls(222                local_private_key_file=key_file,223                local_certificate_file=cert_file,224                validate=cert_validate,225                version=ssl.PROTOCOL_TLSv1_2,226                ca_certs_file=ca_cert_file)227        except LDAPSSLConfigurationError as e:228            current_app.logger.exception(229                "LDAP configuration error: {}\n".format(e))230            return False, gettext("LDAP configuration error: {}\n").format(231                e.args[0])232        return True, tls233 234    def _configure_server(self):235        # Parse the server URI236        uri = getattr(config, 'LDAP_SERVER_URI', None)237 238        if uri:239            uri = urlparse(uri)240 241        # Create the TLS configuration object if required242        tls = None243 244        if isinstance(uri, str):245            return False, gettext(246                "LDAP configuration error: Set the proper LDAP URI.")247 248        if uri.scheme == 'ldaps' or config.LDAP_USE_STARTTLS:249            status, tls = self.__configure_tls()250            if not status:251                return status, tls252 253        if uri.scheme != 'ldaps' and config.LDAP_USE_STARTTLS:254            self.start_tls = True255 256        try:257            # Create the server object258            server = Server(uri.hostname,259                            port=uri.port,260                            use_ssl=(uri.scheme == 'ldaps'),261                            get_info=ALL,262                            tls=tls,263                            connect_timeout=config.LDAP_CONNECTION_TIMEOUT)264        except ValueError as e:265            return False, "LDAP configuration error: {}.".format(e)266 267        return True, server268 269    def search_ldap_user(self):270        """Get a list of users from the LDAP server based on config271         search criteria."""272        try:273            search_base_dn = config.LDAP_SEARCH_BASE_DN274            if (not search_base_dn or search_base_dn == '<Search-Base-DN>')\275                    and (self.anonymous_bind or self.dedicated_user):276                return False, gettext("LDAP configuration error: "277                                      "Set the Search Domain.")278            elif not search_base_dn or search_base_dn == '<Search-Base-DN>':279                search_base_dn = config.LDAP_BASE_DN280 281            search_filter = "({0}={1})".format(config.LDAP_USERNAME_ATTRIBUTE,282                                               self.username)283            if config.LDAP_SEARCH_FILTER:284                search_filter = "(&{0}{1})".format(search_filter,285                                                   config.LDAP_SEARCH_FILTER)286 287            self.conn.search(search_base=search_base_dn,288                             search_filter=search_filter,289                             search_scope=config.LDAP_SEARCH_SCOPE,290                             attributes=ALL_ATTRIBUTES291                             )292 293        except LDAPInvalidScopeError as e:294            current_app.logger.exception(295                ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])296            )297            return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])298        except LDAPAttributeError as e:299            current_app.logger.exception(300                ERROR_SEARCHING_LDAP_DIRECTORY.format(e)301            )302            return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])303        except LDAPInvalidFilterError as e:304            current_app.logger.exception(305                ERROR_SEARCHING_LDAP_DIRECTORY.format(e)306            )307            return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])308 309        results = len(self.conn.entries)310        if results > 1:311            return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(312                gettext("More than one result found."))313        elif results < 1:314            return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(315                gettext("Could not find the specified user."))316        return True, self.conn.entries[0]317 
codekingpro/portable-devtools · Team Ai