mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Ensure that the Apache server is in forked mode
Add ability to update existing users Try to prevent fetching and setting empty strings
This commit is contained in:
82
ipa-admintools/ipa-usermod
Normal file
82
ipa-admintools/ipa-usermod
Normal file
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -75,7 +75,6 @@ class RPCClient:
|
||||
|
||||
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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user