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
|
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
|
return result
|
||||||
|
|
||||||
|
|
||||||
def add_user(self,user):
|
def add_user(self,user):
|
||||||
"""Add a new user"""
|
"""Add a new user"""
|
||||||
server = self.setup_server()
|
server = self.setup_server()
|
||||||
@@ -135,3 +134,16 @@ class RPCClient:
|
|||||||
raise xmlrpclib.Fault(value, msg)
|
raise xmlrpclib.Fault(value, msg)
|
||||||
|
|
||||||
return result
|
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):
|
def getValue(self,name):
|
||||||
"""Get the first value for the attribute named name"""
|
"""Get the first value for the attribute named name"""
|
||||||
value = self.data.get(name,[None])
|
value = self.data.get(name,[None])
|
||||||
|
if (len(value) < 1):
|
||||||
|
return value
|
||||||
if isinstance(value[0],list) or isinstance(value[0],tuple):
|
if isinstance(value[0],list) or isinstance(value[0],tuple):
|
||||||
return value[0]
|
return value[0]
|
||||||
else:
|
else:
|
||||||
@@ -72,6 +74,8 @@ class User:
|
|||||||
ent.setValue('name', ('value1', 'value2', ..., 'valueN'))
|
ent.setValue('name', ('value1', 'value2', ..., 'valueN'))
|
||||||
Since *value is a tuple, we may have to extract a list or tuple from that
|
Since *value is a tuple, we may have to extract a list or tuple from that
|
||||||
tuple as in the last two examples above"""
|
tuple as in the last two examples above"""
|
||||||
|
if (len(value[0]) < 1):
|
||||||
|
return
|
||||||
if isinstance(value[0],list) or isinstance(value[0],tuple):
|
if isinstance(value[0],list) or isinstance(value[0],tuple):
|
||||||
self.data[name] = value[0]
|
self.data[name] = value[0]
|
||||||
else:
|
else:
|
||||||
@@ -85,6 +89,15 @@ class User:
|
|||||||
single value or a list of values."""
|
single value or a list of values."""
|
||||||
return self.data.items()
|
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):
|
def attrList(self):
|
||||||
"""Return a list of all attributes in the entry"""
|
"""Return a list of all attributes in the entry"""
|
||||||
return self.data.keys()
|
return self.data.keys()
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import time
|
|||||||
import operator
|
import operator
|
||||||
import struct
|
import struct
|
||||||
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
|
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
|
||||||
|
from ldap.modlist import modifyModlist
|
||||||
|
|
||||||
from ldap.ldapobject import SimpleLDAPObject
|
from ldap.ldapobject import SimpleLDAPObject
|
||||||
|
|
||||||
@@ -307,6 +308,25 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
raise e
|
raise e
|
||||||
return "Success"
|
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):
|
def __wrapmethods(self):
|
||||||
"""This wraps all methods of SimpleLDAPObject, so that we can intercept
|
"""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
|
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))
|
users.append(self.convert_entry(u))
|
||||||
|
|
||||||
return users
|
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):
|
def handle_request(self,req):
|
||||||
"""Handle a single XML-RPC request"""
|
"""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
|
# XMLRPC uses POST only. Reject anything else
|
||||||
if req.method != 'POST':
|
if req.method != 'POST':
|
||||||
req.allow_methods(['POST'],1)
|
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_add_schema)
|
||||||
h.register_function(f.get_all_users)
|
h.register_function(f.get_all_users)
|
||||||
h.register_function(f.find_users)
|
h.register_function(f.find_users)
|
||||||
|
h.register_function(f.update_user)
|
||||||
h.handle_request(req)
|
h.handle_request(req)
|
||||||
finally:
|
finally:
|
||||||
pass
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user