Rob Crittenden wrote:
Normalize and validate user principals in user and passwd plugins. The
uid in the principal should be lower-case.

rob

With updated API.txt

rob
>From 594bc2e0daf5ee92bbaf6fd3eaa5599bd5157d75 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcrit...@redhat.com>
Date: Fri, 16 Sep 2011 09:35:48 -0400
Subject: [PATCH] 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
---
 API.txt                                   |    6 +-
 ipalib/plugins/passwd.py                  |   11 +----
 ipalib/plugins/user.py                    |   47 +++++++++++++++++++++-
 tests/test_xmlrpc/test_hbactest_plugin.py |    4 +-
 tests/test_xmlrpc/test_user_plugin.py     |   62 ++++++++++++++++++++++++++++-
 5 files changed, 114 insertions(+), 16 deletions(-)

diff --git a/API.txt b/API.txt
index aee0c88..ac6560b 100644
--- a/API.txt
+++ b/API.txt
@@ -1830,7 +1830,7 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('value', <type 'unicode'>, "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=<lambda>, label=Gettext('User name', domain='ipa', localedir=None), primary_key=True)
+arg: Str('principal', validate_principal, autofill=True, cli_name='user', create_default=<lambda>, label=Gettext('User name', domain='ipa', localedir=None), normalizer=<lambda>, primary_key=True)
 arg: Password('password', label=Gettext('Password', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed')
 output: Output('result', <type 'bool'>, '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(<lambda>, '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(<lambda>, '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(<lambda>, '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(<lambda>, 'uid'), flags=['no_update'], label=Gettext('Kerberos principal', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, 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(<lambda>, '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(<lambda>, '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(<lambda>, '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(<lambda>, 'uid'), flags=['no_update'], label=Gettext('Kerberos principal', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, 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..31b7a4c 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]
+        # 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@b...@notfound.org' % user1)
+            ),
+            expected=errors.MalformedUserPrincipal(principal='%s@b...@notfound.org' % user1),
+        ),
+
+
     ]
-- 
1.7.6

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

Reply via email to