Esempio n. 1
0
 def __init__(self, admin_field, admin_value, base_dn, cache_time,
              service_dn, service_password, service_username, url,
              user_search_filter, user_dn_format, ignore_cert,
              ignore_referrals, ignore_multiple_results):
     self._url = url
     self._service_dn = service_dn
     self._service_password = service_password
     self._base_dn = base_dn
     self._user_search_filter = user_search_filter
     self._user_dn_format = user_dn_format
     if user_dn_format is not None:
         if base_dn is not None or user_search_filter is not None:
             raise ValueError("Cannot use user_dn_format with base_dn "
                              "and user_search_filter")
     else:
         if base_dn is None or user_search_filter is None:
             raise ValueError("Must provide user_dn_format or both base_dn "
                              "and user_search_filter")
     self._admin_field = admin_field
     self._admin_value = admin_value
     self._server = None
     if cache_time is not None:
         cache_time = int(cache_time)
     self._cache = TimedCache(cache_time, self._fetch_user)
     if service_username is not None:
         self._cache.set_expire(service_username,
                                User(service_username, service_dn, True),
                                None)
     self._ignore_cert = ignore_cert
     self._ignore_referrals = ignore_referrals
     self._ignore_multiple_results = ignore_multiple_results
Esempio n. 2
0
 def __init__(
     self,
     admin_field,
     admin_group_dn,
     admin_value,
     base_dn,
     cache_time,
     service_dn,
     service_password,
     service_username,
     url,
     user_search_filter,
     user_dn_format,
     ignore_cert,
     ignore_referrals,
     ignore_multiple_results,
 ):
     self._url = url
     self._service_dn = service_dn
     self._service_password = service_password
     self._base_dn = base_dn
     self._user_search_filter = user_search_filter
     self._user_dn_format = user_dn_format
     if user_dn_format is not None:
         if base_dn is not None or user_search_filter is not None:
             raise ValueError(
                 "Cannot use user_dn_format with base_dn " "and user_search_filter"
             )
     else:
         if base_dn is None or user_search_filter is None:
             raise ValueError(
                 "Must provide user_dn_format or both base_dn "
                 "and user_search_filter"
             )
     self._admin_field = admin_field
     self._admin_group_dn = admin_group_dn
     if admin_group_dn and not self._user_dn_format:
         raise ValueError(
             "ldap.admin_group_dn must be used with ldap.user_dn_format"
         )
     self._admin_value = set(admin_value)
     self._server = None
     if cache_time is not None:
         cache_time = int(cache_time)
     self._cache = TimedCache(cache_time, self._fetch_user)
     if service_username is not None:
         self._cache.set_expire(
             service_username, User(service_username, service_dn, True), None
         )
     self._ignore_cert = ignore_cert
     self._ignore_referrals = ignore_referrals
     self._ignore_multiple_results = ignore_multiple_results
     self._admin_member_type = None
Esempio n. 3
0
class LDAP(object):
    """ Handles interactions with the remote LDAP server """
    def __init__(
        self,
        admin_field,
        admin_group_dn,
        admin_value,
        base_dn,
        cache_time,
        service_dn,
        service_password,
        service_username,
        url,
        user_search_filter,
        user_dn_format,
        ignore_cert,
        ignore_referrals,
        ignore_multiple_results,
    ):
        self._url = url
        self._service_dn = service_dn
        self._service_password = service_password
        self._base_dn = base_dn
        self._user_search_filter = user_search_filter
        self._user_dn_format = user_dn_format
        if user_dn_format is not None:
            if base_dn is not None or user_search_filter is not None:
                raise ValueError("Cannot use user_dn_format with base_dn "
                                 "and user_search_filter")
        else:
            if base_dn is None or user_search_filter is None:
                raise ValueError("Must provide user_dn_format or both base_dn "
                                 "and user_search_filter")
        self._admin_field = admin_field
        self._admin_group_dn = admin_group_dn
        if admin_group_dn and not self._user_dn_format:
            raise ValueError(
                "ldap.admin_group_dn must be used with ldap.user_dn_format")
        self._admin_value = set(admin_value)
        self._server = None
        if cache_time is not None:
            cache_time = int(cache_time)
        self._cache = TimedCache(cache_time, self._fetch_user)
        if service_username is not None:
            self._cache.set_expire(service_username,
                                   User(service_username, service_dn, True),
                                   None)
        self._ignore_cert = ignore_cert
        self._ignore_referrals = ignore_referrals
        self._ignore_multiple_results = ignore_multiple_results
        self._admin_member_type = None

    def connect(self):
        """ Initializes the python-ldap module and does the initial bind """
        if self._ignore_cert:
            ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
        if self._ignore_referrals:
            ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
        LOG.debug("LDAP connecting to %s", self._url)
        self._server = ldap.initialize(self._url, bytes_mode=False)
        self._bind_to_service()

    @property
    def admin_member_type(self):
        if self._admin_member_type is None:
            LOG.debug("Fetching admin group %s", self._admin_group_dn)
            try:
                results = self._server.search_s(self._admin_group_dn,
                                                ldap.SCOPE_BASE,
                                                attrlist=["objectClass"])
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
                return "member"
            dn, attributes = results[0]
            classes = [
                self._decode_attribute(x) for x in attributes["objectClass"]
            ]
            if "groupOfUniqueNames" in classes:
                self._admin_member_type = "uniqueMember"
            else:
                self._admin_member_type = "member"
        return self._admin_member_type

    def _bind_to_service(self):
        """ Bind to the service account or anonymous """
        if self._service_dn:
            # bind with the service_dn
            self._server.simple_bind_s(self._service_dn,
                                       self._service_password)
        else:
            # force a connection without binding
            self._server.whoami_s()

    @reconnect
    def test_connection(self):
        """ Binds to service. Will throw if bad connection """
        self._bind_to_service()

    @reconnect
    def _fetch_user(self, username):
        """ Fetch a user entry from the LDAP server """
        LOG.debug("LDAP fetching user %s", username)
        search_attrs = []
        if self._admin_field is not None:
            search_attrs.append(self._admin_field)
        if self._user_dn_format is not None:
            dn = self._user_dn_format.format(username=username)
            LOG.debug("LDAP searching user %r with dn %r", username, dn)
            try:
                results = self._server.search_s(dn,
                                                ldap.SCOPE_BASE,
                                                attrlist=search_attrs)
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
                return
        else:
            search_filter = self._user_search_filter.format(username=username)
            LOG.debug("LDAP searching user %r with filter %r", username,
                      search_filter)
            try:
                results = self._server.search_s(self._base_dn,
                                                ldap.SCOPE_SUBTREE,
                                                search_filter, search_attrs)
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
                return
            except ldap.NO_RESULTS_RETURNED as e:
                LOG.debug("NO_RESULTS_RETURNED %s", e)
                return
        if not results:
            LOG.debug("LDAP user %r not found", username)
            return None
        if len(results) > 1:
            err_msg = "More than one user found for %r: %r" % (
                username,
                [r[0] for r in results],
            )
            if self._ignore_multiple_results:
                LOG.warning(err_msg)
            else:
                raise ValueError(err_msg)
        dn, attributes = results[0]
        LOG.debug("dn: %r, attributes %r", dn, attributes)

        is_admin = False
        if self._admin_field is not None:
            if self._admin_field in attributes:
                is_admin = bool(
                    self._admin_value.intersection([
                        self._decode_attribute(x)
                        for x in attributes[self._admin_field]
                    ]))
        if not is_admin and self._admin_group_dn:
            user_dn = self._user_dn_format.format(username=username)
            search_filter = "(%s=%s)" % (self.admin_member_type, user_dn)
            LOG.debug("Searching admin group %s for %s", self._admin_group_dn,
                      search_filter)
            try:
                results = self._server.search_s(self._admin_group_dn,
                                                ldap.SCOPE_BASE, search_filter)
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
            else:
                is_admin = bool(results)

        return User(username, dn, is_admin)

    def _decode_attribute(self, attribute):
        if attribute and hasattr(attribute, "decode"):
            decoded = attribute.decode("utf-8")
            return decoded
        else:
            return attribute

    def get_user(self, username):
        """ Get the User object or None """
        return self._cache.get(username)

    @reconnect
    def verify_user(self, username, password):
        """
        Attempts to bind as the user, then rebinds as service user again
        """
        LOG.debug("LDAP verifying user %s", username)
        # Empty password may successfully complete an anonymous bind.
        # Explicitly disallow empty passwords.
        if password == "":
            return False
        user = self._cache.get(username)
        if user is None:
            return False

        try:
            LOG.debug("LDAP binding user %r", user.dn)
            self._server.simple_bind_s(user.dn, password)
        except ldap.INVALID_CREDENTIALS:
            return False
        else:
            return True
        finally:
            self._bind_to_service()
Esempio n. 4
0
class LDAP(object):
    """ Handles interactions with the remote LDAP server """

    def __init__(
        self,
        admin_field,
        admin_group_dn,
        admin_value,
        base_dn,
        cache_time,
        service_dn,
        service_password,
        service_username,
        url,
        user_search_filter,
        user_dn_format,
        ignore_cert,
        ignore_referrals,
        ignore_multiple_results,
    ):
        self._url = url
        self._service_dn = service_dn
        self._service_password = service_password
        self._base_dn = base_dn
        self._user_search_filter = user_search_filter
        self._user_dn_format = user_dn_format
        if user_dn_format is not None:
            if base_dn is not None or user_search_filter is not None:
                raise ValueError(
                    "Cannot use user_dn_format with base_dn " "and user_search_filter"
                )
        else:
            if base_dn is None or user_search_filter is None:
                raise ValueError(
                    "Must provide user_dn_format or both base_dn "
                    "and user_search_filter"
                )
        self._admin_field = admin_field
        self._admin_group_dn = admin_group_dn
        if admin_group_dn and not self._user_dn_format:
            raise ValueError(
                "ldap.admin_group_dn must be used with ldap.user_dn_format"
            )
        self._admin_value = set(admin_value)
        self._server = None
        if cache_time is not None:
            cache_time = int(cache_time)
        self._cache = TimedCache(cache_time, self._fetch_user)
        if service_username is not None:
            self._cache.set_expire(
                service_username, User(service_username, service_dn, True), None
            )
        self._ignore_cert = ignore_cert
        self._ignore_referrals = ignore_referrals
        self._ignore_multiple_results = ignore_multiple_results
        self._admin_member_type = None

    def connect(self):
        """ Initializes the python-ldap module and does the initial bind """
        if self._ignore_cert:
            ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
        if self._ignore_referrals:
            ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
        LOG.debug("LDAP connecting to %s", self._url)
        self._server = ldap.initialize(self._url, bytes_mode=False)
        self._bind_to_service()

    @property
    def admin_member_type(self):
        if self._admin_member_type is None:
            LOG.debug("Fetching admin group %s", self._admin_group_dn)
            try:
                results = self._server.search_s(
                    self._admin_group_dn, ldap.SCOPE_BASE, attrlist=["objectClass"]
                )
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
                return "member"
            dn, attributes = results[0]
            classes = [self._decode_attribute(x) for x in attributes["objectClass"]]
            if "groupOfUniqueNames" in classes:
                self._admin_member_type = "uniqueMember"
            else:
                self._admin_member_type = "member"
        return self._admin_member_type

    def _bind_to_service(self):
        """ Bind to the service account or anonymous """
        if self._service_dn:
            # bind with the service_dn
            self._server.simple_bind_s(self._service_dn, self._service_password)
        else:
            # force a connection without binding
            self._server.whoami_s()

    @reconnect
    def test_connection(self):
        """ Binds to service. Will throw if bad connection """
        self._bind_to_service()

    @reconnect
    def _fetch_user(self, username):
        """ Fetch a user entry from the LDAP server """
        LOG.debug("LDAP fetching user %s", username)
        search_attrs = []
        if self._admin_field is not None:
            search_attrs.append(self._admin_field)
        if self._user_dn_format is not None:
            dn = self._user_dn_format.format(username=username)
            LOG.debug("LDAP searching user %r with dn %r", username, dn)
            try:
                results = self._server.search_s(
                    dn, ldap.SCOPE_BASE, attrlist=search_attrs
                )
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
                return
        else:
            search_filter = self._user_search_filter.format(username=username)
            LOG.debug("LDAP searching user %r with filter %r", username, search_filter)
            try:
                results = self._server.search_s(
                    self._base_dn, ldap.SCOPE_SUBTREE, search_filter, search_attrs
                )
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
                return
            except ldap.NO_RESULTS_RETURNED as e:
                LOG.debug("NO_RESULTS_RETURNED %s", e)
                return
        if not results:
            LOG.debug("LDAP user %r not found", username)
            return None
        if len(results) > 1:
            err_msg = "More than one user found for %r: %r" % (
                username,
                [r[0] for r in results],
            )
            if self._ignore_multiple_results:
                LOG.warning(err_msg)
            else:
                raise ValueError(err_msg)
        dn, attributes = results[0]
        LOG.debug("dn: %r, attributes %r", dn, attributes)

        is_admin = False
        if self._admin_field is not None:
            if self._admin_field in attributes:
                is_admin = bool(
                    self._admin_value.intersection(
                        [
                            self._decode_attribute(x)
                            for x in attributes[self._admin_field]
                        ]
                    )
                )
        if not is_admin and self._admin_group_dn:
            user_dn = self._user_dn_format.format(username=username)
            search_filter = "(%s=%s)" % (self.admin_member_type, user_dn)
            LOG.debug(
                "Searching admin group %s for %s", self._admin_group_dn, search_filter
            )
            try:
                results = self._server.search_s(
                    self._admin_group_dn, ldap.SCOPE_BASE, search_filter
                )
            except ldap.NO_SUCH_OBJECT as e:
                LOG.debug("NO_SUCH_OBJECT %s", e)
            else:
                is_admin = bool(results)

        return User(username, dn, is_admin)

    def _decode_attribute(self, attribute):
        if attribute and hasattr(attribute, "decode"):
            decoded = attribute.decode("utf-8")
            return decoded
        else:
            return attribute

    def get_user(self, username):
        """ Get the User object or None """
        return self._cache.get(username)

    @reconnect
    def verify_user(self, username, password):
        """
        Attempts to bind as the user, then rebinds as service user again
        """
        LOG.debug("LDAP verifying user %s", username)
        # Empty password may successfully complete an anonymous bind.
        # Explicitly disallow empty passwords.
        if password == "":
            return False
        user = self._cache.get(username)
        if user is None:
            return False

        try:
            LOG.debug("LDAP binding user %r", user.dn)
            self._server.simple_bind_s(user.dn, password)
        except ldap.INVALID_CREDENTIALS:
            return False
        else:
            return True
        finally:
            self._bind_to_service()
Esempio n. 5
0
class LDAP(object):
    """ Handles interactions with the remote LDAP server """
    def __init__(self, admin_field, admin_value, base_dn, cache_time,
                 service_dn, service_password, service_username, url,
                 user_search_filter, user_dn_format, ignore_cert,
                 ignore_referrals, ignore_multiple_results):
        self._url = url
        self._service_dn = service_dn
        self._service_password = service_password
        self._base_dn = base_dn
        self._user_search_filter = user_search_filter
        self._user_dn_format = user_dn_format
        if user_dn_format is not None:
            if base_dn is not None or user_search_filter is not None:
                raise ValueError("Cannot use user_dn_format with base_dn "
                                 "and user_search_filter")
        else:
            if base_dn is None or user_search_filter is None:
                raise ValueError("Must provide user_dn_format or both base_dn "
                                 "and user_search_filter")
        self._admin_field = admin_field
        self._admin_value = admin_value
        self._server = None
        if cache_time is not None:
            cache_time = int(cache_time)
        self._cache = TimedCache(cache_time, self._fetch_user)
        if service_username is not None:
            self._cache.set_expire(service_username,
                                   User(service_username, service_dn, True),
                                   None)
        self._ignore_cert = ignore_cert
        self._ignore_referrals = ignore_referrals
        self._ignore_multiple_results = ignore_multiple_results

    def connect(self):
        """ Initializes the python-ldap module and does the initial bind """
        if self._ignore_cert:
            ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
        if self._ignore_referrals:
            ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
        LOG.debug("LDAP connecting to %s", self._url)
        self._server = ldap.initialize(self._url)
        self._bind_to_service()

    def _bind_to_service(self):
        """ Bind to the service account or anonymous """
        if self._service_dn:
            # bind with the service_dn
            self._server.simple_bind_s(self._service_dn,
                                       self._service_password)
        else:
            # force a connection without binding
            self._server.whoami_s()

    @reconnect
    def _fetch_user(self, username):
        """ Fetch a user entry from the LDAP server """
        LOG.debug("LDAP fetching user %s", username)
        search_attrs = []
        if self._admin_field is not None:
            search_attrs.append(self._admin_field)
        if self._user_dn_format is not None:
            dn = self._user_dn_format.format(username=username)
            LOG.debug("LDAP searching user %r with dn %r", username, dn)
            results = self._server.search_s(dn,
                                            ldap.SCOPE_BASE,
                                            attrlist=search_attrs)
        else:
            search_filter = self._user_search_filter.format(username=username)
            LOG.debug("LDAP searching user %r with filter %r", username,
                      search_filter)
            results = self._server.search_s(
                self._base_dn,
                ldap.SCOPE_SUBTREE,
                search_filter,
                search_attrs,
            )
        if not results:
            LOG.debug("LDAP user %r not found", username)
            return None
        if len(results) > 1:
            err_msg = "More than one user found for %r: %r" % (
                username, [r[0] for r in results])
            if self._ignore_multiple_results:
                LOG.warning(err_msg)
            else:
                raise ValueError(err_msg)
        dn, attributes = results[0]

        is_admin = False
        if self._admin_field is not None:
            if self._admin_field in attributes:
                is_admin = any((val in attributes[self._admin_field]
                                for val in self._admin_value))

        return User(username, dn, is_admin)

    def get_user(self, username):
        """ Get the User object or None """
        return self._cache.get(username)

    @reconnect
    def verify_user(self, username, password):
        """
        Attempts to bind as the user, then rebinds as service user again
        """
        LOG.debug("LDAP verifying user %s", username)
        # Empty password may successfully complete an anonymous bind.
        # Explicitly disallow empty passwords.
        if password == "":
            return False
        user = self._cache.get(username)
        if user is None:
            return False

        try:
            LOG.debug("LDAP binding user %r", user.dn)
            self._server.simple_bind_s(user.dn, password)
        except ldap.INVALID_CREDENTIALS:
            return False
        else:
            return True
        finally:
            self._bind_to_service()