keystone.identity.backends.ldap.common 模块¶
- class keystone.identity.backends.ldap.common.AsynchronousMessage(message_id, connection, context_manager)[source]¶
基类:
object用于处理异步 LDAP 响应的容器。
某些 LDAP API,例如 search_ext,是异步的,并在服务器成功启动操作时返回消息 ID。客户端可以使用此消息 ID 和原始连接来使用 result3 请求获取结果。
此对象保存消息 ID、原始连接以及一个可调用弱引用 Finalizer,用于清理与消息 ID 关联的连接特定的上下文管理器。
- 参数:
message_id – 消息标识符 (str)。
connection – 与消息标识符关联的连接 (ldappool.StateConnector)。
“clean”属性是一个可调用对象,用于清理用于创建或返回连接对象的上下文管理器 (weakref.finalize)。
- class keystone.identity.backends.ldap.common.BaseLdap(conf)[source]¶
基类:
object- DEFAULT_EXTRA_ATTR_MAPPING: list[str] = []¶
- DEFAULT_FILTER: str | None = None¶
- DEFAULT_ID_ATTR: str = 'cn'¶
- DEFAULT_OBJECTCLASS: str¶
- DEFAULT_OU: str¶
- DEFAULT_STRUCTURAL_CLASSES: list[str]¶
- add_member(member_dn, member_list_dn)[source]¶
将成员添加到成员列表中。
- 参数:
member_dn – 要添加的成员的 DN。
member_list_dn – 要将成员添加到的组的 DN。
- 引发:
keystone.exception.Conflict – 如果用户已经是成员。
self.NotFound – 如果组条目不存在。
- attribute_ignore: list[str] = []¶
- attribute_options_names: dict[str, str] = {}¶
- filter_query(hints, query=None)[source]¶
应用过滤器到查询。
- 参数:
hints – 包含过滤器列表,可能为 None,表示没有要应用的过滤器。如果它不是 None,则满足此处的任何过滤器将被删除,以便调用者知道是否仍有过滤器需要应用。
query – 要包含过滤器的 LDAP 查询
- 返回 query:
已更新任何满足过滤器的 LDAP 查询
- immutable_attrs: list[str] = []¶
- notfound_arg: str | None = None¶
- options_name: str | None = None¶
- tree_dn: str | None = None¶
- class keystone.identity.backends.ldap.common.EnabledEmuMixIn(conf)[source]¶
基类:
BaseLdap如果启用,则模拟布尔“enabled”属性。
创建一个包含所有已启用类别的组,所有缺失的类别都被视为禁用的。
选项
$name_enabled_emulation - 布尔值,启用/禁用
$name_enabled_emulation_dn - 该组的 DN,默认值为 cn=enabled_${name}s,${tree_dn}
$name_enabled_emulation_use_group_config - 布尔值,启用/禁用
其中 ${name}s 是 self.options_name 的复数形式(“users”或“tenants”),${tree_dn} 是 self.tree_dn。
- DEFAULT_GROUP_MEMBERS_ARE_IDS = False¶
- DEFAULT_GROUP_OBJECTCLASS = 'groupOfNames'¶
- DEFAULT_MEMBER_ATTRIBUTE = 'member'¶
- class keystone.identity.backends.ldap.common.KeystoneLDAPHandler(conn=None)[source]¶
基础:
LDAPHandler转换数据类型并执行日志记录。
此 LDAP 接口包装了基于 python-ldap 的接口。python-ldap 接口需要用 UTF-8 编码的字符串值,除了 [1] 之外。在撰写本文时,OpenStack 日志框架无法接受用 UTF-8 编码的字符串,如果字符串中出现非 ASCII 字符,日志函数将抛出解码错误。
[1] 在 python-ldap 中,某些字段(DN、RDN、属性名称、查询)表示为文本(Python 3 上的 str,当 bytes_mode=False 时,Python 2 上的 unicode)。有关更多详细信息,请参见:http://www.python-ldap.org/en/2025.2/bytes_mode.html#bytes-mode
在调用之前,Python 数据类型将转换为 LDAP API 所需的字符串表示形式。
然后执行日志记录,以便我们可以跟踪发送/接收到 LDAP 的内容。日志记录还会过滤掉敏感安全项目(例如密码)。
然后将字符串值编码为 UTF-8。
然后调用 LDAP API 入口点。
从 LDAP 调用返回的数据将从 UTF-8 编码的字符串转换回 OpenStack 内部使用的 Python 数据类型。
- connect(url, page_size=0, alias_dereferencing=None, use_tls=False, tls_cacertfile=None, tls_cacertdir=None, tls_req_cert=2, chase_referrals=None, debug_level=None, conn_timeout=None, use_pool=None, pool_size=None, pool_retry_max=None, pool_retry_delay=None, pool_conn_timeout=None, pool_conn_lifetime=None)[source]¶
- class keystone.identity.backends.ldap.common.LDAPHandler(conn=None)[source]¶
基类:
object抽象类,定义 LDAP API 提供程序的接口。
本地 Keystone 值不能直接传递到 python-ldap API 中和从中传递。类型转换必须发生在 LDAP API 边界处,类型转换的示例是
布尔值映射到字符串“TRUE”和“FALSE”
整数值映射到其字符串表示形式。
unicode 字符串编码为 UTF-8
请注意,在 python-ldap 中,某些字段(DN、RDN、属性名称、查询)表示为文本(Python 3 上的 str,当 bytes_mode=False 时,Python 2 上的 unicode)。有关更多详细信息,请参见:http://www.python-ldap.org/en/2025.2/bytes_mode.html#bytes-mode
除了处理 API 边界处的类型转换之外,我们还要求支持多个 LDAP API 提供程序。目前我们有
python-ldap,这是 Python 的标准 LDAP API,它需要访问实时 LDAP 服务器。
Fake LDAP 模拟 python-ldap。这用于测试,无需实时 LDAP 服务器。
为了支持这些要求,我们需要一个执行类型转换然后调用另一个可配置的 LDAP API(例如,python-ldap 或模拟)的层。
由于日志记录模块的限制,我们还有额外的约束。日志记录模块无法接受 UTF-8 编码的字符串,它将抛出编码异常。因此,所有日志记录都必须在 UTF-8 转换之前执行。这意味着不能在实现 python-ldap API 的 LDAP API 中执行任何日志记录,因为这些 API 被定义为仅接受 UTF-8 字符串。因此,执行类型转换的层也必须执行日志记录。我们分两步执行类型转换,首先将所有 Python 类型转换为 unicode 字符串,然后记录,然后将 unicode 字符串转换为 UTF-8。
可以采用多种方法来实现这一点,我们选择使用链式技术,其中此类实例简单地通过“conn”属性调用链中的下一个成员。通过在实例化类时将现有此类实例作为 conn 属性传递来构建链。
以下是为什么不使用其他可能方法的简要说明
子类化
为了以正确的顺序执行包装操作,类型转换类需要继承每个 API 提供者。这样做很笨拙,会将类数量翻倍,并且可扩展性差。它要求类型转换类了解所有可能的 API 提供者。
装饰器
装饰器提供了一种优雅的解决方案来包装方法,并且是执行类型转换的一种理想方式,在调用被包装的函数之前执行类型转换,然后在被包装的函数返回后转换值。但是,装饰器需要了解方法签名,它必须知道需要转换哪些输入参数以及如何转换结果。对于像 python-ldap 这样具有大量不同方法签名的 API,它需要大量的专用装饰器。经验表明,由于固有的复杂性和剪切粘贴代码的倾向,很容易应用错误的装饰器。另一种选择是参数化装饰器使其“智能”。经验表明,这样的装饰器会变得极其复杂且难以理解和调试。此外,装饰器往往会隐藏方法调用时真正发生的事情,在查看装饰方法的实现时,执行的操作不可见,经验也表明这会导致错误。
链式调用简化了执行类型转换的包装以及替代 API 提供者的替换。只需创建一个 API 接口的新实例,并将其插入到链的前面。类型转换是显式的且明显的。
如果需要向 API 接口添加新方法,则将其添加到抽象类定义中。如果遗漏将新方法添加到抽象类的任何派生类中,代码将无法加载和运行,从而不可能忘记更新所有派生类。
- abstract connect(url, page_size=0, alias_dereferencing=None, use_tls=False, tls_cacertfile=None, tls_cacertdir=None, tls_req_cert=2, chase_referrals=None, debug_level=None, conn_timeout=None, use_pool=None, pool_size=None, pool_retry_max=None, pool_retry_delay=None, pool_conn_timeout=None, pool_conn_lifetime=None)[source]¶
- class keystone.identity.backends.ldap.common.PooledLDAPHandler(conn=None, use_auth_pool=False)[source]¶
基础:
LDAPHandler使用池连接管理器的 LDAPHandler 实现。
池的特定配置在 [ldap] 部分中定义。所有其他 LDAP 配置仍然从 [ldap] 部分使用
Keystone LDAP 身份验证逻辑使用其 DN 和密码通过 LDAP 绑定来验证最终用户,以建立提供的密码是否正确。这会迅速填满池(因为池基于其绑定数据重用现有连接),并且不会为其他 LDAP 操作留下池中的空间。现在,当启用相关标志“use_auth_pool”时,可以为这些请求建立一个单独的池。该池可以有自己的大小和连接生命周期。其他池属性在这些池之间共享。如果禁用“use_pool”,则“use_auth_pool”无关紧要。如果未启用“use_auth_pool”,则对于这些 LDAP 操作不使用连接池。
请注意,python-ldap API 要求所有字符串属性值都采用 UTF-8 编码。KeystoneLDAPHandler 在调用此类中的方法之前强制执行此操作。
请注意,在 python-ldap 中,某些字段(DN、RDN、属性名称、查询)表示为文本(Python 3 上的 str,当 bytes_mode=False 时,Python 2 上的 unicode)。有关更多详细信息,请参见:http://www.python-ldap.org/en/2025.2/bytes_mode.html#bytes-mode
- Connector¶
别名
StateConnector
- auth_pool_prefix = 'auth_pool_'¶
- connect(url, page_size=0, alias_dereferencing=None, use_tls=False, tls_cacertfile=None, tls_cacertdir=None, tls_req_cert=2, chase_referrals=None, debug_level=None, conn_timeout=None, use_pool=None, pool_size=None, pool_retry_max=None, pool_retry_delay=None, pool_conn_timeout=None, pool_conn_lifetime=None)[source]¶
- connection_pools: dict = {}¶
- result3(message, all=1, timeout=None, resp_ctrl_classes=None)[source]¶
等待并返回异步消息的结果。
此方法返回先前由 LDAP 异步操作例程(例如 search_ext())启动的操作的结果。python-ldap 中的 search_ext() 方法在 LDAP 服务器成功启动操作后返回一个调用标识符或消息 ID。
期望 message 是 AsynchronousMessage 类的实例,其中包含消息 ID 和用于发出原始请求的连接。
当调用 message.clean() 时,与 search_ext() 关联的连接和上下文管理器将被清理。
- search_ext(base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0, serverctrls=None, clientctrls=None, timeout=-1, sizelimit=0)[source]¶
返回一个 AsynchronousMessage 实例,它是异步 API。
AsynchronousMessage 实例可以安全地在 result3() 中调用。
为了以可预测的方式使用 result3() API,需要使用最初提供 msgid 的相同 LDAP 连接。因此,此方法将现有的连接和 msgid 包装在一个新的 AsynchronousMessage 实例中。在 result3() 获取与 msgid 关联的数据后,与 search_ext() 关联的连接将被释放。
- class keystone.identity.backends.ldap.common.PythonLDAPHandler(conn=None)[source]¶
基础:
LDAPHandlerLDAPHandler 实现,它调用 python-ldap API。
注意,python-ldap API 要求所有字符串属性值都必须使用 UTF-8 编码。
请注意,在 python-ldap 中,某些字段(DN、RDN、属性名称、查询)表示为文本(Python 3 上的 str,当 bytes_mode=False 时,Python 2 上的 unicode)。有关更多详细信息,请参见:http://www.python-ldap.org/en/2025.2/bytes_mode.html#bytes-mode
KeystoneLDAPHandler 在调用此类的这些方法之前强制执行此操作。
- connect(url, page_size=0, alias_dereferencing=None, use_tls=False, tls_cacertfile=None, tls_cacertdir=None, tls_req_cert=2, chase_referrals=None, debug_level=None, conn_timeout=None, use_pool=None, pool_size=None, pool_retry_max=None, pool_retry_delay=None, pool_conn_timeout=None, pool_conn_lifetime=None)[source]¶
- convert_ldap_result(ldap_result)[source]¶
将 LDAP 搜索结果转换为 OpenStack 使用的 Python 类型。
每个结果元组的形式为 (dn, attrs),其中 dn 是包含条目区分名称 (DN) 的字符串,attrs 是一个字典,其中包含与条目关联的属性。attrs 的键是字符串,关联的值是字符串列表。
OpenStack 希望使用它选择的 Python 类型。字符串将是 unicode,真值是布尔值,整数是 int,等等。DN 在 python-ldap 中默认情况下以文本形式表示,适用于 Python 3,并且当 Python 2 的 bytes_mode=False 时,因此不需要解码。
- 参数:
ldap_result – LDAP 搜索结果
- 返回值:
包含 (dn, attrs) 的 2 元组列表,其中 dn 是 unicode,attrs 是值类型转换为 OpenStack 首选类型的字典。
- keystone.identity.backends.ldap.common.dn_startswith(descendant_dn, dn)[source]¶
如果且仅当 descendant_dn 在 dn 下方时,返回 True。
- 参数:
descendant_dn – 可能是字符串 DN 或由 ldap.dn.str2dn 解析的 DN。
dn – 可能是字符串 DN 或由 ldap.dn.str2dn 解析的 DN。
- keystone.identity.backends.ldap.common.filter_entity(entity_ref)[source]¶
过滤实体字典中的私有项。
- 参数:
entity_ref – 实体字典。将移除 ‘dn’ 字段。‘dn’ 用于 LDAP,但不应返回给用户。此值可能会被修改。
- 返回值:
entity_ref
- keystone.identity.backends.ldap.common.is_ava_value_equal(attribute_type, val1, val2)[source]¶
仅当 AVAs 相等时返回 True。
在比较 AVAs 时,应考虑属性类型的相等匹配规则。为简单起见,此实现执行不区分大小写的比较。
请注意,此函数使用 prep_case_insenstive,因此该函数的限制也适用于此处。
- keystone.identity.backends.ldap.common.is_dn_equal(dn1, dn2)[source]¶
仅当 DNs 相等时返回 True。
如果 DN 具有相同数量的 RDN,并且在每个位置的 RDN 相同,则两个 DN 相等。请参阅 RFC4517。
请注意,此函数使用 is_rdn_equal 比较 RDN,因此该函数的限制也适用于此处。
- 参数:
dn1 – 字符串 DN 或由 ldap.dn.str2dn 解析的 DN。
dn2 – 字符串 DN 或由 ldap.dn.str2dn 解析的 DN。
- keystone.identity.backends.ldap.common.is_rdn_equal(rdn1, rdn2)[source]¶
仅当 RDNs 相等时返回 True。
RDNs 必须具有相同数量的 AVAs。
RDNs 的每个 AVA 必须与相同属性类型相等。顺序不重要。请注意,一个属性类型仅在一个 RDN 中的一个 AVA 中,否则 DN 无效。
属性类型不区分大小写。请注意,属性类型比较比此处实现的更复杂。此函数仅比较不区分大小写。代码应处理属性类型的多个名称(例如,cn、commonName 和 2.5.4.3 相同)。
请注意,此函数使用 is_ava_value_equal 比较 AVAs,因此该函数的限制也适用于此处。
- keystone.identity.backends.ldap.common.ldap2py(val)[source]¶
将 LDAP 格式的值转换为 OpenStack 使用的 Python 类型。
几乎所有 LDAP 值都存储为 UTF-8 编码的字符串。OpenStack 倾向于使用 Unicode 友好的值。
- 参数:
val – LDAP 格式的值
- 返回值:
val 转换为首选的 Python 类型
- keystone.identity.backends.ldap.common.prep_case_insensitive(value)[source]¶
准备字符串进行不区分大小写的比较。
这在 RFC4518 中定义。为简单起见,此函数所做的一切都是将所有字符转换为小写,删除前导和尾随空格,并将空格序列压缩为单个空格。
- keystone.identity.backends.ldap.common.py2ldap(val)[source]¶
将 Python 值转换为 LDAP 接受的类型(unicode)。
LDAP API 仅接受值的字符串,因此将该值的类型转换为 Unicode 字符串。后续类型转换将根据 python-ldap API 的要求将 Unicode 编码为 UTF-8,但现在我们只需要该值的字符串表示形式。
- 参数:
val – 要转换为 LDAP 字符串表示形式的值
- 返回值:
值的 Unicode 字符串表示形式。
- keystone.identity.backends.ldap.common.use_conn_pool(func)[source]¶
仅对连接池特定的 LDAP API 使用此项。
这会将连接对象作为被装饰 API 之后的下一个参数添加到其中。