diff options
-rw-r--r-- | API.txt | 113 | ||||
-rw-r--r-- | install/share/Makefile.am | 2 | ||||
-rw-r--r-- | install/share/automember.ldif | 32 | ||||
-rw-r--r-- | install/share/replica-automember.ldif | 8 | ||||
-rw-r--r-- | ipalib/constants.py | 1 | ||||
-rw-r--r-- | ipalib/plugins/automember.py | 587 | ||||
-rw-r--r-- | ipalib/plugins/user.py | 3 | ||||
-rw-r--r-- | ipaserver/install/dsinstance.py | 9 | ||||
-rw-r--r-- | tests/test_xmlrpc/objectclasses.py | 5 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_automember_plugin.py | 1074 |
10 files changed, 1834 insertions, 0 deletions
@@ -99,6 +99,119 @@ option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_add +args: 1,5,3 +arg: Str('cn', cli_name='automember_rule', label=Gettext('Automember Rule', domain='ipa', localedir=None), normalizer=<lambda>) +option: Str('description', attribute=True, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, required=False) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_add_condition +args: 1,8,5 +arg: Str('cn', cli_name='automember_rule', label=Gettext('Automember Rule', domain='ipa', localedir=None), normalizer=<lambda>) +option: Str('description', attribute=True, autofill=False, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, required=False) +option: List('automemberinclusiveregex?', alwaysask=True, cli_name='inclusive_regex', label=Gettext('Inclusive Regex', domain='ipa', localedir=None), multivalue=True) +option: List('automemberexclusiveregex?', alwaysask=True, cli_name='exclusive_regex', label=Gettext('Exclusive Regex', domain='ipa', localedir=None), multivalue=True) +option: Str('key', flags=['no_create', 'no_update', 'no_search'], label=Gettext('Attribute Key', domain='ipa', localedir=None)) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +output: Output('failed', <type 'dict'>, Gettext('Conditions that could not be added', domain='ipa', localedir=None)) +output: Output('completed', <type 'int'>, Gettext('Number of conditions added', domain='ipa', localedir=None)) +command: automember_default_group_remove +args: 0,5,3 +option: Str('description', attribute=True, autofill=False, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, required=False) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_default_group_set +args: 0,6,3 +option: Str('description', attribute=True, autofill=False, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, required=False) +option: Str('automemberdefaultgroup', cli_name='default_group', flags=['no_create', 'no_update'], label=Gettext('Default Group', domain='ipa', localedir=None)) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_default_group_show +args: 0,4,3 +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_del +args: 1,1,3 +arg: Str('cn', cli_name='automember_rule', label=Gettext('Automember Rule', domain='ipa', localedir=None), normalizer=<lambda>) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Output('result', <type 'dict'>, 'list of deletions that failed') +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_find +args: 1,5,4 +arg: Str('criteria?', noextrawhitespace=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, query=True, required=False) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('count', <type 'int'>, 'Number of entries returned') +output: Output('truncated', <type 'bool'>, 'True if not all results were returned') +command: automember_mod +args: 1,5,3 +arg: Str('cn', cli_name='automember_rule', label=Gettext('Automember Rule', domain='ipa', localedir=None), normalizer=<lambda>) +option: Str('description', attribute=True, autofill=False, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, required=False) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: automember_remove_condition +args: 1,8,5 +arg: Str('cn', cli_name='automember_rule', label=Gettext('Automember Rule', domain='ipa', localedir=None), normalizer=<lambda>) +option: Str('description', attribute=True, autofill=False, cli_name='desc', label=Gettext('Description', domain='ipa', localedir=None), multivalue=False, required=False) +option: List('automemberinclusiveregex?', alwaysask=True, cli_name='inclusive_regex', label=Gettext('Inclusive Regex', domain='ipa', localedir=None), multivalue=True) +option: List('automemberexclusiveregex?', alwaysask=True, cli_name='exclusive_regex', label=Gettext('Exclusive Regex', domain='ipa', localedir=None), multivalue=True) +option: Str('key', flags=['no_create', 'no_update', 'no_search'], label=Gettext('Attribute Key', domain='ipa', localedir=None)) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +output: Output('failed', <type 'dict'>, Gettext('Conditions that could not be removed', domain='ipa', localedir=None)) +output: Output('completed', <type 'int'>, Gettext('Number of conditions removed', domain='ipa', localedir=None)) +command: automember_show +args: 1,4,3 +arg: Str('cn', cli_name='automember_rule', label=Gettext('Automember Rule', domain='ipa', localedir=None), normalizer=<lambda>) +option: StrEnum('type', label=Gettext('Grouping Type', domain='ipa', localedir=None), values=(u'group', u'hostgroup')) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output']) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output']) +option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") command: automountkey_add args: 2,7,3 arg: Str('automountlocationcn', cli_name='automountlocation', label=Gettext('Location', domain='ipa', localedir=None), query=True, required=True) diff --git a/install/share/Makefile.am b/install/share/Makefile.am index cab9e17b5..f2a6a6cae 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -49,6 +49,8 @@ app_DATA = \ entryusn.ldif \ root-autobind.ldif \ sudobind.ldif \ + automember.ldif \ + replica-automember.ldif \ $(NULL) EXTRA_DIST = \ diff --git a/install/share/automember.ldif b/install/share/automember.ldif new file mode 100644 index 000000000..1520e62ad --- /dev/null +++ b/install/share/automember.ldif @@ -0,0 +1,32 @@ +# Configuration for Auto Membership Plugin for Master +# installation. This method should be revisted for +# optimization due to a bug within 389 DS which prevents +# the definition files from being added seperatly after +# the insertion of cn=Auto Membership Plugin,cn=plugins,cn=config +# and subsequent 389 DS restart. +dn: cn=Auto Membership Plugin,cn=plugins,cn=config +changetype: modify +add: nsslapd-pluginConfigArea +nsslapd-pluginConfigArea: cn=automember,cn=etc,$SUFFIX + +dn: cn=automember,cn=etc,$SUFFIX +changetype: add +objectClass: top +objectClass: nsContainer +cn: automember + +dn: cn=Hostgroup,cn=automember,cn=etc,$SUFFIX +changetype: add +objectclass: autoMemberDefinition +cn: Hostgroup +autoMemberScope: cn=computers,cn=accounts,$SUFFIX +autoMemberFilter: objectclass=ipaHost +autoMemberGroupingAttr: member:dn + +dn: cn=Group,cn=automember,cn=etc,$SUFFIX +changetype: add +objectclass: autoMemberDefinition +cn: Group +autoMemberScope: cn=users,cn=accounts,$SUFFIX +autoMemberFilter: objectclass=posixAccount +autoMemberGroupingAttr: member:dn diff --git a/install/share/replica-automember.ldif b/install/share/replica-automember.ldif new file mode 100644 index 000000000..15d5d10ce --- /dev/null +++ b/install/share/replica-automember.ldif @@ -0,0 +1,8 @@ +# Configuration for Auto Membership Plugin for Replica +# installation. This method should be revisted for +# optimization due to a bug within 389 DS. +# dsinstance.py should eventually insert this during common_setup. +dn: cn=Auto Membership Plugin,cn=plugins,cn=config +changetype: modify +add: nsslapd-pluginConfigArea +nsslapd-pluginConfigArea: cn=automember,cn=etc,$SUFFIX diff --git a/ipalib/constants.py b/ipalib/constants.py index 51cf566e1..b4bb86dde 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -112,6 +112,7 @@ DEFAULT_CONFIG = ( ('container_sudocmd', 'cn=sudocmds,cn=sudo'), ('container_sudocmdgroup', 'cn=sudocmdgroups,cn=sudo'), ('container_entitlements', 'cn=entitlements,cn=etc'), + ('container_automember', 'cn=automember,cn=etc'), # Ports, hosts, and URIs: # FIXME: let's renamed xmlrpc_uri to rpc_xml_uri diff --git a/ipalib/plugins/automember.py b/ipalib/plugins/automember.py new file mode 100644 index 000000000..db58a7aa3 --- /dev/null +++ b/ipalib/plugins/automember.py @@ -0,0 +1,587 @@ +# Authors: +# Jr Aquino <jr.aquino@citrix.com> +# +# Copyright (C) 2011 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from ipalib import api, errors +from ipalib import Str, StrEnum +from ipalib.plugins.baseldap import * +from ipalib import _, ngettext +from ipalib.request import context +from ipalib.dn import * +import ldap as _ldap + +__doc__ = _(""" +Auto Membership Rule. + +Bring clarity to the membership of hosts and users by configuring inclusive +or exclusive regex paterns, you can automatically assign a new entries into +a group or hostgroup based upon attribute information. + +A rule is directly associated with a group by name, so you cannot create +a rule without an accompanying group or hostgroup + +A condition is a regular expression used by 389-ds to match a new incoming +entry with an automember rule. If it matches an inclusive rule then the +entry is added to the appropriate group or hostgroup. + +EXAMPLES: + + Create the initial group or hostgroup: + ipa hostgroup-add --desc="Web Servers" webservers + ipa group-add --desc="Developers" devel + + Create the initial rule: + ipa automember-add --type=hostgroup webservers + ipa automember-add --type=group devel + + Add a condition to the rule: + ipa automember-add-condition --key=fqdn --type=hostgroup --inclusive-regex=^web[1-9]+\.example\.com webservers + ipa automember-add-condition --key=manager --type=group --inclusive-regex=^uid=mscott devel + + Add an exclusive condition to the rule to prevent auto assignment: + ipa automember-add-condition --key=fqdn --type=hostgroup --exclusive-regex=^web5\.example\.com webservers + + Add a host: + ipa host-add web1.example.com + + Add a user: + ipa user-add --first=Tim --last=User --password tuser1 --manager=mscott + + Verify automembership: + ipa hostgroup-show webservers + Host-group: webservers + Description: Web Servers + Member hosts: web1.example.com + + ipa group-show devel + Group name: devel + Description: Developers + GID: 1004200000 + Member users: tuser + + Remove a condition from the rule: + ipa automember-remove-condition --key=fqdn --type=hostgroup --inclusive-regex=^web[1-9]+\.example\.com webservers + + Modify the automember rule: + ipa automember-mod + + Set the default target group: + ipa automember-default-group-set --default-group=webservers --type=hostgroup + ipa automember-default-group-set --default-group=ipausers --type=group + + Set the default target group: + ipa automember-default-group-remove --type=hostgroup + ipa automember-default-group-remove --type=group + + Show the default target group: + ipa automember-default-group-show --type=hostgroup + ipa automember-default-group-show --type=group + + Find all of the automember rules: + ipa automember-find + + Display a automember rule: + ipa automember-show --type=hostgroup webservers + ipa automember-show --type=group devel + + Delete an automember rule: + ipa automember-del --type=hostgroup webservers + ipa automember-del --type=group devel +""") + +# Options used by Condition Add and Remove. +INCLUDE_RE = 'automemberinclusiveregex' +EXCLUDE_RE = 'automemberexclusiveregex' + +regex_attrs = ( + List('automemberinclusiveregex?', + cli_name='inclusive_regex', + label=_('Inclusive Regex'), + doc=_('Inclusive Regex'), + multivalue=True, + alwaysask=True, + ), + List('automemberexclusiveregex?', + cli_name='exclusive_regex', + label=_('Exclusive Regex'), + doc=_('Exclusive Regex'), + multivalue=True, + alwaysask=True, + ), + Str('key', + label=_('Attribute Key'), + doc=_('Attribute to filter via regex. For example fqdn for a host, or manager for a user'), + flags=['no_create', 'no_update', 'no_search'] + ), +) + +group_type = ( + StrEnum('type', + label=_('Grouping Type'), + doc=_('Grouping to which the rule applies'), + values=(u'group', u'hostgroup', ), + ), +) + +automember_rule = ( + Str('cn', + cli_name='automember_rule', + label=_('Automember Rule'), + doc=_('Automember Rule'), + normalizer=lambda value: value.lower(), + ), +) + +class automember(LDAPObject): + + """ + Bring automember to a hostgroup with an Auto Membership Rule. + """ + + container_dn = api.env.container_automember + + object_name = 'auto_member_rule' + object_name_plural = 'auto_member_rules' + object_class = ['top', 'automemberregexrule'] + default_attributes = [ + 'automemberinclusiveregex', 'automemberexclusiveregex', + 'cn', 'automembertargetgroup', 'description', 'automemberdefaultgroup' + ] + + label = _('Auto Membership Rule') + + takes_params = ( + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('A description of this auto member rule'), + ), + Str('automemberdefaultgroup?', + cli_name='default_group', + label=_('Default Group'), + doc=_('Default group for entires to land'), + flags=['no_create', 'no_update', 'no_search'] + ), + ) + + def dn_exists(self, grouptype, groupname, *keys): + ldap = self.api.Backend.ldap2 + dn = self.api.Object[grouptype].get_dn(groupname) + try: + (gdn, entry_attrs) = ldap.get_entry(dn, []) + except errors.NotFound: + raise errors.NotFound(reason=_(u'Group: %s not found!' % groupname)) + return gdn + + def get_dn(self, *keys, **options): + if self.parent_object: + parent_dn = self.api.Object[self.parent_object].get_dn(*keys[:-1]) + else: + parent_dn = self.container_dn + grouptype = options['type'] + try: + ndn = DN(('cn', keys[-1]), ('cn', grouptype), DN(parent_dn)) + except IndexError: + ndn = DN(('cn', grouptype), DN(parent_dn)) + parent_dn = str(ndn) + return parent_dn + + def check_attr(self, attr): + """ + Verify that the user supplied key is a valid attribute in the schema + """ + ldap = self.api.Backend.ldap2 + if not ldap.schema: + ldap.get_schema() + obj = ldap.schema.get_obj(_ldap.schema.AttributeType, attr) + if obj is not None: + return obj + else: + raise errors.NotFound(reason=_('%s is not a valid attribute.' % attr)) + +api.register(automember) + + +def automember_container_exists(ldap): + try: + ldap.get_entry(api.env.container_automember, []) + except errors.NotFound: + return False + return True + +class automember_add(LDAPCreate): + __doc__ = _(""" + Add an automember rule. + """) + takes_options = group_type + takes_args = automember_rule + msg_summary = _('Added automember rule "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, *keys, **options): + + entry_attrs['cn'] = keys[-1] + if not automember_container_exists(self.api.Backend.ldap2): + raise errors.NotFound(reason=_('Auto Membership is not configured')) + entry_attrs['automembertargetgroup'] = self.obj.dn_exists(options['type'], keys[-1]) + return dn + + def execute(self, *keys, **options): + result = super(automember_add, self).execute(*keys, **options) + result['value'] = keys[-1] + return result + +api.register(automember_add) + + +class automember_add_condition(LDAPUpdate): + __doc__ = _(""" + Add conditions to an automember rule. + """) + has_output_params = ( + Str('failed', + label=_('Failed to add'), + flags=['suppress_empty'], + ), + ) + + takes_options = regex_attrs + group_type + takes_args = automember_rule + msg_summary = _('Added condition(s) to "%(value)s"') + + # Prepare the output to expect failed results + has_output = ( + output.summary, + output.Entry('result'), + output.value, + output.Output('failed', + type=dict, + doc=_('Conditions that could not be added'), + ), + output.Output('completed', + type=int, + doc=_('Number of conditions added'), + ), + ) + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + # Check to see if the automember rule exists + try: + (tdn, test_attrs) = ldap.get_entry(dn, []) + except errors.NotFound: + raise errors.NotFound(reason=_(u'Auto member rule: %s not found!' % keys[0])) + # Define container key + key = options['key'] + # Check to see if the attribute is valid + self.obj.check_attr(key) + + key = '%s=' % key + completed = 0 + failed = {'failed': {}} + + for attr in (INCLUDE_RE, EXCLUDE_RE): + failed['failed'][attr] = [] + if attr in options and options[attr]: + entry_attrs[attr] = [key + condition for condition in options[attr]] + completed += len(entry_attrs[attr]) + try: + (dn, old_entry) = ldap.get_entry( + dn, [attr], normalize=self.obj.normalize_dn) + for regex in old_entry: + if not isinstance(entry_attrs[regex], (list, tuple)): + entry_attrs[regex] = [entry_attrs[regex]] + duplicate = set(old_entry[regex]) & set(entry_attrs[regex]) + if len(duplicate) > 0: + completed -= 1 + else: + entry_attrs[regex] = list(entry_attrs[regex]) + old_entry[regex] + except errors.NotFound: + failed['failed'][attr].append(regex) + + # Set failed and completed to they can be harvested in the execute super + setattr(context, 'failed', failed) + setattr(context, 'completed', completed) + setattr(context, 'entry_attrs', entry_attrs) + + # Make sure to returned the failed results if there is nothing to remove + if completed == 0: + (dn, entry_attrs) = ldap.get_entry( + dn, attrs_list, normalize=self.obj.normalize_dn + ) + raise errors.EmptyModlist + return dn + + def execute(self, *keys, **options): + __doc__ = _(""" + Override this so we can add completed and failed to the return result. + """) + try: + result = super(automember_add_condition, self).execute(*keys, **options) + except errors.EmptyModlist: + result = {'result': getattr(context, 'entry_attrs'), 'value': keys[-1]} + result['failed'] = getattr(context, 'failed') + result['completed'] = getattr(context, 'completed') + result['value'] = keys[-1] + return result + +api.register(automember_add_condition) + + +class automember_remove_condition(LDAPUpdate): + __doc__ = _(""" + Remove conditions from an automember rule. + """) + takes_options = regex_attrs + group_type + takes_args = automember_rule + msg_summary = _('Removed condition(s) to "%(value)s"') + + # Prepare the output to expect failed results + has_output = ( + output.summary, + output.Entry('result'), + output.value, + output.Output('failed', + type=dict, + doc=_('Conditions that could not be removed'), + ), + output.Output('completed', + type=int, + doc=_('Number of conditions removed'), + ), + ) + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + # Check to see if the automember rule exists + try: + (tdn, test_attrs) = ldap.get_entry(dn, []) + except errors.NotFound: + raise errors.NotFound(reason=_(u'Auto member rule: %s not found!' % keys[0])) + + # Define container key + type_attr_default = {'group': 'manager', 'hostgroup': 'fqdn'} + if 'key' in options: + key = options['key'] + else: + key = type_attr_default[options['type']] + + key = '%s=' % key + completed = 0 + failed = {'failed': {}} + + # Check to see if there are existing exclusive conditions present. + (dn, exclude_present) = ldap.get_entry( + dn, [EXCLUDE_RE], normalize=self.obj.normalize_dn) + + for attr in (INCLUDE_RE, EXCLUDE_RE): + failed['failed'][attr] = [] + if attr in options and options[attr]: + entry_attrs[attr] = [key + condition for condition in options[attr]] + (dn, entry_attrs_) = ldap.get_entry( + dn, [attr], normalize=self.obj.normalize_dn + ) + old_entry = entry_attrs_.get(attr, []) + for regex in entry_attrs[attr]: + if regex in old_entry: + old_entry.remove(regex) + completed += 1 + else: + failed['failed'][attr].append(regex) + entry_attrs[attr] = old_entry + # Set failed and completed to they can be harvested in the execute super + setattr(context, 'failed', failed) + setattr(context, 'completed', completed) + setattr(context, 'entry_attrs', entry_attrs) + + # Make sure to returned the failed results if there is nothing to remove + if completed == 0: + (dn, entry_attrs) = ldap.get_entry( + dn, attrs_list, normalize=self.obj.normalize_dn + ) + raise errors.EmptyModlist + return dn + + def execute(self, *keys, **options): + __doc__ = _(""" + Override this so we can set completed and failed. + """) + try: + result = super(automember_remove_condition, self).execute(*keys, **options) + except errors.EmptyModlist: + result = {'result': getattr(context, 'entry_attrs'), 'value': keys[-1]} + result['failed'] = getattr(context, 'failed') + result['completed'] = getattr(context, 'completed') + result['value'] = keys[-1] + return result + +api.register(automember_remove_condition) + + +class automember_mod(LDAPUpdate): + __doc__ = _(""" + Modify an automember rule. + """) + takes_args = automember_rule + takes_options = group_type + msg_summary = _('Modified automember rule "%(value)s"') + + def execute(self, *keys, **options): + result = super(automember_mod, self).execute(*keys, **options) + result['value'] = keys[-1] + return result + +api.register(automember_mod) + + +class automember_del(LDAPDelete): + __doc__ = _(""" + Delete an automember rule. + """) + takes_args = automember_rule + takes_options = group_type + msg_summary = _('Deleted automember rule "%(value)s"') + + def execute(self, *keys, **options): + result = super(automember_del, self).execute(*keys, **options) + result['value'] = keys[-1] + return result + +api.register(automember_del) + + +class automember_find(LDAPSearch): + __doc__ = _(""" + Search for automember rules. + """) + takes_options = group_type + has_output_params = LDAPSearch.has_output_params + automember_rule + regex_attrs + + msg_summary = ngettext( + '%(count)d rules matched', '%(count)d rules matched', 0 + ) + + def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): + scope = ldap.SCOPE_SUBTREE + ndn = DN(('cn', options['type']), DN(base_dn)) + base_dn = str(ndn) + return (filters, base_dn, scope) + +api.register(automember_find) + + +class automember_show(LDAPRetrieve): + __doc__ = _(""" + Display information about an automember rule. + """) + takes_args = automember_rule + takes_options = group_type + has_output_params = LDAPRetrieve.has_output_params + regex_attrs + + def execute(self, *keys, **options): + result = super(automember_show, self).execute(*keys, **options) + result['value'] = keys[-1] + return result + +api.register(automember_show) + + +class automember_default_group_set(LDAPUpdate): + __doc__ = _(""" + Set default group for all unmatched entries. + """) + + takes_options = ( + Str('automemberdefaultgroup', + cli_name='default_group', + label=_('Default Group'), + doc=_('Default group for entires to land'), + flags=['no_create', 'no_update'] + ), + ) + group_type + msg_summary = _('Set default group for automember "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + dn = DN(('cn', options['type']), api.env.container_automember) + dn = str(dn) + entry_attrs['automemberdefaultgroup'] = self.obj.dn_exists(options['type'], options['automemberdefaultgroup']) + return dn + + def execute(self, *keys, **options): + result = super(automember_default_group_set, self).execute(*keys, **options) + result['value'] = options['type'] + return result + +api.register(automember_default_group_set) + + +class automember_default_group_remove(LDAPUpdate): + __doc__ = _(""" + Remove default group for all unmatched entries. + """) + + takes_options = group_type + msg_summary = _('Removed default group for automember "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + dn = DN(('cn', options['type']), api.env.container_automember) + dn = str(dn) + attr = 'automemberdefaultgroup' + + (dn, entry_attrs_) = ldap.get_entry( + dn, [attr], normalize=self.obj.normalize_dn + ) + + if attr not in entry_attrs_: + raise errors.NotFound(reason=_(u'No default group set')) + else: + entry_attrs[attr] = [] + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + if 'automemberdefaultgroup' not in entry_attrs: + entry_attrs['automemberdefaultgroup'] = u'No default group set' + return dn + + def execute(self, *keys, **options): + result = super(automember_default_group_remove, self).execute(*keys, **options) + result['value'] = options['type'] + return result + +api.register(automember_default_group_remove) + + +class automember_default_group_show(LDAPRetrieve): + __doc__ = _(""" + Display information about the default automember groups. + """) + takes_options = group_type + + def pre_callback(self, ldap, dn, attrs_list, *keys, **options): + dn = DN(('cn', options['type']), api.env.container_automember) + dn = str(dn) + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + if 'automemberdefaultgroup' not in entry_attrs: + entry_attrs['automemberdefaultgroup'] = u'No default group set' + return dn + + def execute(self, *keys, **options): + result = super(automember_default_group_show, self).execute(*keys, **options) + result['value'] = options['type'] + return result + +api.register(automember_default_group_show) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index d728ad47c..92a026d0a 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -391,6 +391,9 @@ class user_add(LDAPCreate): def_primary_group = config.get('ipadefaultprimarygroup') group_dn = self.api.Object['group'].get_dn(def_primary_group) ldap.add_entry_to_group(dn, group_dn) + if self.api.env.wait_for_attr: + newentry = wait_for_value(ldap, dn, 'memberOf', def_primary_group) + entry_from_entry(entry_attrs, newentry) self.obj._convert_manager(entry_attrs, **options) # delete description attribute NO_UPG_MAGIC if present if options.get('noprivate', False): diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index b1037fa38..fdbddb0ee 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -250,6 +250,7 @@ class DsInstance(service.Service): self.step("configuring user private groups", self.__user_private_groups) self.step("configuring netgroups from hostgroups", self.__host_nis_groups) self.step("creating default Sudo bind user", self.__add_sudo_binduser) + self.step("creating default Auto Member layout", self.__add_automember_config) if hbac_allow: self.step("creating default HBAC rule allow_all", self.add_hbac) @@ -283,6 +284,8 @@ class DsInstance(service.Service): self.step("setting up initial replication", self.__setup_replica) self.step("adding replication acis", self.__add_replication_acis) + # See LDIFs for automember configuration during replica install + self.step("setting Auto Member configuration", self.__add_replica_automember_config) # Managed Entries configuration is done via update files @@ -784,6 +787,12 @@ class DsInstance(service.Service): def __add_sudo_binduser(self): self._ldap_mod("sudobind.ldif", self.sub_dict) + def __add_automember_config(self): + self._ldap_mod("automember.ldif", self.sub_dict) + + def __add_replica_automember_config(self): + self._ldap_mod("replica-automember.ldif", self.sub_dict) + def replica_populate(self): self.ldap_connect() diff --git a/tests/test_xmlrpc/objectclasses.py b/tests/test_xmlrpc/objectclasses.py index 29cd6af51..ce904d81e 100644 --- a/tests/test_xmlrpc/objectclasses.py +++ b/tests/test_xmlrpc/objectclasses.py @@ -120,3 +120,8 @@ netgroup = [ u'ipaassociation', u'ipanisnetgroup', ] + +automember = [ + u'top', + u'automemberregexrule' +] diff --git a/tests/test_xmlrpc/test_automember_plugin.py b/tests/test_xmlrpc/test_automember_plugin.py new file mode 100644 index 000000000..342bc21eb --- /dev/null +++ b/tests/test_xmlrpc/test_automember_plugin.py @@ -0,0 +1,1074 @@ +# Authors: +# Jr Aquino <jr.aquino@citrix.com> +# +# Copyright (C) 2011 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +""" +Test the `ipalib/plugins/automember.py` module. +""" + +from ipalib import api, errors +from tests.test_xmlrpc import objectclasses +from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid + + +user1=u'tuser1' +manager1=u'mscott' +fqdn1 = u'web1.%s' % api.env.domain +short1 = u'web1' +fqdn2 = u'dev1.%s' % api.env.domain +short2 = u'dev1' +fqdn3 = u'web5.%s' % api.env.domain +short3 = u'web5' +fqdn4 = u'www5.%s' % api.env.domain +short4 = u'www5' +fqdn5 = u'webserver5.%s' % api.env.domain +short5 = u'webserver5' + +group1=u'group1' +defaultgroup1=u'defaultgroup1' +hostgroup1=u'hostgroup1' +hostgroup2=u'hostgroup2' +hostgroup3=u'hostgroup3' +hostgroup4=u'hostgroup4' +defaulthostgroup1=u'defaulthostgroup1' + +group_include_regex = u'mscott' +hostgroup_include_regex = u'^web[1-9]' +hostgroup_include_regex2 = u'^www[1-9]' +hostgroup_include_regex3 = u'webserver[1-9]' +hostgroup_exclude_regex = u'^web5' +hostgroup_exclude_regex2 = u'^www5' +hostgroup_exclude_regex3 = u'^webserver5' + + +class test_automember(Declarative): + + cleanup_commands = [ + ('user_del', [user1, manager1], {}), + ('group_del', [group1, defaultgroup1], {}), + ('host_del', [fqdn1, fqdn2, fqdn3, fqdn4, fqdn5], {}), + ('hostgroup_del', [hostgroup1, hostgroup2, hostgroup3, hostgroup4, defaulthostgroup1], {}), + ('automember_del', [group1], {'type': u'group'}), + ('automember_del', [hostgroup1], {'type': u'hostgroup'}), + ('automember_del', [hostgroup2], {'type': u'hostgroup'}), + ('automember_del', [hostgroup3], {'type': u'hostgroup'}), + ('automember_del', [hostgroup4], {'type': u'hostgroup'}), + ('automember_default_group_remove', [], {'type': u'hostgroup'}), + ('automember_default_group_remove', [], {'type': u'group'}), + + ] + + tests = [ + + dict( + desc='Try to retrieve non-existent %r' % user1, + command=('user_show', [user1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Try to update non-existent %r' % user1, + command=('user_mod', [user1], dict(givenname=u'Foo')), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Try to delete non-existent %r' % user1, + command=('user_del', [user1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Try to rename non-existent %r' % user1, + command=('user_mod', [user1], dict(setattr=u'uid=tuser')), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Create %r' % group1, + command=( + 'group_add', [group1], dict(description=u'Test desc') + ), + expected=dict( + value=group1, + summary=u'Added group "%s"' % group1, + result=dict( + cn=[group1], + description=[u'Test desc'], + gidnumber=[fuzzy_digits], + objectclass=objectclasses.group + [u'posixgroup'], + ipauniqueid=[fuzzy_uuid], + dn=u'cn=%s,cn=groups,cn=accounts,%s' % (group1, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create %r' % hostgroup1, + command=( + 'hostgroup_add', [hostgroup1], dict(description=u'Test desc') + ), + expected=dict( + value=hostgroup1, + summary=u'Added hostgroup "%s"' % hostgroup1, + result=dict( + cn=[hostgroup1], + description=[u'Test desc'], + objectclass=objectclasses.hostgroup, + ipauniqueid=[fuzzy_uuid], + memberof_netgroup=[u'hostgroup1'], + mepmanagedentry=['cn=%s,cn=ng,cn=alt,%s' % (hostgroup1, api.env.basedn)], + dn=u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create %r' % hostgroup2, + command=( + 'hostgroup_add', [hostgroup2], dict(description=u'Test desc') + ), + expected=dict( + value=hostgroup2, + summary=u'Added hostgroup "%s"' % hostgroup2, + result=dict( + cn=[hostgroup2], + description=[u'Test desc'], + objectclass=objectclasses.hostgroup, + ipauniqueid=[fuzzy_uuid], + memberof_netgroup=[u'hostgroup2'], + mepmanagedentry=['cn=%s,cn=ng,cn=alt,%s' % (hostgroup2, api.env.basedn)], + dn=u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup2, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create %r' % hostgroup3, + command=( + 'hostgroup_add', [hostgroup3], dict(description=u'Test desc') + ), + expected=dict( + value=hostgroup3, + summary=u'Added hostgroup "%s"' % hostgroup3, + result=dict( + cn=[hostgroup3], + description=[u'Test desc'], + objectclass=objectclasses.hostgroup, + ipauniqueid=[fuzzy_uuid], + memberof_netgroup=[u'hostgroup3'], + mepmanagedentry=['cn=%s,cn=ng,cn=alt,%s' % (hostgroup3, api.env.basedn)], + dn=u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup3, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create %r' % hostgroup4, + command=( + 'hostgroup_add', [hostgroup4], dict(description=u'Test desc') + ), + expected=dict( + value=hostgroup4, + summary=u'Added hostgroup "%s"' % hostgroup4, + result=dict( + cn=[hostgroup4], + description=[u'Test desc'], + objectclass=objectclasses.hostgroup, + ipauniqueid=[fuzzy_uuid], + memberof_netgroup=[u'hostgroup4'], + mepmanagedentry=['cn=%s,cn=ng,cn=alt,%s' % (hostgroup4, api.env.basedn)], + dn=u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup4, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create %r' % defaultgroup1, + command=( + 'group_add', [defaultgroup1], dict(description=u'Default test desc') + ), + expected=dict( + value=defaultgroup1, + summary=u'Added group "%s"' % defaultgroup1, + result=dict( + cn=[defaultgroup1], + description=[u'Default test desc'], + gidnumber=[fuzzy_digits], + objectclass=objectclasses.group + [u'posixgroup'], + ipauniqueid=[fuzzy_uuid], + dn=u'cn=%s,cn=groups,cn=accounts,%s' % (defaultgroup1, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create %r' % defaulthostgroup1, + command=( + 'hostgroup_add', [defaulthostgroup1], dict(description=u'Default test desc') + ), + expected=dict( + value=defaulthostgroup1, + summary=u'Added hostgroup "%s"' % defaulthostgroup1, + result=dict( + cn=[defaulthostgroup1], + description=[u'Default test desc'], + objectclass=objectclasses.hostgroup, + ipauniqueid=[fuzzy_uuid], + memberof_netgroup=[u'defaulthostgroup1'], + mepmanagedentry=['cn=%s,cn=ng,cn=alt,%s' % (defaulthostgroup1, api.env.basedn)], + dn=u'cn=%s,cn=hostgroups,cn=accounts,%s' % (defaulthostgroup1, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create automember %r' % group1, + command=( + 'automember_add', [group1], dict(description=u'Test desc', type=u'group') + ), + expected=dict( + value=group1, + summary=u'Added automember rule "%s"' % group1, + result=dict( + cn=[group1], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (group1, api.env.basedn)], + objectclass=objectclasses.automember, + dn=u'cn=%s,cn=group,cn=automember,cn=etc,%s' % (group1, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create automember condition %r' % group1, + command=( + 'automember_add_condition', [group1], dict( + key=u'manager', type=u'group', + automemberinclusiveregex=[group_include_regex], + ) + ), + expected=dict( + value=group1, + summary=u'Added condition(s) to "%s"' % group1, + completed=1, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + cn=[group1], + description=[u'Test desc'], + automemberinclusiveregex=[u'manager=%s' % group_include_regex], + automembertargetgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (group1, api.env.basedn)], + ), + ), + ), + + + dict( + desc='Create automember %r' % hostgroup1, + command=( + 'automember_add', [hostgroup1], dict( + description=u'Test desc', type=u'hostgroup', + ) + ), + expected=dict( + value=hostgroup1, + summary=u'Added automember rule "%s"' % hostgroup1, + result=dict( + cn=[hostgroup1], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn)], + objectclass=objectclasses.automember, + dn=u'cn=%s,cn=hostgroup,cn=automember,cn=etc,%s' % (hostgroup1, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create automember condition %r' % hostgroup1, + command=( + 'automember_add_condition', [hostgroup1], dict( + key=u'fqdn', type=u'hostgroup', + automemberinclusiveregex=[hostgroup_include_regex], + ) + ), + expected=dict( + value=hostgroup1, + summary=u'Added condition(s) to "%s"' % hostgroup1, + completed=1, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + cn=[hostgroup1], + description=[u'Test desc'], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn)], + ), + ), + ), + + + dict( + desc='Create duplicate automember condition %r' % hostgroup1, + command=( + 'automember_add_condition', [hostgroup1], dict( + key=u'fqdn', type=u'hostgroup', + automemberinclusiveregex=[hostgroup_include_regex], + ) + ), + expected=dict( + value=hostgroup1, + summary=u'Added condition(s) to "%s"' % hostgroup1, + completed=0, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex], + ), + ), + ), + + + dict( + desc='Create additional automember conditions %r' % hostgroup1, + command=( + 'automember_add_condition', [hostgroup1], dict( + key=u'fqdn', type=u'hostgroup', + automemberinclusiveregex=[hostgroup_include_regex2, hostgroup_include_regex3], + automemberexclusiveregex=[hostgroup_exclude_regex, hostgroup_exclude_regex2, hostgroup_exclude_regex3], + ) + ), + expected=dict( + value=hostgroup1, + summary=u'Added condition(s) to "%s"' % hostgroup1, + completed=5, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + cn=[hostgroup1], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn)], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex, + u'fqdn=%s' % hostgroup_include_regex3, + u'fqdn=%s' % hostgroup_include_regex2, + ], + automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2, + u'fqdn=%s' % hostgroup_exclude_regex3, + u'fqdn=%s' % hostgroup_exclude_regex, + ], + ), + ), + ), + + + dict( + desc='Create automember %r' % hostgroup2, + command=( + 'automember_add', [hostgroup2], dict( + description=u'Test desc', type=u'hostgroup', + ) + ), + expected=dict( + value=hostgroup2, + summary=u'Added automember rule "%s"' % hostgroup2, + result=dict( + cn=[hostgroup2], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup2, api.env.basedn)], + objectclass=objectclasses.automember, + dn=u'cn=%s,cn=hostgroup,cn=automember,cn=etc,%s' % (hostgroup2, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create automember condition %r' % hostgroup2, + command=( + 'automember_add_condition', [hostgroup2], dict( + key=u'fqdn', type=u'hostgroup', + automemberinclusiveregex=[hostgroup_exclude_regex], + ) + ), + expected=dict( + value=hostgroup2, + summary=u'Added condition(s) to "%s"' % hostgroup2, + completed=1, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + cn=[hostgroup2], + description=[u'Test desc'], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup2, api.env.basedn)], + ), + ), + ), + + + dict( + desc='Create automember %r' % hostgroup3, + command=( + 'automember_add', [hostgroup3], dict( + description=u'Test desc', type=u'hostgroup', + ) + ), + expected=dict( + value=hostgroup3, + summary=u'Added automember rule "%s"' % hostgroup3, + result=dict( + cn=[hostgroup3], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup3, api.env.basedn)], + objectclass=objectclasses.automember, + dn=u'cn=%s,cn=hostgroup,cn=automember,cn=etc,%s' % (hostgroup3, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create automember condition %r' % hostgroup3, + command=( + 'automember_add_condition', [hostgroup3], dict( + key=u'fqdn', type=u'hostgroup', + automemberinclusiveregex=[hostgroup_exclude_regex2], + ) + ), + expected=dict( + value=hostgroup3, + summary=u'Added condition(s) to "%s"' % hostgroup3, + completed=1, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + cn=[hostgroup3], + description=[u'Test desc'], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup3, api.env.basedn)], + ), + ), + ), + + + dict( + desc='Create automember %r' % hostgroup4, + command=( + 'automember_add', [hostgroup4], dict( + description=u'Test desc', type=u'hostgroup', + ) + ), + expected=dict( + value=hostgroup4, + summary=u'Added automember rule "%s"' % hostgroup4, + result=dict( + cn=[hostgroup4], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup4, api.env.basedn)], + objectclass=objectclasses.automember, + dn=u'cn=%s,cn=hostgroup,cn=automember,cn=etc,%s' % (hostgroup4, api.env.basedn), + ), + ), + ), + + + dict( + desc='Create automember condition %r' % hostgroup4, + command=( + 'automember_add_condition', [hostgroup4], dict( + key=u'fqdn', type=u'hostgroup', + automemberinclusiveregex=[hostgroup_exclude_regex3], + ) + ), + expected=dict( + value=hostgroup4, + summary=u'Added condition(s) to "%s"' % hostgroup4, + completed=1, + failed=dict( + failed = dict( + automemberinclusiveregex=tuple(), + automemberexclusiveregex=tuple(), + ) + ), + result=dict( + cn=[hostgroup4], + description=[u'Test desc'], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex3], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup4, api.env.basedn)], + ), + ), + ), + + + dict( + desc="Retrieve automember rule for group %s" % group1, + command=('automember_show', [group1], dict( + type=u'group', + ) + ), + expected=dict( + value=group1, + result=dict( + cn=[group1], + description=[u'Test desc'], + automemberinclusiveregex=[u'manager=%s' % group_include_regex], + automembertargetgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (group1, api.env.basedn)], + dn=u'cn=%s,cn=group,cn=automember,cn=etc,%s' % (group1, api.env.basedn), + ), + summary=None, + ), + ), + + + dict( + desc='Search for %r' % group1, + command=('automember_find', [group1], dict( + type=u'group' + ) + ), + expected=dict( + count=1, + truncated=False, + result=[ + dict( + cn=[group1], + description=[u'Test desc'], + automemberinclusiveregex=[u'manager=%s' % group_include_regex], + automembertargetgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (group1, api.env.basedn)], + dn=u'cn=%s,cn=group,cn=automember,cn=etc,%s' % (group1, api.env.basedn), + ), + ], + summary=u'1 rules matched', + ), + ), + + + dict( + desc='Updated automember rule %r' % group1, + command=( + 'automember_mod', [group1], dict( + type=u'group', + description=u'New desc 1', + ) + ), + expected=dict( + result=dict( + cn=[group1], + description=[u'New desc 1'], + automemberinclusiveregex=[u'manager=%s' % group_include_regex], + automembertargetgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (group1, api.env.basedn)], + ), + summary=u'Modified automember rule "%s"' % group1, + value=group1, + ), + ), + + + dict( + desc="Retrieve automember rule for hostgroup %s" % hostgroup1, + command=('automember_show', [hostgroup1], dict( + type=u'hostgroup', + ) + ), + expected=dict( + value=hostgroup1, + result=dict( + cn=[hostgroup1], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn)], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex, + u'fqdn=%s' % hostgroup_include_regex3, + u'fqdn=%s' % hostgroup_include_regex2, + ], + automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2, + u'fqdn=%s' % hostgroup_exclude_regex3, + u'fqdn=%s' % hostgroup_exclude_regex, + ], + dn=u'cn=%s,cn=hostgroup,cn=automember,cn=etc,%s' % (hostgroup1, api.env.basedn), + ), + summary=None, + ), + ), + + + dict( + desc='Search for %r' % hostgroup1, + command=('automember_find', [hostgroup1], dict( + type=u'hostgroup' + ) + ), + expected=dict( + count=1, + truncated=False, + result=[ + dict( + cn=[hostgroup1], + description=[u'Test desc'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn)], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex, + u'fqdn=%s' % hostgroup_include_regex3, + u'fqdn=%s' % hostgroup_include_regex2, + ], + automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2, + u'fqdn=%s' % hostgroup_exclude_regex3, + u'fqdn=%s' % hostgroup_exclude_regex, + ], + dn=u'cn=%s,cn=hostgroup,cn=automember,cn=etc,%s' % (hostgroup1, api.env.basedn), + ), + ], + summary=u'1 rules matched', + ), + ), + + + dict( + desc='Updated automember rule %r' % hostgroup1, + command=( + 'automember_mod', [hostgroup1], dict( + type=u'hostgroup', + description=u'New desc 1', + ) + ), + expected=dict( + result=dict( + cn=[hostgroup1], + description=[u'New desc 1'], + automembertargetgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn)], + automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex, + u'fqdn=%s' % hostgroup_include_regex3, + u'fqdn=%s' % hostgroup_include_regex2, + ], + automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2, + u'fqdn=%s' % hostgroup_exclude_regex3, + u'fqdn=%s' % hostgroup_exclude_regex, + ], + ), + summary=u'Modified automember rule "%s"' % hostgroup1, + value=hostgroup1, + ), + ), + + + dict( + desc='Set default automember group for groups', + command=( + 'automember_default_group_set', [], dict( + type=u'group', + automemberdefaultgroup=defaultgroup1 + ) + ), + expected=dict( + result=dict( + cn=[u'Group'], + automemberdefaultgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (defaultgroup1, api.env.basedn)], + ), + value=u'group', + summary=u'Set default group for automember "group"', + ), + ), + + + dict( + desc='Retrieve default automember group for groups', + command=( + 'automember_default_group_show', [], dict( + type=u'group', + automemberdefaultgroup=defaultgroup1, + ) + ), + expected=dict( + result=dict( + dn=u'cn=group,cn=automember,cn=etc,%s' % (api.env.basedn), + cn=[u'Group'], + automemberdefaultgroup=[u'cn=%s,cn=groups,cn=accounts,%s' % (defaultgroup1, api.env.basedn)], + ), + value=u'group', + summary=None, + ), + ), + + + dict( + desc='Set default automember group for hostgroups', + command=( + 'automember_default_group_set', [], dict( + type=u'hostgroup', + automemberdefaultgroup=defaulthostgroup1, + ) + ), + expected=dict( + result=dict( + cn=[u'Hostgroup'], + automemberdefaultgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (defaulthostgroup1, api.env.basedn)], + ), + value=u'hostgroup', + summary=u'Set default group for automember "hostgroup"', + ), + ), + + + dict( + desc='Retrieve default automember group for hostgroups', + command=( + 'automember_default_group_show', [], dict( + type=u'hostgroup', + ) + ), + expected=dict( + result=dict( + dn=u'cn=hostgroup,cn=automember,cn=etc,%s' % (api.env.basedn), + cn=[u'Hostgroup'], + automemberdefaultgroup=[u'cn=%s,cn=hostgroups,cn=accounts,%s' % (defaulthostgroup1, api.env.basedn)], + ), + value=u'hostgroup', + summary=None, + ), + ), + + + dict( + desc='Create %r' % manager1, + command=( + 'user_add', [manager1], dict(givenname=u'Michael', sn=u'Scott') + ), + expected=dict( + value=manager1, + summary=u'Added user "mscott"', + result=dict( + gecos=[u'Michael Scott'], + givenname=[u'Michael'], + homedirectory=[u'/home/mscott'], + krbprincipalname=[u'mscott@' + api.env.realm], + has_keytab=False, + has_password=False, + loginshell=[u'/bin/sh'], + objectclass=objectclasses.user, + sn=[u'Scott'], + uid=[manager1], + uidnumber=[fuzzy_digits], + gidnumber=[fuzzy_digits], + displayname=[u'Michael Scott'], + cn=[u'Michael Scott'], + initials=[u'MS'], + ipauniqueid=[fuzzy_uuid], + krbpwdpolicyreference=[u'cn=global_policy,cn=%s,cn=kerberos,%s' % (api.env.realm, api.env.basedn)], + mepmanagedentry=[u'cn=%s,cn=groups,cn=accounts,%s' % (manager1, api.env.basedn)], + memberof_group=[u'defaultgroup1', u'ipausers'], + dn=u'uid=mscott,cn=users,cn=accounts,' + api.env.basedn, + ), + ), + ), + + + dict( + desc='Create %r' % user1, + command=( + 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', manager=manager1) + ), + expected=dict( + value=user1, + summary=u'Added user "tuser1"', + result=dict( + gecos=[u'Test User1'], + givenname=[u'Test'], + homedirectory=[u'/home/tuser1'], + krbprincipalname=[u'tuser1@' + api.env.realm], + has_keytab=False, + has_password=False, + loginshell=[u'/bin/sh'], + objectclass=objectclasses.user, + sn=[u'User1'], + uid=[user1], + uidnumber=[fuzzy_digits], + gidnumber=[fuzzy_digits], + manager=[u'uid=mscott,cn=users,cn=accounts,%s' % api.env.basedn], + displayname=[u'Test User1'], + cn=[u'Test User1'], + initials=[u'TU'], + ipauniqueid=[fuzzy_uuid], + krbpwdpolicyreference=[u'cn=global_policy,cn=%s,cn=kerberos,%s' % (api.env.realm, api.env.basedn)], + mepmanagedentry=[u'cn=%s,cn=groups,cn=accounts,%s' % (user1, api.env.basedn)], + memberof_group=[u'group1', u'ipausers'], + dn=u'uid=tuser1,cn=users,cn=accounts,' + api.env.basedn, + ), + ), + ), + + + dict( + desc='Create %r' % fqdn1, + command=('host_add', [fqdn1], + dict( + description=u'Test host 1', + l=u'Undisclosed location 1', + force=True, + ), + ), + expected=dict( + value=fqdn1, + summary=u'Added host "%s"' % fqdn1, + result=dict( + dn=u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn1, api.env.basedn), + fqdn=[fqdn1], + description=[u'Test host 1'], + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)], + has_keytab=False, + has_password=False, + objectclass=objectclasses.host, + ipauniqueid=[fuzzy_uuid], + managedby_host=[fqdn1], + ), + ), + ), + + + dict( + desc='Create %r' % fqdn2, + command=('host_add', [fqdn2], + dict( + description=u'Test host 2', + l=u'Undisclosed location 1', + force=True, + ), + ), + expected=dict( + value=fqdn2, + summary=u'Added host "%s"' % fqdn2, + result=dict( + dn=u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn2, api.env.basedn), + fqdn=[fqdn2], + description=[u'Test host 2'], + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn2, api.env.realm)], + has_keytab=False, + has_password=False, + objectclass=objectclasses.host, + ipauniqueid=[fuzzy_uuid], + managedby_host=[fqdn2], + ), + ), + ), + + + dict( + desc='Create %r' % fqdn3, + command=('host_add', [fqdn3], + dict( + description=u'Test host 3', + l=u'Undisclosed location 1', + force=True, + ), + ), + expected=dict( + value=fqdn3, + summary=u'Added host "%s"' % fqdn3, + result=dict( + dn=u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn3, api.env.basedn), + fqdn=[fqdn3], + description=[u'Test host 3'], + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn3, api.env.realm)], + has_keytab=False, + has_password=False, + objectclass=objectclasses.host, + ipauniqueid=[fuzzy_uuid], + managedby_host=[fqdn3], + ), + ), + ), + + + dict( + desc='Create %r' % fqdn4, + command=('host_add', [fqdn4], + dict( + description=u'Test host 4', + l=u'Undisclosed location 1', + force=True, + ), + ), + expected=dict( + value=fqdn4, + summary=u'Added host "%s"' % fqdn4, + result=dict( + dn=u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn4, api.env.basedn), + fqdn=[fqdn4], + description=[u'Test host 4'], + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn4, api.env.realm)], + has_keytab=False, + has_password=False, + objectclass=objectclasses.host, + ipauniqueid=[fuzzy_uuid], + managedby_host=[fqdn4], + ), + ), + ), + + + dict( + desc='Create %r' % fqdn5, + command=('host_add', [fqdn5], + dict( + description=u'Test host 5', + l=u'Undisclosed location 1', + force=True, + ), + ), + expected=dict( + value=fqdn5, + summary=u'Added host "%s"' % fqdn5, + result=dict( + dn=u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn5, api.env.basedn), + fqdn=[fqdn5], + description=[u'Test host 5'], + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn5, api.env.realm)], + has_keytab=False, + has_password=False, + objectclass=objectclasses.host, + ipauniqueid=[fuzzy_uuid], + managedby_host=[fqdn5], + ), + ), + ), + + + dict( + desc='Retrieve %r' % hostgroup1, + command=('hostgroup_show', [hostgroup1], {}), + expected=dict( + value=hostgroup1, + summary=None, + result={ + 'dn': u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn), + 'member_host': [u'%s' % fqdn1], + 'cn': [hostgroup1], + 'description': [u'Test desc'], + 'memberof_netgroup': [u'hostgroup1'], + }, + ), + ), + + + dict( + desc='Retrieve %r' % defaulthostgroup1, + command=('hostgroup_show', [defaulthostgroup1], {}), + expected=dict( + value=defaulthostgroup1, + summary=None, + result={ + 'dn': u'cn=%s,cn=hostgroups,cn=accounts,%s' % (defaulthostgroup1, api.env.basedn), + 'member_host': [u'%s' % fqdn2], + 'cn': [defaulthostgroup1], + 'description': [u'Default test desc'], + 'memberof_netgroup': [u'defaulthostgroup1'], + }, + ), + ), + + + dict( + desc='Retrieve %r' % hostgroup2, + command=('hostgroup_show', [hostgroup2], {}), + expected=dict( + value=hostgroup2, + summary=None, + result={ + 'dn': u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup2, api.env.basedn), + 'member_host': [u'%s' % fqdn3], + 'cn': [hostgroup2], + 'description': [u'Test desc'], + 'memberof_netgroup': [u'hostgroup2'], + }, + ), + ), + + + dict( + desc='Retrieve %r' % hostgroup3, + command=('hostgroup_show', [hostgroup3], {}), + expected=dict( + value=hostgroup3, + summary=None, + result={ + 'dn': u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup3, api.env.basedn), + 'member_host': [u'%s' % fqdn4], + 'cn': [hostgroup3], + 'description': [u'Test desc'], + 'memberof_netgroup': [u'hostgroup3'], + }, + ), + ), + + + dict( + desc='Retrieve %r' % hostgroup4, + command=('hostgroup_show', [hostgroup4], {}), + expected=dict( + value=hostgroup4, + summary=None, + result={ + 'dn': u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup4, api.env.basedn), + 'member_host': [u'%s' % fqdn5], + 'cn': [hostgroup4], + 'description': [u'Test desc'], + 'memberof_netgroup': [u'hostgroup4'], + }, + ), + ), + + ]
\ No newline at end of file |