mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Improve ipalib.plugins.baseldap classes.
- remove obsolete code related to PluginProxy - remove parent_key attribute, for the purpose of nested objects the parent's primary key is retrieved automatically - added support for auto-generating of UUIDs - make use of the improved attribute printing in CLI - make LDAPDelete delete all sub-entries, not just one-level - minor bug fixes
This commit is contained in:
parent
b519b87ea4
commit
2147a845cf
@ -20,86 +20,35 @@
|
|||||||
Base classes for LDAP plugins.
|
Base classes for LDAP plugins.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ipalib import crud, errors
|
from ipalib import crud, errors, uuid
|
||||||
from ipalib import Command, Method, Object
|
from ipalib import Command, Method, Object
|
||||||
from ipalib import Flag, List, Str
|
from ipalib import Flag, List, Str
|
||||||
from ipalib.cli import to_cli, from_cli
|
|
||||||
from ipalib.base import NameSpace
|
from ipalib.base import NameSpace
|
||||||
|
from ipalib.cli import to_cli, from_cli
|
||||||
|
|
||||||
|
|
||||||
class LDAPObject(Object):
|
class LDAPObject(Object):
|
||||||
"""
|
"""
|
||||||
Object representing a LDAP entry.
|
Object representing a LDAP entry.
|
||||||
"""
|
"""
|
||||||
__public__ = frozenset((
|
|
||||||
'backend',
|
|
||||||
'methods',
|
|
||||||
'properties',
|
|
||||||
'params',
|
|
||||||
'primary_key',
|
|
||||||
'parent_key',
|
|
||||||
'params_minus_pk',
|
|
||||||
'params_minus',
|
|
||||||
'get_dn',
|
|
||||||
|
|
||||||
'container_dn',
|
|
||||||
'object_name',
|
|
||||||
'object_name_plural',
|
|
||||||
'parent_object_name',
|
|
||||||
'object_class',
|
|
||||||
'object_class_config',
|
|
||||||
'default_attributes',
|
|
||||||
'hidden_attributes',
|
|
||||||
'attribute_names',
|
|
||||||
'attribute_order',
|
|
||||||
'attribute_members',
|
|
||||||
'get_primary_key_from_dn',
|
|
||||||
'convert_attribute_members',
|
|
||||||
'print_entry',
|
|
||||||
))
|
|
||||||
parent_key = None
|
|
||||||
|
|
||||||
backend_name = 'ldap2'
|
backend_name = 'ldap2'
|
||||||
|
|
||||||
|
parent_object = ''
|
||||||
container_dn = ''
|
container_dn = ''
|
||||||
object_name = 'entry'
|
object_name = 'entry'
|
||||||
object_name_plural = 'entries'
|
object_name_plural = 'entries'
|
||||||
parent_object_name = ''
|
object_class = []
|
||||||
object_class = ['top']
|
|
||||||
object_class_config = None
|
object_class_config = None
|
||||||
default_attributes = ['']
|
default_attributes = []
|
||||||
hidden_attributes = ['objectclass', 'aci']
|
hidden_attributes = ['objectclass', 'aci']
|
||||||
|
uuid_attribute = ''
|
||||||
attribute_names = {}
|
attribute_names = {}
|
||||||
attribute_order = []
|
attribute_order = []
|
||||||
attribute_members = {}
|
attribute_members = {}
|
||||||
|
|
||||||
def set_api(self, api):
|
|
||||||
super(LDAPObject, self).set_api(api)
|
|
||||||
parent_keys = filter(lambda p: p.parent_key, self.params())
|
|
||||||
if len(parent_keys) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
'%s (LDAPObject) has multiple parent keys: %s' % (
|
|
||||||
self.name,
|
|
||||||
', '.join(p.name for p in parent_keys),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if len(parent_keys) == 1:
|
|
||||||
self.parent_key = parent_keys[0]
|
|
||||||
self.params_minus_pk = NameSpace(
|
|
||||||
filter(
|
|
||||||
lambda p: not p.primary_key and not p.parent_key,
|
|
||||||
self.params()
|
|
||||||
),
|
|
||||||
sort=False
|
|
||||||
)
|
|
||||||
elif self.params_minus_pk is None:
|
|
||||||
self.params_minus_pk = self.params
|
|
||||||
|
|
||||||
def get_dn(self, *keys, **kwargs):
|
def get_dn(self, *keys, **kwargs):
|
||||||
if len(keys) > 1:
|
if self.parent_object:
|
||||||
parent_dn = self.backend.make_dn_from_attr(
|
parent_dn = self.api.Object[self.parent_object].get_dn(*keys[:-1])
|
||||||
self.parent_key.name, keys[0], self.container_dn
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
parent_dn = self.container_dn
|
parent_dn = self.container_dn
|
||||||
return self.backend.make_dn_from_attr(
|
return self.backend.make_dn_from_attr(
|
||||||
@ -109,6 +58,14 @@ class LDAPObject(Object):
|
|||||||
def get_primary_key_from_dn(self, dn):
|
def get_primary_key_from_dn(self, dn):
|
||||||
return dn[len(self.primary_key.name) + 1:dn.find(',')]
|
return dn[len(self.primary_key.name) + 1:dn.find(',')]
|
||||||
|
|
||||||
|
def get_ancestor_primary_keys(self):
|
||||||
|
if self.parent_object:
|
||||||
|
parent_obj = self.api.Object[self.parent_object]
|
||||||
|
for key in parent_obj.get_ancestor_primary_keys():
|
||||||
|
yield key
|
||||||
|
if parent_obj.primary_key:
|
||||||
|
yield parent_obj.primary_key.clone(query=True)
|
||||||
|
|
||||||
def convert_attribute_members(self, entry_attrs, *keys, **options):
|
def convert_attribute_members(self, entry_attrs, *keys, **options):
|
||||||
if options.get('raw', False):
|
if options.get('raw', False):
|
||||||
return
|
return
|
||||||
@ -117,7 +74,7 @@ class LDAPObject(Object):
|
|||||||
for ldap_obj_name in self.attribute_members[attr]:
|
for ldap_obj_name in self.attribute_members[attr]:
|
||||||
ldap_obj = self.api.Object[ldap_obj_name]
|
ldap_obj = self.api.Object[ldap_obj_name]
|
||||||
if member.find(ldap_obj.container_dn) > 0:
|
if member.find(ldap_obj.container_dn) > 0:
|
||||||
new_attr = 'member %s' % ldap_obj.object_name_plural
|
new_attr = '%s %s' % (attr, ldap_obj.object_name_plural)
|
||||||
entry_attrs.setdefault(new_attr, []).append(
|
entry_attrs.setdefault(new_attr, []).append(
|
||||||
ldap_obj.get_primary_key_from_dn(member)
|
ldap_obj.get_primary_key_from_dn(member)
|
||||||
)
|
)
|
||||||
@ -140,7 +97,7 @@ class LDAPObject(Object):
|
|||||||
del entry_attrs[a]
|
del entry_attrs[a]
|
||||||
textui.print_entry(
|
textui.print_entry(
|
||||||
entry_attrs, attr_map=self.attribute_names,
|
entry_attrs, attr_map=self.attribute_names,
|
||||||
attr_order=self.attribute_order
|
attr_order=self.attribute_order, one_value_per_line=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -156,8 +113,8 @@ class LDAPCreate(crud.Create):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_args(self):
|
def get_args(self):
|
||||||
if self.obj.parent_key:
|
for key in self.obj.get_ancestor_primary_keys():
|
||||||
yield self.obj.parent_key.clone(query=True)
|
yield key
|
||||||
if self.obj.primary_key:
|
if self.obj.primary_key:
|
||||||
yield self.obj.primary_key.clone(attribute=True)
|
yield self.obj.primary_key.clone(attribute=True)
|
||||||
|
|
||||||
@ -175,6 +132,9 @@ class LDAPCreate(crud.Create):
|
|||||||
self.obj.object_class_config, entry_attrs['objectclass']
|
self.obj.object_class_config, entry_attrs['objectclass']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.obj.uuid_attribute:
|
||||||
|
entry_attrs[self.obj.uuid_attribute] = str(uuid.uuid1())
|
||||||
|
|
||||||
dn = self.pre_callback(ldap, dn, entry_attrs, *keys, **options)
|
dn = self.pre_callback(ldap, dn, entry_attrs, *keys, **options)
|
||||||
|
|
||||||
ldap.add_entry(dn, entry_attrs)
|
ldap.add_entry(dn, entry_attrs)
|
||||||
@ -192,8 +152,9 @@ class LDAPCreate(crud.Create):
|
|||||||
if len(keys) > 1:
|
if len(keys) > 1:
|
||||||
textui.print_dashed(
|
textui.print_dashed(
|
||||||
'Created %s "%s" in %s "%s".' % (
|
'Created %s "%s" in %s "%s".' % (
|
||||||
self.obj.object_name, keys[1], self.obj.parent_object_name,
|
self.obj.object_name, keys[-1],
|
||||||
keys[0]
|
self.api.Object[self.obj.parent_object].object_name,
|
||||||
|
keys[-2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif len(keys) == 1:
|
elif len(keys) == 1:
|
||||||
@ -215,8 +176,8 @@ class LDAPQuery(crud.PKQuery):
|
|||||||
Base class for commands that need to retrieve an existing entry.
|
Base class for commands that need to retrieve an existing entry.
|
||||||
"""
|
"""
|
||||||
def get_args(self):
|
def get_args(self):
|
||||||
if self.obj.parent_key:
|
for key in self.obj.get_ancestor_primary_keys():
|
||||||
yield self.obj.parent_key.clone(query=True)
|
yield key
|
||||||
if self.obj.primary_key:
|
if self.obj.primary_key:
|
||||||
yield self.obj.primary_key.clone(attribute=True, query=True)
|
yield self.obj.primary_key.clone(attribute=True, query=True)
|
||||||
|
|
||||||
@ -304,8 +265,9 @@ class LDAPUpdate(LDAPQuery, crud.Update):
|
|||||||
if len(keys) > 1:
|
if len(keys) > 1:
|
||||||
textui.print_dashed(
|
textui.print_dashed(
|
||||||
'Modified %s "%s" in %s "%s".' % (
|
'Modified %s "%s" in %s "%s".' % (
|
||||||
self.obj.object_name, keys[1], self.obj.parent_object_name,
|
self.obj.object_name, keys[-1],
|
||||||
keys[0]
|
self.api.Object[self.obj.parent_object].object_name,
|
||||||
|
keys[-2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif len(keys) == 1:
|
elif len(keys) == 1:
|
||||||
@ -333,19 +295,21 @@ class LDAPDelete(LDAPQuery):
|
|||||||
|
|
||||||
dn = self.pre_callback(ldap, dn, *keys, **options)
|
dn = self.pre_callback(ldap, dn, *keys, **options)
|
||||||
|
|
||||||
|
def delete_subtree(base_dn):
|
||||||
truncated = True
|
truncated = True
|
||||||
while truncated:
|
while truncated:
|
||||||
try:
|
try:
|
||||||
(subentries, truncated) = ldap.find_entries(
|
(subentries, truncated) = ldap.find_entries(
|
||||||
None, [''], dn, ldap.SCOPE_ONELEVEL
|
None, [''], base_dn, ldap.SCOPE_ONELEVEL
|
||||||
)
|
)
|
||||||
except errors.NotFound:
|
except errors.NotFound:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
for (dn_, entry_attrs) in subentries:
|
for (dn_, entry_attrs) in subentries:
|
||||||
ldap.delete_entry(dn_)
|
delete_subtree(dn_)
|
||||||
|
ldap.delete_entry(base_dn)
|
||||||
|
|
||||||
ldap.delete_entry(dn)
|
delete_subtree(dn)
|
||||||
|
|
||||||
result = self.post_callback(ldap, dn, *keys, **options)
|
result = self.post_callback(ldap, dn, *keys, **options)
|
||||||
|
|
||||||
@ -356,8 +320,9 @@ class LDAPDelete(LDAPQuery):
|
|||||||
if len(keys) > 1:
|
if len(keys) > 1:
|
||||||
textui.print_dashed(
|
textui.print_dashed(
|
||||||
'Deleted %s "%s" in %s "%s".' % (
|
'Deleted %s "%s" in %s "%s".' % (
|
||||||
self.obj.object_name, keys[1], self.obj.parent_object_name,
|
self.obj.object_name, keys[-1],
|
||||||
keys[0]
|
self.api.Object[self.obj.parent_object].object_name,
|
||||||
|
keys[-2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif len(keys) == 1:
|
elif len(keys) == 1:
|
||||||
@ -378,6 +343,7 @@ class LDAPModMember(LDAPQuery):
|
|||||||
"""
|
"""
|
||||||
Base class for member manipulation.
|
Base class for member manipulation.
|
||||||
"""
|
"""
|
||||||
|
member_attributes = ['member']
|
||||||
member_param_doc = 'comma-separated list of %s'
|
member_param_doc = 'comma-separated list of %s'
|
||||||
member_count_out = ('%i member processed.', '%i members processed.')
|
member_count_out = ('%i member processed.', '%i members processed.')
|
||||||
|
|
||||||
@ -389,7 +355,7 @@ class LDAPModMember(LDAPQuery):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_options(self):
|
def get_options(self):
|
||||||
for attr in self.obj.attribute_members:
|
for attr in self.member_attributes:
|
||||||
for ldap_obj_name in self.obj.attribute_members[attr]:
|
for ldap_obj_name in self.obj.attribute_members[attr]:
|
||||||
ldap_obj = self.api.Object[ldap_obj_name]
|
ldap_obj = self.api.Object[ldap_obj_name]
|
||||||
name = to_cli(ldap_obj_name)
|
name = to_cli(ldap_obj_name)
|
||||||
@ -399,7 +365,7 @@ class LDAPModMember(LDAPQuery):
|
|||||||
def get_member_dns(self, **options):
|
def get_member_dns(self, **options):
|
||||||
dns = {}
|
dns = {}
|
||||||
failed = {}
|
failed = {}
|
||||||
for attr in self.obj.attribute_members:
|
for attr in self.member_attributes:
|
||||||
dns[attr] = {}
|
dns[attr] = {}
|
||||||
failed[attr] = {}
|
failed[attr] = {}
|
||||||
for ldap_obj_name in self.obj.attribute_members[attr]:
|
for ldap_obj_name in self.obj.attribute_members[attr]:
|
||||||
@ -465,7 +431,7 @@ class LDAPAddMember(LDAPModMember):
|
|||||||
else:
|
else:
|
||||||
completed += 1
|
completed += 1
|
||||||
|
|
||||||
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
|
(dn, entry_attrs) = ldap.get_entry(dn, member_dns.keys())
|
||||||
|
|
||||||
(completed, dn) = self.post_callback(
|
(completed, dn) = self.post_callback(
|
||||||
ldap, completed, failed, dn, entry_attrs, *keys, **options
|
ldap, completed, failed, dn, entry_attrs, *keys, **options
|
||||||
@ -495,7 +461,7 @@ class LDAPRemoveMember(LDAPModMember):
|
|||||||
|
|
||||||
dn = self.obj.get_dn(*keys, **options)
|
dn = self.obj.get_dn(*keys, **options)
|
||||||
|
|
||||||
dn = self.pre_callback(ldap, dn, members_dns, failed, *keys, **options)
|
dn = self.pre_callback(ldap, dn, member_dns, failed, *keys, **options)
|
||||||
|
|
||||||
completed = 0
|
completed = 0
|
||||||
for (attr, objs) in member_dns.iteritems():
|
for (attr, objs) in member_dns.iteritems():
|
||||||
@ -513,14 +479,14 @@ class LDAPRemoveMember(LDAPModMember):
|
|||||||
else:
|
else:
|
||||||
completed += 1
|
completed += 1
|
||||||
|
|
||||||
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
|
(dn, entry_attrs) = ldap.get_entry(dn, member_dns.keys())
|
||||||
|
|
||||||
(completed, dn) = self.post_callback(
|
(completed, dn) = self.post_callback(
|
||||||
ldap, completed, failed, dn, entry_attrs, *keys, **options
|
ldap, completed, failed, dn, entry_attrs, *keys, **options
|
||||||
)
|
)
|
||||||
|
|
||||||
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
|
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
|
||||||
return (completed, add_failed, (dn, entry_attrs))
|
return (completed, failed, (dn, entry_attrs))
|
||||||
|
|
||||||
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
|
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
|
||||||
return dn
|
return dn
|
||||||
@ -545,18 +511,27 @@ class LDAPSearch(crud.Search):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_args(self):
|
def get_args(self):
|
||||||
if self.obj.parent_key:
|
for key in self.obj.get_ancestor_primary_keys():
|
||||||
yield self.obj.parent_key.clone(query=True)
|
yield key
|
||||||
yield Str('criteria?')
|
yield Str('criteria?')
|
||||||
|
|
||||||
|
def get_options(self):
|
||||||
|
for option in super(LDAPSearch, self).get_options():
|
||||||
|
yield option
|
||||||
|
if self.obj.uuid_attribute:
|
||||||
|
yield Str('%s?' % self.obj.uuid_attribute,
|
||||||
|
cli_name='uuid',
|
||||||
|
doc='unique identifier',
|
||||||
|
attribute=True,
|
||||||
|
query=True,
|
||||||
|
)
|
||||||
|
|
||||||
def execute(self, *args, **options):
|
def execute(self, *args, **options):
|
||||||
ldap = self.obj.backend
|
ldap = self.obj.backend
|
||||||
|
|
||||||
term = args[-1]
|
term = args[-1]
|
||||||
if self.obj.parent_key:
|
if self.obj.parent_object:
|
||||||
base_dn = ldap.make_dn_from_attr(
|
base_dn = self.api.Object[self.obj.parent_object].get_dn(*args[:-1])
|
||||||
self.obj.parent_key.name, args[0], base_dn
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
base_dn = self.obj.container_dn
|
base_dn = self.obj.container_dn
|
||||||
|
|
||||||
@ -592,11 +567,11 @@ class LDAPSearch(crud.Search):
|
|||||||
|
|
||||||
self.post_callback(self, ldap, entries, truncated, *args, **options)
|
self.post_callback(self, ldap, entries, truncated, *args, **options)
|
||||||
|
|
||||||
if options.get('raw', False):
|
if not options.get('raw', False):
|
||||||
for i in xrange(len(entries)):
|
for i in xrange(len(entries)):
|
||||||
dn = self.obj.get_primary_key_from_dn(entries[i][0])
|
dn = self.obj.get_primary_key_from_dn(entries[i][0])
|
||||||
self.obj.convert_attribute_members(
|
self.obj.convert_attribute_members(
|
||||||
entries[i][1], *keys, **options
|
entries[i][1], *args, **options
|
||||||
)
|
)
|
||||||
entries[i] = (dn, entries[i][1])
|
entries[i] = (dn, entries[i][1])
|
||||||
return (entries, truncated)
|
return (entries, truncated)
|
||||||
|
Loading…
Reference in New Issue
Block a user