On 11/12/2013 01:27 PM, Ana Krivokapic wrote:
> On 10/30/2013 09:56 PM, Martin Kosek wrote:
>> ----- Original Message -----
>>> From: "Simo Sorce" <s...@redhat.com>
>>> To: "Ana Krivokapic" <akriv...@redhat.com>
>>> Cc: "Martin Kosek" <mko...@redhat.com>, "freeipa-devel" 
>>> <freeipa-devel@redhat.com>
>>> Sent: Wednesday, October 30, 2013 7:11:20 PM
>>> Subject: Re: [Freeipa-devel] [PATCHES] 0080-0081 Add userClass attributes 
>>> for users and hosts
>>>
>>> On Wed, 2013-10-30 at 19:01 +0100, Ana Krivokapic wrote:
>>>> On 10/29/2013 02:04 PM, Simo Sorce wrote:
>>>>> On Tue, 2013-10-29 at 12:42 +0100, Martin Kosek wrote:
>>>>>> On 10/29/2013 10:49 AM, Ana Krivokapic wrote:
>>>>>>> Hello,
>>>>>>>
>>>>>>> Patch 0080 adds userClass attribute for users to IPA CLI.
>>>>>>> Patch 0081 adds userClass attribute for users and hosts to the web UI.
>>>>>>>
>>>>>>> Design page:
>>>>>>> http://www.freeipa.org/page/V3/Integration_with_a_provisioning_systems
>>>>>>>
>>>>>>> Tickets:
>>>>>>> https://fedorahosted.org/freeipa/ticket/3588
>>>>>>> https://fedorahosted.org/freeipa/ticket/3590
>>>>>> NACK to just extending posixAccount objectclass. This is a standard
>>>>>> objectclass
>>>>>> defined by RFC 2307 and we cannot just simply extend and overwrite it as
>>>>>> we wish.
>>>>> Uhh indeed this is a big No-no.
>>>>>
>>>>>> We will need to come up with some custom objectclass, like ipaUser. This
>>>>>> is the
>>>>>> reason why I wrote to ticket "A second goal of this ticket is to review
>>>>>> current
>>>>>> objectClass hierarchy of users and do changes if needed." so that we can
>>>>>> pick
>>>>>> the best option where to place it.
>>>>> userClass is used in ipaHost, so I guess it could be instead add to an
>>>>> ipa objectclass. ipaObject might be used perhaps, otherwise we'll need a
>>>>> new ipaUser objectlass.
>>>>>
>>>>> Simo.
>>>>>
>>>> If there are no objections to using the ipaObject objectclass, the attached
>>>> patches implement this approach.
>>> After some thinking ipaObject is more generic than just users, not sure
>>> that attaching userClass there is appropriate. I think we really need
>>> ipaUser at this point.
>> +1. I also do not think that ipaObject is the right OC to place the 
>> attribute, it is just too general.
>>
>> Let's go with the ipaUser objectClass, looking something like that:
>>
>> ( <OID> NAME 'ipaUser' AUXILIARY MUST ( uid ) MAY ( userClass ) X-ORIGIN 
>> 'IPA v3' )
>>
>> We will need to add the OC when needed, we cannot just add it to default 
>> list. Ideally, we could also implement
>> https://fedorahosted.org/freeipa/ticket/3922
>> in scope of this effort as this need to add additional OCs is piling up.
>>
>> Martin
> This implementation introduces a new objectclass 'ipaUser'.
>
>
>
> _______________________________________________
> Freeipa-devel mailing list
> Freeipa-devel@redhat.com
> https://www.redhat.com/mailman/listinfo/freeipa-devel

The web UI patch needed an update as well, as we need to allow writing the
userClass attribute even when the ipaUser objectclass is not (yet) set on the
user object. Thanks Petr for pointing it out.

Attaching both patches again (the CLI patch has not changed since the last
iteration).

-- 
Regards,

Ana Krivokapic
Associate Software Engineer
FreeIPA team
Red Hat Inc.

From ccc42e65ae9f6066e4427af82ddf283894cf0e4b Mon Sep 17 00:00:00 2001
From: Ana Krivokapic <akriv...@redhat.com>
Date: Fri, 25 Oct 2013 16:31:50 +0200
Subject: [PATCH] WebUI: Add userClass attribute to user and host pages

Add userClass attribute to:
- user and host adder dialogs
- user and host detail facets

Design page: http://www.freeipa.org/page/V3/Integration_with_a_provisioning_systems
https://fedorahosted.org/freeipa/ticket/3590
---
 install/ui/src/freeipa/host.js | 2 ++
 install/ui/src/freeipa/user.js | 9 +++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/install/ui/src/freeipa/host.js b/install/ui/src/freeipa/host.js
index f5007538e8ad1ea2e372c194b129f6c668d31b3e..460a19a9cda9a7d1d4457bb19f306dd88df8ceac 100644
--- a/install/ui/src/freeipa/host.js
+++ b/install/ui/src/freeipa/host.js
@@ -82,6 +82,7 @@ return {
                             $type: 'textarea',
                             name: 'description'
                         },
+                        'userclass',
                         'l',
                         'nshostlocation',
                         'nshardwareplatform',
@@ -234,6 +235,7 @@ return {
             {
                 name: 'other',
                 fields: [
+                    'userclass',
                     {
                         name: 'ip_address',
                         validators: [ 'ip_address' ],
diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js
index 61bdb43b4ee7d23a5d118c4f29ff81e3b9f56fa1..771d379be793462182123020651f2af286887ccc 100644
--- a/install/ui/src/freeipa/user.js
+++ b/install/ui/src/freeipa/user.js
@@ -103,7 +103,11 @@ return {
                         'cn',
                         'displayname',
                         'initials',
-                        'gecos'
+                        'gecos',
+                        {
+                            name: 'userclass',
+                            flags: ['w_if_no_aci']
+                        }
                     ]
                 },
                 {
@@ -306,7 +310,8 @@ return {
                         required: false
                     },
                     'givenname',
-                    'sn'
+                    'sn',
+                    'userclass'
                 ]
             },
             {
-- 
1.8.3.1

From f951c5fb6187c0aa5eb982bb7e22829364d58fb8 Mon Sep 17 00:00:00 2001
From: Ana Krivokapic <akriv...@redhat.com>
Date: Tue, 12 Nov 2013 11:03:28 +0100
Subject: [PATCH] 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
---
 API.txt                                  |  9 ++++--
 VERSION                                  |  2 +-
 install/share/60basev3.ldif              |  1 +
 install/updates/10-60basev3.update       |  3 ++
 ipalib/plugins/user.py                   | 22 +++++++++++++--
 ipatests/test_xmlrpc/test_user_plugin.py | 48 +++++++++++++++++++++++++++++---
 6 files changed, 74 insertions(+), 11 deletions(-)

diff --git a/API.txt b/API.txt
index cddb9d719f38dd3d89e2633148311e437aed0bc9..77619c53490bd598ebfe30f9c2c9ec1608f4c7d1 100644
--- a/API.txt
+++ b/API.txt
@@ -3587,7 +3587,7 @@ command: trustdomain_mod
 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')
@@ -3623,6 +3623,7 @@ command: user_add
 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))
@@ -3651,7 +3652,7 @@ command: user_enable
 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)
@@ -3696,6 +3697,7 @@ command: user_find
 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)
@@ -3704,7 +3706,7 @@ command: user_find
 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')
@@ -3741,6 +3743,7 @@ command: user_mod
 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))
diff --git a/VERSION b/VERSION
index 32f6efbc4d4768c77c514a3367cb9feb039205e5..0dacb97041d903733d3005045cac850e21f56d65 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=66
+IPA_API_VERSION_MINOR=67
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index b84789e25d75033f18fa5b70f69d852ddf35b7ca..2561bbbeea70973ce5d244c72cd1add244d4eb28 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -54,3 +54,4 @@ dn: cn=schema
 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/install/updates/10-60basev3.update b/install/updates/10-60basev3.update
index 476fa3ba5b194036e33fe7c8dd395bd42e243fb3..af72a31a05aa148e38da8911a83ba4c7c54268c9 100644
--- a/install/updates/10-60basev3.update
+++ b/install/updates/10-60basev3.update
@@ -20,3 +20,6 @@ dn: cn=schema
 # Add ipaUserAuthType and ipaUserAuthTypeClass
 add:attributeTypes: (2.16.840.1.113730.3.8.11.40 NAME 'ipaUserAuthType' DESC 'Allowed authentication methods' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v3')
 add: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')
+
+# Add ipaUser objectclass
+add:objectclasses: (2.16.840.1.113730.3.8.12.20 NAME 'ipaUser' AUXILIARY MUST ( uid ) MAY ( userClass ) X-ORIGIN 'IPA v3')
\ No newline at end of file
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index 54d11c229206034421b92b422932139d4c54c95a..a7005faf16821e7aba0df0ed1ee04ae23853f17d 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 @@ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         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 @@ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
             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 @@ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
                 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 4f30ec61458922d49d26d39d530cff986d189a16..2f07e1495ff31cac7ea4db2eb42453e065aec418 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,
-- 
1.8.3.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to