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
This commit is contained in:
Ana Krivokapic
2013-11-12 11:03:28 +01:00
committed by Petr Viktorin
parent 2bc7803b69
commit b216a7b610
5 changed files with 71 additions and 11 deletions

View File

@@ -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))

View File

@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=67
IPA_API_VERSION_MINOR=68

View File

@@ -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' )

View File

@@ -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):

View File

@@ -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,