diff options
author | Ana Krivokapic <akrivoka@redhat.com> | 2013-11-12 11:03:28 +0100 |
---|---|---|
committer | Petr Viktorin <pviktori@redhat.com> | 2013-11-19 14:27:50 +0100 |
commit | b216a7b6106be3a9e4b60144ca237dc3cedd8112 (patch) | |
tree | ed457dd92b3c8a7cd74edfcbac1c984a1252afab | |
parent | 2bc7803b69d15a246486ab5c8a44ead7593e8e90 (diff) | |
download | freeipa-b216a7b6106be3a9e4b60144ca237dc3cedd8112.tar.gz freeipa-b216a7b6106be3a9e4b60144ca237dc3cedd8112.tar.xz freeipa-b216a7b6106be3a9e4b60144ca237dc3cedd8112.zip |
Add userClass attribute for users
This new freeform user attribute will allow provisioning systems
to add custom tags for user objects which can be later used for
automember rules or for additional local interpretation.
Design page: http://www.freeipa.org/page/V3/Integration_with_a_provisioning_systems
https://fedorahosted.org/freeipa/ticket/3588
-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 dbb6a2de6..1d913542a 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 54d11c229..a7005faf1 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 4f30ec614..2f07e1495 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, |