summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAna Krivokapic <akrivoka@redhat.com>2013-11-12 11:03:28 +0100
committerPetr Viktorin <pviktori@redhat.com>2013-11-19 14:27:50 +0100
commitb216a7b6106be3a9e4b60144ca237dc3cedd8112 (patch)
treeed457dd92b3c8a7cd74edfcbac1c984a1252afab
parent2bc7803b69d15a246486ab5c8a44ead7593e8e90 (diff)
downloadfreeipa-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.txt9
-rw-r--r--VERSION2
-rw-r--r--install/share/60basev3.ldif1
-rw-r--r--ipalib/plugins/user.py22
-rw-r--r--ipatests/test_xmlrpc/test_user_plugin.py48
5 files changed, 71 insertions, 11 deletions
diff --git a/API.txt b/API.txt
index b3b3a7e3..c29efad3 100644
--- a/API.txt
+++ b/API.txt
@@ -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))
diff --git a/VERSION b/VERSION
index 0dacb970..c036dc56 100644
--- a/VERSION
+++ b/VERSION
@@ -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,