diff options
-rw-r--r-- | ipa-admintools/ipa-usermod | 82 | ||||
-rw-r--r-- | ipa-python/ipaclient.py | 6 | ||||
-rw-r--r-- | ipa-python/rpcclient.py | 16 | ||||
-rw-r--r-- | ipa-python/user.py | 13 | ||||
-rw-r--r-- | ipa-server/ipaserver/ipaldap.py | 20 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/funcs.py | 47 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/ipaxmlrpc.py | 6 |
7 files changed, 188 insertions, 2 deletions
diff --git a/ipa-admintools/ipa-usermod b/ipa-admintools/ipa-usermod new file mode 100644 index 00000000..d2aff4ab --- /dev/null +++ b/ipa-admintools/ipa-usermod @@ -0,0 +1,82 @@ +#! /usr/bin/python -E +# Authors: Rob Crittenden <rcritten@redhat.com> +# +# Copyright (C) 2007 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import sys +from optparse import OptionParser +import ipa +import ipa.ipaclient as ipaclient +import ipa.config + +import xmlrpclib +import kerberos + +def usage(): + print "ipa-usermod [-c|--gecos STRING] [-d|--directory STRING] user" + sys.exit(1) + +def parse_options(): + parser = OptionParser() + parser.add_option("-c", "--gecos", dest="gecos", + help="Set the GECOS field") + parser.add_option("-d", "--directory", dest="directory", + help="Set the User's home directory") + parser.add_option("-s", "--shell", dest="shell", + help="Set user's login shell to shell") + parser.add_option("--usage", action="store_true", + help="Program usage") + + args = ipa.config.init_config(sys.argv) + options, args = parser.parse_args(args) + + return options, args + +def main(): + olduser={} + newuser={} + options, args = parse_options() + + if len(args) != 2: + usage() + + client = ipaclient.IPAClient() + u = client.get_user(args[1]) + olduser = u.toDict() + newuser = u.toDict() + + if options.gecos: + newuser['gecos'] = options.gecos + if options.directory: + newuser['homedirectory'] = options.directory + if options.shell: + newuser['loginshell'] = options.shell + + try: + client.update_user(olduser, newuser) + print args[1] + " successfully modified" + except xmlrpclib.Fault, f: + print f.faultString + return 1 + except kerberos.GSSError, e: + print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0]) + return 1 + + return 0 + +main() diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index 2c24e77d..626e0a11 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -96,3 +96,9 @@ class IPAClient: return users + def update_user(self,olduser,newuser): + + realm = config.config.get_realm() + + result = self.transport.update_user(olduser,newuser) + return result diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index 243b5485..abff0f8f 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -74,8 +74,7 @@ class RPCClient: raise xmlrpclib.Fault(value, msg) return result - - + def add_user(self,user): """Add a new user""" server = self.setup_server() @@ -135,3 +134,16 @@ class RPCClient: raise xmlrpclib.Fault(value, msg) return result + + def update_user(self,olduser,newuser): + """Update an existing user. olduser and newuser are dicts of attributes""" + server = self.setup_server() + + try: + result = server.update_user(olduser, newuser) + except xmlrpclib.Fault, fault: + raise xmlrpclib.Fault(fault.faultCode, fault.faultString) + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) + + return result diff --git a/ipa-python/user.py b/ipa-python/user.py index 6162354b..3c925bfb 100644 --- a/ipa-python/user.py +++ b/ipa-python/user.py @@ -58,6 +58,8 @@ class User: def getValue(self,name): """Get the first value for the attribute named name""" value = self.data.get(name,[None]) + if (len(value) < 1): + return value if isinstance(value[0],list) or isinstance(value[0],tuple): return value[0] else: @@ -72,6 +74,8 @@ class User: ent.setValue('name', ('value1', 'value2', ..., 'valueN')) Since *value is a tuple, we may have to extract a list or tuple from that tuple as in the last two examples above""" + if (len(value[0]) < 1): + return if isinstance(value[0],list) or isinstance(value[0],tuple): self.data[name] = value[0] else: @@ -85,6 +89,15 @@ class User: single value or a list of values.""" return self.data.items() + def toDict(self): + """Convert the attrs and values to a dict. The dict is keyed on the + attribute name. The value is either single value or a list of values.""" + result = {} + for k in self.data.keys(): + result[k] = self.data[k] + result['dn'] = self.dn + return result + def attrList(self): """Return a list of all attributes in the entry""" return self.data.keys() diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index 7268594a..08113b4c 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -35,6 +35,7 @@ import time import operator import struct from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples +from ldap.modlist import modifyModlist from ldap.ldapobject import SimpleLDAPObject @@ -307,6 +308,25 @@ class IPAdmin(SimpleLDAPObject): raise e return "Success" + def updateEntry(self,dn,olduser,newuser): + """This wraps the mod function. It assumes that the entry is already + populated with all of the desired objectclasses and attributes""" + + sctrl = self.__get_server_controls__() + + # find the differences but don't remove attributes that are missing + # from the update + modlist = modifyModlist(olduser, newuser, None, 1) + + try: + self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) + self.modify_s(dn, modlist) + except ldap.ALREADY_EXISTS: + raise ldap.ALREADY_EXISTS + except ldap.LDAPError, e: + raise e + return "Success" + def __wrapmethods(self): """This wraps all methods of SimpleLDAPObject, so that we can intercept the methods that deal with entries. Instead of using a raw list of tuples diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index a17fe2f4..358f04f2 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -325,3 +325,50 @@ class IPAServer: users.append(self.convert_entry(u)) return users + + def update_user (self, args, newuser=None, opts=None): + """Update a user in LDAP""" + global _LDAPPool + + # The XML-RPC server marshals the arguments into one variable + # while the direct caller has them separate. So do a little + # bit of gymnastics to figure things out. There has to be a + # better way, so FIXME + if isinstance(args,tuple): + opts = newuser + if len(args) == 2: + olduser = args[0] + newuser = args[1] + else: + olduser = args + + if (isinstance(olduser, tuple)): + olduser = olduser[0] + if (isinstance(newuser, tuple)): + newuser = newuser[0] + + # Should be able to get this from either the old or new user + # but just in case someone has decided to try changing it, use the + # original + try: + moddn = olduser['dn'] + except KeyError, e: + raise xmlrpclib.Fault(4, "Old user has no dn") + + if opts: + self.set_principal(opts['remoteuser']) + + try: + proxydn = self.get_dn_from_principal(self.princ) + except ldap.LDAPError, e: + raise xmlrpclib.Fault(1, e) + except ipaserver.ipaldap.NoSuchEntryError: + raise xmlrpclib.Fault(2, "No such user") + + try: + m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn) + res = m1.updateEntry(moddn, olduser, newuser) + _LDAPPool.releaseConn(m1) + return res + except ldap.LDAPError, e: + raise xmlrpclib.Fault(1, str(e)) diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index 963117f0..7d8529d2 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -239,6 +239,11 @@ class ModXMLRPCRequestHandler(object): def handle_request(self,req): """Handle a single XML-RPC request""" + # The LDAP connection pool is not thread-safe. Avoid problems and + # force the forked model for now. + if not apache.mpm_query(apache.AP_MPMQ_IS_FORKED): + raise Fault(3, "Apache must use the forked model") + # XMLRPC uses POST only. Reject anything else if req.method != 'POST': req.allow_methods(['POST'],1) @@ -279,6 +284,7 @@ def handler(req, profiling=False): h.register_function(f.get_add_schema) h.register_function(f.get_all_users) h.register_function(f.find_users) + h.register_function(f.update_user) h.handle_request(req) finally: pass |