diff options
-rw-r--r-- | API.txt | 24 | ||||
-rw-r--r-- | ipalib/crud.py | 6 | ||||
-rw-r--r-- | ipalib/parameters.py | 3 | ||||
-rw-r--r-- | ipalib/plugins/automount.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/baseldap.py | 21 | ||||
-rw-r--r-- | ipalib/plugins/group.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/permission.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/privilege.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/role.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/user.py | 2 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_group_plugin.py | 22 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_host_plugin.py | 42 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_role_plugin.py | 10 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_user_plugin.py | 36 |
14 files changed, 145 insertions, 31 deletions
@@ -2036,12 +2036,12 @@ command: permission_add args: 1,12,3 arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, required=True) option: Str('permissions', attribute=True, cli_name='permissions', csv=True, multivalue=True, required=True) -option: Str('attrs', alwaysask=True, attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, query=True, required=False) -option: StrEnum('type', alwaysask=True, attribute=True, autofill=False, cli_name='type', multivalue=False, query=True, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord')) -option: Str('memberof', alwaysask=True, attribute=True, autofill=False, cli_name='memberof', multivalue=False, query=True, required=False) -option: Str('filter', alwaysask=True, attribute=True, autofill=False, cli_name='filter', multivalue=False, query=True, required=False) -option: Str('subtree', alwaysask=True, attribute=True, autofill=False, cli_name='subtree', multivalue=False, query=True, required=False) -option: Str('targetgroup', alwaysask=True, attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, query=True, required=False) +option: Str('attrs', alwaysask=True, attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, query=False, required=False) +option: StrEnum('type', alwaysask=True, attribute=True, autofill=False, cli_name='type', multivalue=False, query=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord')) +option: Str('memberof', alwaysask=True, attribute=True, autofill=False, cli_name='memberof', multivalue=False, query=False, required=False) +option: Str('filter', alwaysask=True, attribute=True, autofill=False, cli_name='filter', multivalue=False, query=False, required=False) +option: Str('subtree', alwaysask=True, attribute=True, autofill=False, cli_name='subtree', multivalue=False, query=False, required=False) +option: Str('targetgroup', alwaysask=True, attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, query=False, required=False) option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -2092,12 +2092,12 @@ command: permission_mod args: 1,15,3 arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True) option: Str('permissions', attribute=True, autofill=False, cli_name='permissions', csv=True, multivalue=True, required=False) -option: Str('attrs', alwaysask=True, attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, query=True, required=False) -option: StrEnum('type', alwaysask=True, attribute=True, autofill=False, cli_name='type', multivalue=False, query=True, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord')) -option: Str('memberof', alwaysask=True, attribute=True, autofill=False, cli_name='memberof', multivalue=False, query=True, required=False) -option: Str('filter', alwaysask=True, attribute=True, autofill=False, cli_name='filter', multivalue=False, query=True, required=False) -option: Str('subtree', alwaysask=True, attribute=True, autofill=False, cli_name='subtree', multivalue=False, query=True, required=False) -option: Str('targetgroup', alwaysask=True, attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, query=True, required=False) +option: Str('attrs', alwaysask=True, attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, query=False, required=False) +option: StrEnum('type', alwaysask=True, attribute=True, autofill=False, cli_name='type', multivalue=False, query=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord')) +option: Str('memberof', alwaysask=True, attribute=True, autofill=False, cli_name='memberof', multivalue=False, query=False, required=False) +option: Str('filter', alwaysask=True, attribute=True, autofill=False, cli_name='filter', multivalue=False, query=False, required=False) +option: Str('subtree', alwaysask=True, attribute=True, autofill=False, cli_name='subtree', multivalue=False, query=False, required=False) +option: Str('targetgroup', alwaysask=True, attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, query=False, required=False) option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('addattr*', cli_name='addattr', exclude='webui') option: Str('delattr*', cli_name='delattr', exclude='webui') diff --git a/ipalib/crud.py b/ipalib/crud.py index 833914cfa..b9dfb025e 100644 --- a/ipalib/crud.py +++ b/ipalib/crud.py @@ -144,7 +144,7 @@ class Create(Method): continue if 'ask_create' in option.flags: yield option.clone( - attribute=attribute, query=True, required=False, + attribute=attribute, query=False, required=False, autofill=False, alwaysask=True ) else: @@ -161,6 +161,8 @@ class PKQuery(Method): def get_args(self): if self.obj.primary_key: + # Don't enforce rules on the primary key so we can reference + # any stored entry, legal or not yield self.obj.primary_key.clone(attribute=True, query=True) @@ -189,7 +191,7 @@ class Update(PKQuery): continue if 'ask_update' in option.flags: yield option.clone( - attribute=attribute, query=True, required=False, + attribute=attribute, query=False, required=False, autofill=False, alwaysask=True ) elif 'req_update' in option.flags: diff --git a/ipalib/parameters.py b/ipalib/parameters.py index c533f9d0b..b1525b4d5 100644 --- a/ipalib/parameters.py +++ b/ipalib/parameters.py @@ -508,7 +508,8 @@ class Param(ReadOnly): self.class_rules = tuple(class_rules) self.rules = rules if self.query: - self.all_rules = self.class_rules + # by definition a query enforces no class or parameter rules + self.all_rules = () else: self.all_rules = self.class_rules + self.rules for rule in self.all_rules: diff --git a/ipalib/plugins/automount.py b/ipalib/plugins/automount.py index 8d743d75b..31c143d84 100644 --- a/ipalib/plugins/automount.py +++ b/ipalib/plugins/automount.py @@ -645,7 +645,7 @@ class automountkey(LDAPObject): default_attributes = [ 'automountkey', 'automountinformation', 'description' ] - rdnattr = 'description' + rdn_is_primary_key = True rdn_separator = ' ' takes_params = ( diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 725704ee0..2664160fa 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -429,7 +429,7 @@ class LDAPObject(Object): rdn_attribute = '' uuid_attribute = '' attribute_members = {} - rdnattr = None + rdn_is_primary_key = False # Do we need RDN change to do a rename? password_attributes = [] # Can bind as this entry (has userPassword or krbPrincipalKey) bindable = False @@ -1178,7 +1178,7 @@ class LDAPUpdate(LDAPQuery, crud.Update): has_output_params = global_output_params def _get_rename_option(self): - rdnparam = getattr(self.obj.params, self.obj.rdnattr) + rdnparam = getattr(self.obj.params, self.obj.primary_key.name) return rdnparam.clone_rename('rename', cli_name='rename', required=False, label=_('Rename'), doc=_('Rename the %(ldap_obj_name)s object') % dict( @@ -1189,7 +1189,7 @@ class LDAPUpdate(LDAPQuery, crud.Update): def get_options(self): for option in super(LDAPUpdate, self).get_options(): yield option - if self.obj.rdnattr: + if self.obj.rdn_is_primary_key: yield self._get_rename_option() def execute(self, *keys, **options): @@ -1229,18 +1229,19 @@ class LDAPUpdate(LDAPQuery, crud.Update): rdnupdate = False try: - if self.obj.rdnattr and 'rename' in options: + if self.obj.rdn_is_primary_key and 'rename' in options: if not options['rename']: raise errors.ValidationError(name='rename', error=u'can\'t be empty') - entry_attrs[self.obj.rdnattr] = options['rename'] + entry_attrs[self.obj.primary_key.name] = options['rename'] - if self.obj.rdnattr and self.obj.rdnattr in entry_attrs: + if self.obj.rdn_is_primary_key and self.obj.primary_key.name in entry_attrs: # RDN change - ldap.update_entry_rdn(dn, unicode('%s=%s' % (self.obj.rdnattr, - entry_attrs[self.obj.rdnattr]))) - rdnkeys = keys[:-1] + (entry_attrs[self.obj.rdnattr], ) + ldap.update_entry_rdn(dn, + unicode('%s=%s' % (self.obj.primary_key.name, + entry_attrs[self.obj.primary_key.name]))) + rdnkeys = keys[:-1] + (entry_attrs[self.obj.primary_key.name], ) dn = self.obj.get_dn(*rdnkeys) - del entry_attrs[self.obj.rdnattr] + del entry_attrs[self.obj.primary_key.name] options['rdnupdate'] = True rdnupdate = True diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py index b101d1285..096cb9eae 100644 --- a/ipalib/plugins/group.py +++ b/ipalib/plugins/group.py @@ -95,7 +95,7 @@ class group(LDAPObject): 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], } - rdnattr = 'cn' + rdn_is_primary_key = True label = _('User Groups') label_singular = _('User Group') diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py index c9fd5649f..ce2536d99 100644 --- a/ipalib/plugins/permission.py +++ b/ipalib/plugins/permission.py @@ -144,7 +144,7 @@ class permission(LDAPObject): attribute_members = { 'member': ['privilege'], } - rdnattr='cn' + rdn_is_primary_key = True label = _('Permissions') label_singular = _('Permission') diff --git a/ipalib/plugins/privilege.py b/ipalib/plugins/privilege.py index 53f76512e..53e1de223 100644 --- a/ipalib/plugins/privilege.py +++ b/ipalib/plugins/privilege.py @@ -60,7 +60,7 @@ class privilege(LDAPObject): reverse_members = { 'member': ['permission'], } - rdnattr='cn' + rdn_is_primary_key = True label = _('Privileges') label_singular = _('Privilege') diff --git a/ipalib/plugins/role.py b/ipalib/plugins/role.py index ee6ebcdc0..2837c418b 100644 --- a/ipalib/plugins/role.py +++ b/ipalib/plugins/role.py @@ -76,7 +76,7 @@ class role(LDAPObject): reverse_members = { 'member': ['privilege'], } - rdnattr='cn' + rdn_is_primary_key = True label = _('Roles') label_singular = _('Role') diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index d8da3a373..591132d36 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -168,7 +168,7 @@ class user(LDAPObject): 'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], } - rdnattr = 'uid' + rdn_is_primary_key = True bindable = True password_attributes = [('userpassword', 'has_password'), ('krbprincipalkey', 'has_keytab')] diff --git a/tests/test_xmlrpc/test_group_plugin.py b/tests/test_xmlrpc/test_group_plugin.py index 86c0d90da..3ef60f693 100644 --- a/tests/test_xmlrpc/test_group_plugin.py +++ b/tests/test_xmlrpc/test_group_plugin.py @@ -602,6 +602,28 @@ class test_group(Declarative): expected=errors.ValidationError(name='cn', error='may only include letters, numbers, _, -, . and $'), ), + # The assumption on these next 4 tests is that if we don't get a + # validation error then the request was processed normally. + dict( + desc='Test that validation is disabled on mods', + command=('group_mod', [invalidgroup1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on deletes', + command=('group_del', [invalidgroup1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on show', + command=('group_show', [invalidgroup1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + ##### managed entry tests dict( diff --git a/tests/test_xmlrpc/test_host_plugin.py b/tests/test_xmlrpc/test_host_plugin.py index 0323ac39a..4f24b6e39 100644 --- a/tests/test_xmlrpc/test_host_plugin.py +++ b/tests/test_xmlrpc/test_host_plugin.py @@ -47,6 +47,7 @@ dn3 = DN(('fqdn',fqdn3),('cn','computers'),('cn','accounts'), fqdn4 = u'testhost2.lab.%s' % api.env.domain dn4 = DN(('fqdn',fqdn4),('cn','computers'),('cn','accounts'), api.env.basedn) +invalidfqdn1 = u'foo_bar.lab.%s' % api.env.domain # We can use the same cert we generated for the service tests fd = open('tests/test_xmlrpc/service.crt', 'r') @@ -670,4 +671,45 @@ class test_host(Declarative): expected=errors.ValidationError(name='fqdn', error='An IPA master host cannot be deleted'), ), + dict( + desc='Test that validation is enabled on adds', + command=('host_add', [invalidfqdn1], {}), + expected=errors.ValidationError(name='fqdn', error='may only include letters, numbers, and -'), + ), + + + # The assumption on these next 4 tests is that if we don't get a + # validation error then the request was processed normally. + dict( + desc='Test that validation is disabled on mods', + command=('host_mod', [invalidfqdn1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on deletes', + command=('host_del', [invalidfqdn1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on show', + command=('host_show', [invalidfqdn1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on find', + command=('host_find', [invalidfqdn1], {}), + expected=dict( + count=0, + truncated=False, + summary=u'0 hosts matched', + result=[], + ), + ), + ] diff --git a/tests/test_xmlrpc/test_role_plugin.py b/tests/test_xmlrpc/test_role_plugin.py index 9c405cff7..f871e2683 100644 --- a/tests/test_xmlrpc/test_role_plugin.py +++ b/tests/test_xmlrpc/test_role_plugin.py @@ -33,6 +33,7 @@ role1 = u'test-role-1' role1_dn = DN(('cn',role1),api.env.container_rolegroup, api.env.basedn) renamedrole1 = u'test-role' +invalidrole1 = u' whitespace ' role2 = u'test-role-2' role2_dn = DN(('cn',role2),api.env.container_rolegroup, @@ -101,6 +102,15 @@ class test_role(Declarative): dict( + desc='Create invalid %r' % invalidrole1, + command=('role_add', [invalidrole1], + dict(description=u'role desc 1') + ), + expected=errors.ValidationError(name='cn', error='Leading and trailing spaces are not allowed'), + ), + + + dict( desc='Create %r' % role1, command=('role_add', [role1], dict(description=u'role desc 1') diff --git a/tests/test_xmlrpc/test_user_plugin.py b/tests/test_xmlrpc/test_user_plugin.py index d1cdb2f98..b21737f89 100644 --- a/tests/test_xmlrpc/test_user_plugin.py +++ b/tests/test_xmlrpc/test_user_plugin.py @@ -643,6 +643,42 @@ class test_user(Declarative): expected=errors.ValidationError(name='uid', error='can be at most 33 characters'), ), + + # The assumption on these next 4 tests is that if we don't get a + # validation error then the request was processed normally. + dict( + desc='Test that validation is disabled on deletes', + command=('user_del', [invaliduser1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on show', + command=('user_show', [invaliduser1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Test that validation is disabled on find', + command=('user_find', [invaliduser1], {}), + expected=dict( + count=0, + truncated=False, + summary=u'0 users matched', + result=[], + ), + ), + + + dict( + desc='Try to rename to invalid username %r' % user1, + command=('user_mod', [user1], dict(rename=invaliduser1)), + expected=errors.ValidationError(name='uid', error='may only include letters, numbers, _, -, . and $'), + ), + + dict( desc='Create %r' % group1, command=( |