summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrcritten@redhat.com <rcritten@redhat.com>2007-08-14 17:22:05 -0400
committerrcritten@redhat.com <rcritten@redhat.com>2007-08-14 17:22:05 -0400
commit5f0f23ee91d51b2a45206ce7d9ddc413d3fa9670 (patch)
tree92fd71833e1689e008ccb4844350c566ebfd0b16
parent547f501faee224d7d5d6998ce457fd2df0fd465d (diff)
downloadfreeipa-5f0f23ee91d51b2a45206ce7d9ddc413d3fa9670.tar.gz
freeipa-5f0f23ee91d51b2a45206ce7d9ddc413d3fa9670.tar.xz
freeipa-5f0f23ee91d51b2a45206ce7d9ddc413d3fa9670.zip
Ensure that the Apache server is in forked mode
Add ability to update existing users Try to prevent fetching and setting empty strings
-rw-r--r--ipa-admintools/ipa-usermod82
-rw-r--r--ipa-python/ipaclient.py6
-rw-r--r--ipa-python/rpcclient.py16
-rw-r--r--ipa-python/user.py13
-rw-r--r--ipa-server/ipaserver/ipaldap.py20
-rw-r--r--ipa-server/xmlrpc-server/funcs.py47
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py6
7 files changed, 188 insertions, 2 deletions
diff --git a/ipa-admintools/ipa-usermod b/ipa-admintools/ipa-usermod
new file mode 100644
index 000000000..d2aff4abf
--- /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 2c24e77d0..626e0a111 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 243b5485f..abff0f8fa 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 6162354be..3c925bfb3 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 7268594a9..08113b4c8 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 a17fe2f46..358f04f2a 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 963117f0e..7d8529d28 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