在使用Python ldap3库操作LDAP目录服务时,修改用户属性、组属性等操作时经常会出现属性只读的错误提示,这类问题大多和操作的语法规范、连接权限配置有关,需要按照正确的流程处理才能避免错误。

常见的只读错误触发场景
首先我们需要明确哪些情况会触发LDAP属性只读的错误,常见场景主要有以下几类:
- 使用了错误的修改方法,比如直接对条目对象的属性赋值后调用保存方法,而不是使用专门的修改接口
- 连接LDAP时使用的账号没有对应属性的写权限,只能读取属性内容
- 尝试修改LDAP中内置的只读属性,比如条目的
entryUUID、createTimestamp这类系统自动生成的属性 - 修改操作的语法不符合ldap3库的要求,导致服务端判定为非法修改请求从而返回只读错误
ldap3库修改属性的正确流程
1. 建立有写权限的LDAP连接
首先连接LDAP服务时,需要使用具备对应条目写权限的账号,同时确认连接的协议和端口正确,如果是修改操作建议使用ldap3.Server配置时开启正确的策略:
from ldap3 import Server, Connection, ALL
# 配置LDAP服务器,使用ALL策略获取完整 schema 信息
server = Server('ldap://127.0.0.1', get_info=ALL)
# 使用有写权限的管理员账号连接
conn = Connection(
server,
user='cn=admin,dc=example,dc=com',
password='admin_password',
auto_bind=True
)
2. 使用正确的修改方法
ldap3库提供了modify方法专门用于修改条目的属性,该方法需要传入条目的DN和属性修改的字典,修改字典的格式为{属性名: [(操作类型, [值1, 值2])]},操作类型支持MODIFY_ADD、MODIFY_DELETE、MODIFY_REPLACE三种:
from ldap3 import MODIFY_REPLACE
# 要修改的条目DN
user_dn = 'uid=test_user,ou=people,dc=example,dc=com'
# 修改属性,这里替换用户的邮箱属性
changes = {
'mail': [(MODIFY_REPLACE, ['new_email@ipipp.com'])]
}
# 执行修改操作
result = conn.modify(user_dn, changes)
if result:
print('属性修改成功')
else:
print(f'修改失败,错误信息:{conn.result}')
3. 多属性批量修改示例
如果需要同时修改多个属性,只需要在changes字典中添加对应的键值对即可,比如同时修改用户的手机号和显示名称:
from ldap3 import MODIFY_REPLACE, MODIFY_ADD
user_dn = 'uid=test_user,ou=people,dc=example,dc=com'
changes = {
# 替换显示名称
'displayName': [(MODIFY_REPLACE, ['测试用户新名称'])],
# 新增手机号属性,如果属性已存在可以用MODIFY_REPLACE
'mobile': [(MODIFY_ADD, ['13800138000'])]
}
result = conn.modify(user_dn, changes)
print(conn.result)
常见错误排查方法
如果按照上述流程操作还是出现只读错误,可以按照以下步骤排查:
- 先检查要修改的属性是否为LDAP条目的只读属性,可以通过
conn.get_entry(user_dn)查看条目的属性定义,确认属性是否允许修改 - 检查连接使用的账号是否有对应条目的写权限,可以尝试用该账号手动在LDAP管理工具中修改对应属性,确认权限是否正常
- 检查修改的字典格式是否正确,操作类型必须是ldap3库定义的常量,值必须是列表格式,即使是单个值也要放在列表中
- 查看
conn.result返回的详细错误信息,根据错误码判断是权限问题还是语法问题
注意事项
不要尝试直接修改LDAP的系统内置属性,比如objectGUID、whenCreated这类属性,这些属性由LDAP服务端自动维护,修改时会直接返回只读错误。
另外如果修改的是密码类属性,需要注意LDAP服务端对密码的加密要求,部分服务端要求密码必须通过加密通道传输,或者需要符合密码复杂度策略,否则也会返回修改失败的错误。