From a1430dcb2c8e63e3077d00878431c0698944a07d Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Sep 22 2011 13:41:19 +0000 Subject: Normalize uid in user principal to lower-case and do validation Use same normalization and validation in passwd plugin and add some tests for invalid principals https://fedorahosted.org/freeipa/ticket/1778 --- diff --git a/API.txt b/API.txt index ba573a7..73652fe 100644 --- a/API.txt +++ b/API.txt @@ -1830,7 +1830,7 @@ output: Entry('result', , Gettext('A dictionary representing an LDA output: Output('value', , "The primary_key value of the entry, e.g. 'jdoe' for a user") command: passwd args: 2,0,3 -arg: Str('principal', autofill=True, cli_name='user', create_default=, label=Gettext('User name', domain='ipa', localedir=None), primary_key=True) +arg: Str('principal', validate_principal, autofill=True, cli_name='user', create_default=, label=Gettext('User name', domain='ipa', localedir=None), normalizer=, primary_key=True) arg: Password('password', label=Gettext('Password', domain='ipa', localedir=None)) output: Output('summary', (, ), 'User-friendly description of action performed') output: Output('result', , 'True means the operation was successful') @@ -2729,7 +2729,7 @@ option: Str('initials', attribute=True, autofill=True, cli_name='initials', defa option: Str('homedirectory', attribute=True, cli_name='homedir', default_from=DefaultFrom(, 'uid'), label=Gettext('Home directory', domain='ipa', localedir=None), multivalue=False, required=False) option: Str('gecos', attribute=True, autofill=True, cli_name='gecos', default_from=DefaultFrom(, 'givenname', 'sn'), label=Gettext('GECOS field', domain='ipa', localedir=None), multivalue=False, required=False) option: Str('loginshell', attribute=True, cli_name='shell', default=u'/bin/sh', label=Gettext('Login shell', domain='ipa', localedir=None), multivalue=False, required=False) -option: Str('krbprincipalname', attribute=True, autofill=True, cli_name='principal', default_from=DefaultFrom(, 'uid'), flags=['no_update'], label=Gettext('Kerberos principal', domain='ipa', localedir=None), multivalue=False, required=False) +option: Str('krbprincipalname', validate_principal, attribute=True, autofill=True, cli_name='principal', default_from=DefaultFrom(, 'uid'), flags=['no_update'], label=Gettext('Kerberos principal', domain='ipa', localedir=None), multivalue=False, normalizer=, required=False) option: Str('mail', attribute=True, cli_name='email', label=Gettext('Email address', domain='ipa', localedir=None), multivalue=True, required=False) option: Password('userpassword', attribute=True, cli_name='password', exclude='webui', label=Gettext('Password', domain='ipa', localedir=None), multivalue=False, required=False) option: Int('uidnumber', attribute=True, autofill=True, cli_name='uid', default=999, label=Gettext('UID', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False) @@ -2786,7 +2786,7 @@ option: Str('initials', attribute=True, autofill=False, cli_name='initials', def option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', default_from=DefaultFrom(, 'uid'), label=Gettext('Home directory', domain='ipa', localedir=None), multivalue=False, query=True, required=False) option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', default_from=DefaultFrom(, 'givenname', 'sn'), label=Gettext('GECOS field', domain='ipa', localedir=None), multivalue=False, query=True, required=False) option: Str('loginshell', attribute=True, autofill=False, cli_name='shell', default=u'/bin/sh', label=Gettext('Login shell', domain='ipa', localedir=None), multivalue=False, query=True, required=False) -option: Str('krbprincipalname', attribute=True, autofill=False, cli_name='principal', default_from=DefaultFrom(, 'uid'), flags=['no_update'], label=Gettext('Kerberos principal', domain='ipa', localedir=None), multivalue=False, query=True, required=False) +option: Str('krbprincipalname', validate_principal, attribute=True, autofill=False, cli_name='principal', default_from=DefaultFrom(, 'uid'), flags=['no_update'], label=Gettext('Kerberos principal', domain='ipa', localedir=None), multivalue=False, normalizer=, query=True, required=False) option: Str('mail', attribute=True, autofill=False, cli_name='email', label=Gettext('Email address', domain='ipa', localedir=None), multivalue=True, query=True, required=False) option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', label=Gettext('Password', domain='ipa', localedir=None), multivalue=False, query=True, required=False) option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', default=999, label=Gettext('UID', domain='ipa', localedir=None), minvalue=1, multivalue=False, query=True, required=False) diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py index 901a56f..b7d82f3 100644 --- a/ipalib/plugins/passwd.py +++ b/ipalib/plugins/passwd.py @@ -22,6 +22,7 @@ from ipalib import Command from ipalib import Str, Password from ipalib import _ from ipalib import output +from ipalib.plugins.user import split_principal, validate_principal, normalize_principal __doc__ = _(""" Set a user's password @@ -46,12 +47,13 @@ class passwd(Command): __doc__ = _("Set a user's password.") takes_args = ( - Str('principal', + Str('principal', validate_principal, cli_name='user', label=_('User name'), primary_key=True, autofill=True, create_default=lambda **kw: util.get_current_principal(), + normalizer=lambda value: normalize_principal(value), ), Password('password', label=_('Password'), @@ -75,13 +77,6 @@ class passwd(Command): """ ldap = self.api.Backend.ldap2 - if principal.find('@') != -1: - principal_parts = principal.split('@') - if len(principal_parts) > 2: - raise errors.MalformedUserPrincipal(principal=principal) - else: - principal = '%s@%s' % (principal, self.api.env.realm) - (dn, entry_attrs) = ldap.find_entry_by_attr( 'krbprincipalname', principal, 'posixaccount', [''], ",".join([api.env.container_user, api.env.basedn]) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 92a026d..35866d6 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -84,6 +84,48 @@ def convert_nsaccountlock(entry_attrs): nsaccountlock = Bool('temp') entry_attrs['nsaccountlock'] = nsaccountlock.convert(entry_attrs['nsaccountlock'][0]) +def split_principal(principal): + """ + Split the principal into its components and do some basic validation. + + Automatically append our realm if it wasn't provided. + """ + realm = None + parts = principal.split('@') + user = parts[0].lower() + if len(parts) > 2: + raise errors.MalformedUserPrincipal( + principal=principal + ) + + if len(parts) == 2: + realm = parts[1].upper() + # At some point we'll support multiple realms + if realm != api.env.realm: + raise errors.RealmMismatch() + else: + realm = api.env.realm + + return (user, realm) + +def validate_principal(ugettext, principal): + """ + All the real work is done in split_principal. + """ + (user, realm) = split_principal(principal) + return None + +def normalize_principal(principal): + """ + Ensure that the name in the principal is lower-case. The realm is + upper-case by convention but it isn't required. + + The principal is validated at this point. + """ + (user, realm) = split_principal(principal) + return unicode('%s@%s' % (user, realm)) + + class user(LDAPObject): """ User object. @@ -169,12 +211,13 @@ class user(LDAPObject): label=_('Login shell'), default=u'/bin/sh', ), - Str('krbprincipalname?', + Str('krbprincipalname?', validate_principal, cli_name='principal', label=_('Kerberos principal'), - default_from=lambda uid: '%s@%s' % (uid, api.env.realm), + default_from=lambda uid: '%s@%s' % (uid.lower(), api.env.realm), autofill=True, flags=['no_update'], + normalizer=lambda value: normalize_principal(value), ), Str('mail*', cli_name='email', diff --git a/tests/test_xmlrpc/test_hbactest_plugin.py b/tests/test_xmlrpc/test_hbactest_plugin.py index 37e3ad8..7e4607c 100644 --- a/tests/test_xmlrpc/test_hbactest_plugin.py +++ b/tests/test_xmlrpc/test_hbactest_plugin.py @@ -42,9 +42,9 @@ class test_hbactest(XMLRPC_test): test_user = u'hbacrule_test_user' test_group = u'hbacrule_test_group' - test_host = u'hbacrule._test_host' + test_host = u'hbacrule.test-host' test_hostgroup = u'hbacrule_test_hostgroup' - test_sourcehost = u'hbacrule._test_src_host' + test_sourcehost = u'hbacrule.test-src-host' test_sourcehostgroup = u'hbacrule_test_src_hostgroup' test_service = u'ssh' diff --git a/tests/test_xmlrpc/test_user_plugin.py b/tests/test_xmlrpc/test_user_plugin.py index 9392742..7a2489e 100644 --- a/tests/test_xmlrpc/test_user_plugin.py +++ b/tests/test_xmlrpc/test_user_plugin.py @@ -466,7 +466,6 @@ class test_user(Declarative): ), - dict( desc='Create %r' % user1, command=( @@ -704,4 +703,65 @@ class test_user(Declarative): ), + dict( + desc='Create user %r with upper-case principal' % user1, + command=( + 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', + krbprincipalname=user1.upper()) + ), + expected=dict( + value=user1, + summary=u'Added user "tuser1"', + result=dict( + gecos=[u'Test User1'], + givenname=[u'Test'], + homedirectory=[u'/home/tuser1'], + krbprincipalname=[u'tuser1@' + api.env.realm], + loginshell=[u'/bin/sh'], + objectclass=objectclasses.user, + sn=[u'User1'], + uid=[user1], + uidnumber=[fuzzy_digits], + gidnumber=[fuzzy_digits], + displayname=[u'Test User1'], + cn=[u'Test User1'], + initials=[u'TU'], + ipauniqueid=[fuzzy_uuid], + krbpwdpolicyreference=lambda x: [DN(i) for i in x] == \ + [DN(('cn','global_policy'),('cn',api.env.realm), + ('cn','kerberos'),api.env.basedn)], + mepmanagedentry=lambda x: [DN(i) for i in x] == \ + [DN(('cn',user1),('cn','groups'),('cn','accounts'), + api.env.basedn)], + memberof_group=[u'ipausers'], + has_keytab=False, + has_password=False, + dn=lambda x: DN(x) == \ + DN(('uid','tuser1'),('cn','users'),('cn','accounts'), + api.env.basedn), + ), + ), + ), + + + dict( + desc='Create user %r with bad realm in principal' % user1, + command=( + 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', + krbprincipalname='%s@NOTFOUND.ORG' % user1) + ), + expected=errors.RealmMismatch() + ), + + + dict( + desc='Create user %r with malformed principal' % user1, + command=( + 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', + krbprincipalname='%s@BAD@NOTFOUND.ORG' % user1) + ), + expected=errors.MalformedUserPrincipal(principal='%s@BAD@NOTFOUND.ORG' % user1), + ), + + ]