Add support for setting/adding arbitrary attributes

This introduces 2 new params: --setattr and --addattr

Both take a name/value pair, ala:

ipa user-mod --setattr=postalcode=20601 jsmith

--setattr replaces or sets the current attribute to the value
--addattr adds the value to an attribute (or sets a new attribute)

OptionsParser allows multiple versions of this, so you can have multiple
setattr and addattr, either for the same attribute or for different
attributes.

ipa user-mod --addattr=postalcode=20601 --addattr=postalcode=30330 jsmith

Values are silent dropped if either of these on an existing param:

ipa user-mod --setattr=givenname=Jerry jsmith

Is a no-op.
This commit is contained in:
Rob Crittenden 2009-11-04 15:56:43 -05:00 committed by Jason Gerard DeRose
parent 680bf7c548
commit 55c62ac79a
2 changed files with 106 additions and 0 deletions

View File

@ -473,6 +473,34 @@ class Command(HasParam):
kw = self.args_options_2_params(*args, **options) kw = self.args_options_2_params(*args, **options)
return dict(self.__attributes_2_entry(kw)) return dict(self.__attributes_2_entry(kw))
def __convert_2_dict(self, attrs, append=True):
"""
Convert a string in the form of name/value pairs into
a dictionary. The incoming attribute may be a string or
a list.
Any attribute found that is also a param is silently dropped.
append controls whether this returns a list of values or a single
value.
"""
newdict = {}
if not type(attrs) in (list, tuple):
attrs = [attrs]
for a in attrs:
m = re.match("\s*(.*?)\s*=\s*(.*?)\s*$", a)
attr = str(m.group(1)).lower()
value = m.group(2)
if len(value) == 0:
# None means "delete this attribute"
value = None
if attr not in self.params:
if append and attr in newdict:
newdict[attr].append(value)
else:
newdict[attr] = [value]
return newdict
def __attributes_2_entry(self, kw): def __attributes_2_entry(self, kw):
for name in self.params: for name in self.params:
if self.params[name].attribute and name in kw: if self.params[name].attribute and name in kw:
@ -482,6 +510,23 @@ class Command(HasParam):
else: else:
yield (name, kw[name]) yield (name, kw[name])
adddict = {}
if 'setattr' in kw:
adddict = self.__convert_2_dict(kw['setattr'], append=False)
if 'addattr' in kw:
adddict.update(self.__convert_2_dict(kw['addattr']))
for name in adddict:
value = adddict[name]
if isinstance(value, list):
if len(value) == 1:
yield (name, value[0])
else:
yield (name, [v for v in value])
else:
yield (name, value)
def params_2_args_options(self, **params): def params_2_args_options(self, **params):
""" """
Split params into (args, options). Split params into (args, options).

View File

@ -20,6 +20,7 @@
Base classes for LDAP plugins. Base classes for LDAP plugins.
""" """
import re
from ipalib import crud, errors, uuid 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
@ -27,6 +28,28 @@ from ipalib.base import NameSpace
from ipalib.cli import to_cli, from_cli from ipalib.cli import to_cli, from_cli
def validate_add_attribute(ugettext, attr):
validate_attribute(ugettext, 'addattr', attr)
def validate_set_attribute(ugettext, attr):
validate_attribute(ugettext, 'setattr', attr)
def validate_attribute(ugettext, name, attr):
m = re.match("\s*(.*?)\s*=\s*(.*?)\s*$", attr)
if not m or len(m.groups()) != 2:
raise errors.ValidationError(name=name, error='Invalid format. Should be name=value')
def get_attributes(attrs):
"""
Given a list of values in the form name=value, return a list of name.
"""
attrlist=[]
for attr in attrs:
m = re.match("\s*(.*?)\s*=\s*(.*?)\s*$", attr)
attrlist.append(str(m.group(1)).lower())
return attrlist
class LDAPObject(Object): class LDAPObject(Object):
""" """
Object representing a LDAP entry. Object representing a LDAP entry.
@ -111,6 +134,16 @@ class LDAPCreate(crud.Create):
doc='print entries as they are stored in LDAP', doc='print entries as they are stored in LDAP',
exclude='webui', exclude='webui',
), ),
Str('addattr*', validate_add_attribute,
cli_name='addattr',
doc='Add an attribute/value pair. Format is attr=value',
exclude='webui',
),
Str('setattr*', validate_set_attribute,
cli_name='setattr',
doc='Set an attribute to an name/value pair. Format is attr=value',
exclude='webui',
),
) )
def get_args(self): def get_args(self):
@ -241,6 +274,16 @@ class LDAPUpdate(LDAPQuery, crud.Update):
cli_name='all', cli_name='all',
doc='retrieve all attributes', doc='retrieve all attributes',
), ),
Str('addattr*', validate_add_attribute,
cli_name='addattr',
doc='Add an attribute/value pair. Format is attr=value',
exclude='webui',
),
Str('setattr*', validate_set_attribute,
cli_name='setattr',
doc='Set an attribute to an name/value pair. Format is attr=value',
exclude='webui',
),
) )
def execute(self, *keys, **options): def execute(self, *keys, **options):
@ -257,6 +300,24 @@ class LDAPUpdate(LDAPQuery, crud.Update):
dn = self.pre_callback(ldap, dn, entry_attrs, attrs_list, *keys, **options) dn = self.pre_callback(ldap, dn, entry_attrs, attrs_list, *keys, **options)
"""
Some special handling is needed because we need to update the
values here rather than letting ldap.update_entry() do the work. We
have to do the work of adding new values to an existing attribute
because if we pass just what is addded only the new values get
set.
"""
if 'addattr' in options:
(dn, old_entry) = ldap.get_entry(dn, attrs_list)
attrlist = get_attributes(options['addattr'])
for attr in attrlist:
if attr in old_entry:
if type(entry_attrs[attr]) in (tuple,list):
entry_attrs[attr] = old_entry[attr] + entry_attrs[attr]
else:
old_entry[attr].append(entry_attrs[attr])
entry_attrs[attr] = old_entry[attr]
try: try:
ldap.update_entry(dn, entry_attrs) ldap.update_entry(dn, entry_attrs)
except errors.EmptyModlist: except errors.EmptyModlist: