[Freeipa-devel] [PATCH] Use more krb

Rob Crittenden rcritten at redhat.com
Mon Oct 1 16:19:37 UTC 2007


Simo Sorce wrote:
> This patch changes a bit of code to rely more on kerberos.
> 
> It also changes ipa-adduser to something I think is a more useful
> workflow, it does not require an email for example as we don't need it.
> Instead it allows passing a principal name in case you need to create
> specific one or want to avoid conflicts with an exiting one.

What about the other tools?

> This patch also removes some classes we don't want to use by default for
> users.
> 
> Note: this patch may not apply cleanly as a pull from upstream after the
> commit required me a merge. I have the merge patch in my tree so just
> ack/nack it and I will push both the patch and the merge patch at the
> same time.

I'd rather ack the actual patch that is going to be committed.

Other comments in-line in the code, marked by []


# HG changeset patch
# User Simo Sorce <ssorce at redhat.com>
# Date 1191252940 14400
# Node ID fbee5ea59a1f622aadcbf14c545c877920fd1458
# Parent  e950c62a04f9d472d287bcf91012a2a1294cb014
Rely more on kerberos.
Don't read ipa.conf to get the realm, the kerberos libs do that for you.
Use the krbPrincipalName to change passwords
Make it possible to specify the principal at user creation.
Mail is not a required attribute so far, don't require it.

[ E-mail is required in the GUI so I made it required here for 
consistency. ]

diff -r e950c62a04f9 -r fbee5ea59a1f ipa-admintools/ipa-adduser
--- a/ipa-admintools/ipa-adduser	Fri Sep 28 14:55:28 2007 -0400
+++ b/ipa-admintools/ipa-adduser	Mon Oct 01 11:35:40 2007 -0400
@@ -28,6 +28,7 @@ import ipa.config

  import xmlrpclib
  import kerberos
+import krbV
  import ldap
  import getpass

@@ -51,8 +52,10 @@ def parse_options():
                        help="Set user's login shell to shell")
      parser.add_option("-G", "--groups", dest="groups",
                        help="Add account to one or more groups 
(comma-separated)")
+    parser.add_option("-k", "--krb-principal", dest="principal",
+                      help="Set user's Kerberos Principal Name")
      parser.add_option("-M", "--mailAddress", dest="mail",
-                      help="Set uesr's e-mail address")
+                      help="Set user's e-mail address")
      parser.add_option("--usage", action="store_true",
                        help="Program usage")

@@ -66,8 +69,9 @@ def main():
      givenname = ""
      lastname = ""
      username = ""
+    principal = ""
      password = ""
-    mail = ""
+    mail = ""
      gecos = ""
      directory = ""
      shell = ""
@@ -100,7 +104,7 @@ def main():
      cont = False
      if not options.sn:
          while (cont != True):
-            lastname = raw_input(" Last name: ")
+            lastname = raw_input("Last name: ")
              if (ipavalidate.plain(lastname, notEmpty=True)):
                  print "Field is required and must be letters or '"
              else:
@@ -140,18 +144,10 @@ def main():
      else:
          password = options.sn

-    cont = False
-    if not options.mail:
-        while (cont != True):
-            mail = raw_input("E-mail addr: ")
-            if (ipavalidate.email(mail)):
-                print "Field is required and must include a user and 
domain name"
-            else:
-                cont = True
-    else:
+    if options.mail:
          mail = options.mail
          if (ipavalidate.email(mail)):
-            print "E-mail is required and must include a user and 
domain name"
+            print "The email provided seem not a valid email."
              return 1

      # Ask the questions we don't normally force. We don't require answers
@@ -168,8 +164,10 @@ def main():
          cont = False
          if not options.directory:
              while (cont != True):
-                directory = raw_input("home directory []: ")
-                if (ipavalidate.path(gecos, notEmpty=False)):
+                directory = raw_input("home directory 
[/home/"+username+"]: ")
+                if directory == "":
+                     directory = "/home/"+username

[ /home should not be hardcoded here. It is currently hardcoded on the 
server side. This needs not be hardcoded anywhere but should be stored 
in LDAP somewhere. I'd rather not have to fix both places in the future 
though. ]

+                if (ipavalidate.path(directory, notEmpty=False)):
                      print "Must be letters, numbers, spaces or '"
                  else:
                      cont = True
@@ -180,29 +178,26 @@ def main():

                  if len(shell) < 1:
                      shell = None
-                    cont = True
-        cont = False
-        if not options.groups:
-            while (cont != True):
-                g = raw_input("Add to group [blank to exit]: ")
-
-                if len(g) < 1:
-                    cont = True
-                else:
-                    if (ipavalidate.path(g, notEmpty=False)):
-                        print "Must be letters, numbers, spaces or '"
-                    else:
-                        groups = groups + "," + g

[ Why not add groups at the same time? ]

+                cont = True
+
      else:
          gecos = options.gecos
          directory = options.directory
          shell = options.shell
          groups = options.groups

+    if options.principal:
+        principal = options.principal
+    else:
+        ctx = krbV.default_context()
+        principal = username + "@" + ctx.default_realm
+
      user.setValue('givenname', givenname)
      user.setValue('sn', lastname)
      user.setValue('uid', username)
-    user.setValue('mail', mail)
+    user.setValue('krbprincipalname', principal)
+    if mail:
+        user.setValue('mail', mail)
      if gecos:
          user.setValue('gecos', gecos)
      if directory:
@@ -231,7 +226,7 @@ def main():
      # Set the User's password
      if password is not None:
          try:
-            client.modifyPassword(username, None, password)
+            client.modifyPassword(principal, None, password)
          except ipa.ipaerror.IPAError, e:
              print "User added but setting the password failed."
              print "%s" % (e.message)
diff -r e950c62a04f9 -r fbee5ea59a1f ipa-admintools/ipa-passwd
--- a/ipa-admintools/ipa-passwd	Fri Sep 28 14:55:28 2007 -0400
+++ b/ipa-admintools/ipa-passwd	Mon Oct 01 11:35:40 2007 -0400
@@ -44,12 +44,12 @@ def parse_options():

      return options, args

-def get_principal():
+def get_principal(krbctx):
      try:
-        ctx = krbV.default_context()
-        ccache = ctx.default_ccache()
+        ccache = krbctx.default_ccache()
          cprinc = ccache.principal()
      except krbV.Krb5Error, e:
+        #TODO: do a kinit
          print "Unable to get kerberos principal: %s" % e[1]
          return None

@@ -57,39 +57,47 @@ def get_principal():

  def main():
      match = False
+    username = None
+    principal = None

+    krbctx = krbV.default_context()
      options, args = parse_options()

      if len(args) == 2:
          username = args[1]
      else:
-        username = get_principal()
-        if username is None:
+        principal = get_principal(krbctx)
+        if principal is None:
              return 1

-    u = username.split('@')
-    if len(u) > 1:
-        username = u[0]
+    if not principal:
+        u = username.split('@')
+        if len(u) > 2 or len(u) == 0:
+            print "Invalid user name (%s)" % username
+        if len(u) == 1:
+            principal = username+"@"+krbctx.default_realm
+        else:
+            principal = username

-    print "Changing password for %s" % username
+    print "Changing password for %s" % principal

      while (match != True):
          # No syntax checking of the password is required because that 
is done
          # on the server side
          password = getpass.getpass("  New Password: ")
-        confirm = getpass.getpass("  New Password (again): ")
+        confirm = getpass.getpass("  Confirm Password: ")
          if (password != confirm):
              print "Passwords do not match"
              match = False
+        elif (len(password) < 1):
+            print "Password cannot be empty"
+            match = False
          else:
              match = True
-            if (len(password) < 1):
-                print "Password cannot be empty"
-                match = False

      try:
          client = ipaclient.IPAClient()
-        client.modifyPassword(username, None, password)
+        client.modifyPassword(principal, None, password)
      except ipa.ipaerror.IPAError, e:
          print "%s" % (e.message)
          return 1
diff -r e950c62a04f9 -r fbee5ea59a1f ipa-python/freeipa-python.spec
--- a/ipa-python/freeipa-python.spec	Fri Sep 28 14:55:28 2007 -0400
+++ b/ipa-python/freeipa-python.spec	Mon Oct 01 11:35:40 2007 -0400
@@ -10,7 +10,7 @@ BuildRoot:      %{_tmppath}/%{name}-%{ve
  BuildRoot: 
%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
  BuildArch: 	noarch

-Requires: python PyKerberos python-krbV
+Requires: python PyKerberos

[ This looks like a mis-merge. python-krbV is required now ]

  %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}

diff -r e950c62a04f9 -r fbee5ea59a1f ipa-python/ipaclient.py
--- a/ipa-python/ipaclient.py	Fri Sep 28 14:55:28 2007 -0400
+++ b/ipa-python/ipaclient.py	Mon Oct 01 11:35:40 2007 -0400
@@ -34,7 +34,6 @@ class IPAClient:

      def __init__(self,local=None):
          self.local = local
-        ipa.config.init_config()
          if local:
              self.transport = funcs.IPAServer()
              # client needs to call set_principal(user at REALM)
@@ -80,8 +79,6 @@ class IPAClient:
      def add_user(self,user,user_container=None):
          """Add a user. user is a ipa.user.User object"""

-        realm = config.config.get_realm()
-
          user_dict = user.toDict()

          # dn is set on the server-side
@@ -125,30 +122,24 @@ class IPAClient:
      def update_user(self,user):
          """Update a user entry."""

-        realm = config.config.get_realm()
-
          result = self.transport.update_user(user.origDataDict(), 
user.toDict())
          return result

      def delete_user(self,uid):
          """Delete a user entry."""

-        realm = config.config.get_realm()
-
          result = self.transport.delete_user(uid)
          return result

-    def modifyPassword(self,uid,oldpass,newpass):
+    def modifyPassword(self,principal,oldpass,newpass):
          """Modify a user's password"""

-        result = self.transport.modifyPassword(uid,oldpass,newpass)
+        result = self.transport.modifyPassword(principal,oldpass,newpass)

          return result

      def mark_user_deleted(self,uid):
          """Set a user as inactive by uid."""
-
-        realm = config.config.get_realm()

          result = self.transport.mark_user_deleted(uid)
          return result
@@ -181,8 +172,6 @@ class IPAClient:
      def add_group(self,group,group_container=None):
          """Add a group. group is a ipa.group.Group object"""

-        realm = config.config.get_realm()
-
          group_dict = group.toDict()

          # dn is set on the server-side
@@ -237,6 +226,8 @@ class IPAClient:

      def add_user_to_group(self, user_uid, group_cn):
          """Add a user to an existing group.
+           user is a uid of the user to add
+           group is the cn of the group to be added to
          """

          return self.transport.add_user_to_group(user_uid, group_cn)
@@ -252,6 +243,8 @@ class IPAClient:

      def remove_user_from_group(self, user_uid, group_cn):
          """Remove a user from an existing group.
+           user is a uid of the user to remove
+           group is the cn of the group to be removed from
          """

          return self.transport.remove_user_from_group(user_uid, group_cn)
diff -r e950c62a04f9 -r fbee5ea59a1f ipa-python/rpcclient.py
--- a/ipa-python/rpcclient.py	Fri Sep 28 14:55:28 2007 -0400
+++ b/ipa-python/rpcclient.py	Mon Oct 01 11:35:40 2007 -0400
@@ -212,7 +212,7 @@ class RPCClient:

          return result

-    def modifyPassword(self,uid,oldpass,newpass):
+    def modifyPassword(self,principal,oldpass,newpass):
          """Modify a user's password"""
          server = self.setup_server()

@@ -220,7 +220,7 @@ class RPCClient:
              oldpass = "__NONE__"

          try:
-            result = server.modifyPassword(uid,oldpass,newpass)
+            result = server.modifyPassword(principal,oldpass,newpass)
          except xmlrpclib.Fault, fault:
              raise ipaerror.gen_exception(fault.faultCode, 
fault.faultString)
          except socket.error, (value, msg):
diff -r e950c62a04f9 -r fbee5ea59a1f 
ipa-server/ipa-gui/ipagui/controllers.py
--- a/ipa-server/ipa-gui/ipagui/controllers.py	Fri Sep 28 14:55:28 2007 
-0400
+++ b/ipa-server/ipa-gui/ipagui/controllers.py	Mon Oct 01 11:35:40 2007 
-0400
@@ -191,7 +191,7 @@ class Root(controllers.RootController):

          try:
              if password_change:
-                rv = client.modifyPassword(kw['uid'], "", 
kw.get('userpassword'))
+                rv = client.modifyPassword(kw['krbprincipalname'], "", 
kw.get('userpassword'))
          except ipaerror.IPAError, e:
              turbogears.flash("User password change failed: " + str(e))
              return dict(form=user_edit_form, user=kw,
diff -r e950c62a04f9 -r fbee5ea59a1f ipa-server/xmlrpc-server/funcs.py
--- a/ipa-server/xmlrpc-server/funcs.py	Fri Sep 28 14:55:28 2007 -0400
+++ b/ipa-server/xmlrpc-server/funcs.py	Mon Oct 01 11:35:40 2007 -0400
@@ -20,12 +20,12 @@ import sys
  import sys
  sys.path.append("/usr/share/ipa")

+import krbV
  import ldap
  import ipaserver.dsinstance
  import ipaserver.ipaldap
  import ipa.ipautil
  import xmlrpclib
-import ipa.config
  import copy
  from ipa import ipaerror

@@ -86,11 +86,12 @@ class IPAServer:
          self.bindcert = "/usr/share/ipa/cert.pem"
          self.bindkey = "/usr/share/ipa/key.pem"
          self.bindca = "/usr/share/ipa/cacert.asc"
-
+        self.krbctx = krbV.default_context()
+        self.realm = self.krbctx.default_realm
+
          if _LDAPPool is None:
              _LDAPPool = IPAConnPool()
-        ipa.config.init_config()
-        self.basedn = 
ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm())
+        self.basedn = ipa.ipautil.realm_to_suffix(self.realm)
          self.scope = ldap.SCOPE_SUBTREE
          self.princ = None
          self.krbccache = None
@@ -311,6 +312,15 @@ class IPAServer:
          filter = "(objectClass=*)"
          return self.__get_entry(dn, filter, sattrs, opts)

+    def get_user_by_principal(self, principal, sattrs=None, opts=None):
+        """Get a user entry searching by Kerberos Principal Name.
+           Return as a dict of values. Multi-valued fields are
+           represented as lists.
+        """
+
+        filter = "(krbPrincipalName="+self.__safe_filter(principal)+")"
+        return self.__get_entry(self.basedn, filter, sattrs, opts)
+
      def get_users_by_manager (self, manager_dn, sattrs=None, opts=None):
          """Gets the users that report to a particular manager.
          """
@@ -350,8 +360,7 @@ class IPAServer:
          # FIXME: What is the default group for users?
          user['gidnumber'] = '501'

-        realm = ipa.config.config.get_realm()
-        user['krbprincipalname'] = "%s@%s" % (user.get('uid'), realm)
+        user['krbprincipalname'] = "%s@%s" % (user.get('uid'), self.realm)

[ You need to check to see if krbprincipalname is already set ]

          # FIXME. This is a hack so we can request separate First and Last
          # name in the GUI.
@@ -562,31 +571,31 @@ class IPAServer:
             The memberOf plugin handles removing the user from any other
             groups.
          """
-        user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 
'objectclass'], opts)
-        if user_dn is None:
+        user = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], 
opts)
+        if user is None:
              raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)

          conn = self.getConnection(opts)
          try:
-            res = conn.deleteEntry(user_dn['dn'])
+            res = conn.deleteEntry(user['dn'])
          finally:
              self.releaseConnection(conn)
          return res

-    def modifyPassword (self, uid, oldpass, newpass, opts=None):
+    def modifyPassword (self, principal, oldpass, newpass, opts=None):
          """Set/Reset a user's password

             uid tells us who's password to change
             oldpass is the old password (if available)
             newpass is the new password
          """
-        user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 
'objectclass'], opts)
-        if user_dn is None:
+        user = self.get_user_by_principal(principal, 
['krbprincipalname'], opts)
+        if user is None or user['krbprincipalname'] != principal:
              raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)

[ If you do a search on principal how could user['principalname] ever != 
principal? ]

          conn = self.getConnection(opts)
          try:
-            res = conn.modifyPassword(user_dn['dn'], oldpass, newpass)
+            res = conn.modifyPassword(user['dn'], oldpass, newpass)
          finally:
              self.releaseConnection(conn)
          return res


-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 3245 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20071001/2961fb4e/attachment.bin>


More information about the Freeipa-devel mailing list