diff options
-rw-r--r-- | API.txt | 9 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | install/share/60basev3.ldif | 1 | ||||
-rw-r--r-- | ipalib/plugins/user.py | 22 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_user_plugin.py | 48 |
5 files changed, 71 insertions, 11 deletions
@@ -3596,7 +3596,7 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('value', <type 'unicode'>, None) command: user_add -args: 1,36,3 +args: 1,37,3 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -3632,6 +3632,7 @@ option: Str('street', attribute=True, cli_name='street', multivalue=False, requi option: Str('telephonenumber', attribute=True, cli_name='phone', multivalue=True, required=False) option: Str('title', attribute=True, cli_name='title', multivalue=False, required=False) option: Int('uidnumber', attribute=True, cli_name='uid', minvalue=1, multivalue=False, required=False) +option: Str('userclass', attribute=True, cli_name='class', multivalue=True, required=False) option: Password('userpassword', attribute=True, cli_name='password', exclude='webui', multivalue=False, required=False) option: Str('version?', exclude='webui') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) @@ -3660,7 +3661,7 @@ output: Output('result', <type 'bool'>, None) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('value', <type 'unicode'>, None) command: user_find -args: 1,46,4 +args: 1,47,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('carlicense', attribute=True, autofill=False, cli_name='carlicense', multivalue=False, query=True, required=False) @@ -3705,6 +3706,7 @@ option: Int('timelimit?', autofill=False, minvalue=0) option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, query=True, required=False) option: Str('uid', attribute=True, autofill=False, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, query=True, required=False) option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', minvalue=1, multivalue=False, query=True, required=False) +option: Str('userclass', attribute=True, autofill=False, cli_name='class', multivalue=True, query=True, required=False) option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', multivalue=False, query=True, required=False) option: Str('version?', exclude='webui') option: Flag('whoami', autofill=True, default=False) @@ -3713,7 +3715,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('truncated', <type 'bool'>, None) command: user_mod -args: 1,37,3 +args: 1,38,3 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -3750,6 +3752,7 @@ option: Str('street', attribute=True, autofill=False, cli_name='street', multiva option: Str('telephonenumber', attribute=True, autofill=False, cli_name='phone', multivalue=True, required=False) option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, required=False) option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', minvalue=1, multivalue=False, required=False) +option: Str('userclass', attribute=True, autofill=False, cli_name='class', multivalue=True, required=False) option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', multivalue=False, required=False) option: Str('version?', exclude='webui') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) @@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=67 +IPA_API_VERSION_MINOR=68 diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index dbb6a2de..1d913542 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -54,3 +54,4 @@ objectClasses: (2.16.840.1.113730.3.8.12.15 NAME 'ipaIDrange' ABSTRACT MUST ( cn objectClasses: (2.16.840.1.113730.3.8.12.16 NAME 'ipaDomainIDRange' SUP ipaIDrange STRUCTURAL MAY ( ipaBaseRID $ ipaSecondaryBaseRID ) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.17 NAME 'ipaTrustedADDomainRange' SUP ipaIDrange STRUCTURAL MUST ( ipaBaseRID $ ipaNTTrustedDomainSID ) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.19 NAME 'ipaUserAuthTypeClass' SUP top AUXILIARY DESC 'Class for authentication methods definition' MAY ipaUserAuthType X-ORIGIN 'IPA v3') +objectClasses: (2.16.840.1.113730.3.8.12.20 NAME 'ipaUser' AUXILIARY MUST ( uid ) MAY ( userClass ) X-ORIGIN 'IPA v3' ) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 54d11c22..a7005faf 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -198,14 +198,16 @@ class user(LDAPObject): object_name_plural = _('users') object_class = ['posixaccount'] object_class_config = 'ipauserobjectclasses' - possible_objectclasses = ['meporiginentry', 'ipauserauthtypeclass'] + possible_objectclasses = [ + 'meporiginentry', 'ipauserauthtypeclass', 'ipauser' + ] disallow_object_classes = ['krbticketpolicyaux'] search_attributes_config = 'ipausersearchfields' default_attributes = [ 'uid', 'givenname', 'sn', 'homedirectory', 'loginshell', 'uidnumber', 'gidnumber', 'mail', 'ou', 'telephonenumber', 'title', 'memberof', 'nsaccountlock', - 'memberofindirect', 'ipauserauthtype' + 'memberofindirect', 'ipauserauthtype', 'userclass' ] search_display_attributes = [ 'uid', 'givenname', 'sn', 'homedirectory', 'loginshell', @@ -372,6 +374,12 @@ class user(LDAPObject): values=(u'password',), csv=True, ), + Str('userclass*', + cli_name='class', + label=_('Class'), + doc=_('User category (semantics placed on this attribute are for ' + 'local interpretation)'), + ), ) def _normalize_and_validate_email(self, email, config=None): @@ -547,6 +555,11 @@ class user_add(LDAPCreate): if 'manager' in entry_attrs: entry_attrs['manager'] = self.obj._normalize_manager(entry_attrs['manager']) + if ('objectclass' in entry_attrs + and 'userclass' in entry_attrs + and 'ipauser' not in entry_attrs['objectclass']): + entry_attrs['objectclass'].append('ipauser') + return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): @@ -640,7 +653,8 @@ class user_mod(LDAPUpdate): entry_attrs['userpassword'] = ipa_generate_password(user_pwdchars) # save the password so it can be displayed in post_callback setattr(context, 'randompassword', entry_attrs['userpassword']) - if 'ipasshpubkey' in entry_attrs or 'ipauserauthtype' in entry_attrs: + if ('ipasshpubkey' in entry_attrs or 'ipauserauthtype' in entry_attrs + or 'userclass' in entry_attrs): if 'objectclass' in entry_attrs: obj_classes = entry_attrs['objectclass'] else: @@ -650,6 +664,8 @@ class user_mod(LDAPUpdate): obj_classes.append('ipasshuser') if 'ipauserauthtype' in entry_attrs and 'ipauserauthtype' not in obj_classes: obj_classes.append('ipauserauthtypeclass') + if 'userclass' in entry_attrs and 'ipauser' not in obj_classes: + obj_classes.append('ipauser') return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py index 4f30ec61..2f07e149 100644 --- a/ipatests/test_xmlrpc/test_user_plugin.py +++ b/ipatests/test_xmlrpc/test_user_plugin.py @@ -188,12 +188,28 @@ class test_user(Declarative): dict( desc='Create "%s"' % user1, command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1') + 'user_add', + [user1], + dict( + givenname=u'Test', + sn=u'User1', + userclass=u'testusers' + ) ), expected=dict( value=user1, summary=u'Added user "%s"' % user1, - result=get_user_result(user1, u'Test', u'User1', 'add'), + result=get_user_result( + user1, + u'Test', + u'User1', + 'add', + userclass=[u'testusers'], + objectclass=add_oc( + objectclasses.user, + u'ipantuserattrs' + ) + [u'ipauser'] + ), ), extra_check = upg_check, ), @@ -215,12 +231,27 @@ class test_user(Declarative): 'user_show', [user1], {} ), expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'show'), + result=get_user_result( + user1, + u'Test', + u'User1', + 'show', + userclass=[u'testusers'] + ), value=user1, summary=None, ), ), + dict( + desc='Remove userclass for user "%s"' % user1, + command=('user_mod', [user1], dict(userclass=u'')), + expected=dict( + result=get_user_result(user1, u'Test', u'User1', 'mod'), + value=user1, + summary=u'Modified user "%s"' % user1, + ), + ), dict( desc='Search for "%s" with all=True' % user1, @@ -229,7 +260,16 @@ class test_user(Declarative): ), expected=dict( result=[ - get_user_result(user1, u'Test', u'User1', 'show-all'), + get_user_result( + user1, + u'Test', + u'User1', + 'show-all', + objectclass=add_oc( + objectclasses.user, + u'ipantuserattrs' + ) + [u'ipauser'] + ), ], summary=u'1 user matched', count=1, truncated=False, |