mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Second (final) part of xmlrpc patch.
This commit is contained in:
parent
4d656111e6
commit
1d8d4222ab
@ -1,5 +1,8 @@
|
||||
all:
|
||||
all: ;
|
||||
|
||||
install:
|
||||
$(MAKE) -C src $@
|
||||
|
||||
clean:
|
||||
clean:
|
||||
$(MAKE) -C src $@
|
||||
rm -f *~
|
||||
|
10
ipa-admintools/src/Makefile
Normal file
10
ipa-admintools/src/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
SBINDIR = $(DESTDIR)/usr/sbin
|
||||
|
||||
all: ;
|
||||
|
||||
install:
|
||||
install -m 755 ipa-adduser $(SBINDIR)
|
||||
install -m 755 ipa-finduser $(SBINDIR)
|
||||
|
||||
clean:
|
||||
rm -f *~ *.pyc
|
80
ipa-admintools/src/ipa-adduser
Normal file
80
ipa-admintools/src/ipa-adduser
Normal file
@ -0,0 +1,80 @@
|
||||
#! /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.rpcclient
|
||||
import xmlrpclib
|
||||
|
||||
def usage():
|
||||
print "ipa-adduser [-c|--gecos STRING] [-d|--directory STRING] [-f|--firstname STRING] [-l|--lastname 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("-f", "--firstname", dest="gn",
|
||||
help="User's first name")
|
||||
parser.add_option("-l", "--lastname", dest="sn",
|
||||
help="User's last name")
|
||||
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")
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if not options.gn or not options.sn:
|
||||
usage()
|
||||
|
||||
return options, args
|
||||
|
||||
def main():
|
||||
user={}
|
||||
(options, args) = parse_options()
|
||||
|
||||
if len(args) != 1:
|
||||
usage()
|
||||
|
||||
user['gn'] = options.gn
|
||||
user['sn'] = options.sn
|
||||
user['uid'] = args[0]
|
||||
if options.gecos:
|
||||
user['gecos'] = options.gecos
|
||||
if options.directory:
|
||||
user['homedirectory'] = options.directory
|
||||
if options.shell:
|
||||
user['loginshell'] = options.shell
|
||||
else
|
||||
user['loginshell'] = "/bin/bash"
|
||||
|
||||
try:
|
||||
ipa.rpcclient.add_user(user)
|
||||
print args[0] "successfully added"
|
||||
except xmlrpclib.Fault, f:
|
||||
print f.faultString
|
||||
|
||||
return 0
|
||||
|
||||
main()
|
58
ipa-admintools/src/ipa-finduser
Normal file
58
ipa-admintools/src/ipa-finduser
Normal file
@ -0,0 +1,58 @@
|
||||
#! /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
|
||||
#
|
||||
|
||||
from optparse import OptionParser
|
||||
import ipa
|
||||
import ipa.rpcclient
|
||||
import ipa.ipaldap
|
||||
import base64
|
||||
import sys
|
||||
import xmlrpclib
|
||||
|
||||
def usage():
|
||||
print "ipa-finduser <uid>"
|
||||
sys.exit()
|
||||
|
||||
def parse_options():
|
||||
parser = OptionParser()
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
return options, args
|
||||
|
||||
def main():
|
||||
user={}
|
||||
(options, args) = parse_options()
|
||||
|
||||
if len(args) != 1:
|
||||
usage()
|
||||
|
||||
try:
|
||||
ent = ipa.rpcclient.get_user(args[0])
|
||||
entry = ipa.ipaldap.Entry(ent['dn'])
|
||||
for e in ent:
|
||||
entry.setValues(e, ent[e])
|
||||
print entry
|
||||
except xmlrpclib.Fault, fault:
|
||||
print fault.faultString
|
||||
|
||||
return 0
|
||||
|
||||
main()
|
@ -19,3 +19,5 @@ clean:
|
||||
@for subdir in $(SUBDIRS); do \
|
||||
(cd $$subdir && $(MAKE) $@) || exit 1; \
|
||||
done
|
||||
rm -f *~
|
||||
rm -f ipaserver/*~
|
||||
|
9
ipa-server/ipa-web/Makefile
Normal file
9
ipa-server/ipa-web/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
all: ;
|
||||
|
||||
install:
|
||||
$(MAKE) -C api $@
|
||||
$(MAKE) -C client $@
|
||||
|
||||
clean:
|
||||
$(MAKE) -C api $@
|
||||
rm -f *~
|
12
ipa-server/ipa-web/api/Makefile
Normal file
12
ipa-server/ipa-web/api/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
SHAREDIR = $(DESTDIR)/usr/share/ipa
|
||||
HTTPDIR = $(DESTDIR)/etc/httpd/conf.d/
|
||||
|
||||
all: ;
|
||||
|
||||
install:
|
||||
-mkdir -p $(SHAREDIR)
|
||||
install -m 644 *.py $(SHAREDIR)
|
||||
install -m 644 ipa.conf $(HTTPDIR)
|
||||
|
||||
clean:
|
||||
rm -f *~ *.pyc
|
168
ipa-server/ipa-web/api/funcs.py
Normal file
168
ipa-server/ipa-web/api/funcs.py
Normal file
@ -0,0 +1,168 @@
|
||||
# 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 ldap
|
||||
import ipa
|
||||
import ipa.dsinstance
|
||||
import ipa.ipaldap
|
||||
import pdb
|
||||
import string
|
||||
from types import *
|
||||
import xmlrpclib
|
||||
|
||||
# FIXME, this needs to be auto-discovered
|
||||
host = 'localhost'
|
||||
port = 389
|
||||
binddn = "cn=directory manager"
|
||||
bindpw = "freeipa"
|
||||
|
||||
basedn = "dc=greyoak,dc=com"
|
||||
scope = ldap.SCOPE_SUBTREE
|
||||
|
||||
def get_user (username):
|
||||
"""Get a specific user's entry. Return as a dict of values.
|
||||
Multi-valued fields are represented as lists.
|
||||
"""
|
||||
ent=""
|
||||
|
||||
# FIXME: Is this the filter we want or should it be more specific?
|
||||
filter = "(uid=" username ")"
|
||||
try:
|
||||
m1 = ipa.ipaldap.IPAdmin(host,port,binddn,bindpw)
|
||||
ent = m1.getEntry(basedn, scope, filter, None)
|
||||
except ldap.LDAPError, e:
|
||||
raise xmlrpclib.Fault(1, e)
|
||||
except ipa.ipaldap.NoSuchEntryError:
|
||||
raise xmlrpclib.Fault(2, "No such user")
|
||||
|
||||
# Convert to LDIF
|
||||
entry = str(ent)
|
||||
|
||||
# Strip off any junk
|
||||
entry = entry.strip()
|
||||
|
||||
# Don't need to identify binary fields and this breaks the parser so
|
||||
# remove double colons
|
||||
entry = entry.replace('::', ':')
|
||||
specs = [spec.split(':') for spec in entry.split('\n')]
|
||||
|
||||
# Convert into a dict. We need to handle multi-valued attributes as well
|
||||
# so we'll convert those into lists.
|
||||
user={}
|
||||
for (k,v) in specs:
|
||||
k = k.lower()
|
||||
if user.get(k) is not None:
|
||||
if isinstance(user[k],list):
|
||||
user[k].append(v.strip())
|
||||
else:
|
||||
first = user[k]
|
||||
user[k] = []
|
||||
user[k].append(first)
|
||||
user[k].append(v.strip())
|
||||
else:
|
||||
user[k] = v.strip()
|
||||
|
||||
return user
|
||||
# return str(ent) # return as LDIF
|
||||
|
||||
def add_user (user):
|
||||
"""Add a user in LDAP"""
|
||||
dn="uid=%s,ou=users,ou=default,dc=greyoak,dc=com" % user['uid']
|
||||
entry = ipa.ipaldap.Entry(dn)
|
||||
|
||||
# some required objectclasses
|
||||
entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux')
|
||||
|
||||
# Fill in shadow fields
|
||||
entry.setValue('shadowMin', '0')
|
||||
entry.setValue('shadowMax', '99999')
|
||||
entry.setValue('shadowWarning', '7')
|
||||
entry.setValue('shadowExpire', '-1')
|
||||
entry.setValue('shadowInactive', '-1')
|
||||
entry.setValue('shadowFlag', '-1')
|
||||
|
||||
# FIXME: calculate shadowLastChange
|
||||
|
||||
# fill in our new entry with everything sent by the user
|
||||
for u in user:
|
||||
entry.setValues(u, user[u])
|
||||
|
||||
try:
|
||||
m1 = ipa.ipaldap.IPAdmin(host,port,binddn,bindpw)
|
||||
res = m1.addEntry(entry)
|
||||
return res
|
||||
except ldap.ALREADY_EXISTS:
|
||||
raise xmlrpclib.Fault(3, "User already exists")
|
||||
return None
|
||||
except ldap.LDAPError, e:
|
||||
raise xmlrpclib.Fault(1, str(e))
|
||||
return None
|
||||
|
||||
def get_add_schema ():
|
||||
"""Get the list of fields to be used when adding users in the GUI."""
|
||||
|
||||
# FIXME: this needs to be pulled from LDAP
|
||||
fields = []
|
||||
|
||||
field1 = {
|
||||
"name": "uid" ,
|
||||
"label": "Login:",
|
||||
"type": "text",
|
||||
"validator": "text",
|
||||
"required": "true"
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
field1 = {
|
||||
"name": "userPassword" ,
|
||||
"label": "Password:",
|
||||
"type": "password",
|
||||
"validator": "String",
|
||||
"required": "true"
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
field1 = {
|
||||
"name": "gn" ,
|
||||
"label": "First name:",
|
||||
"type": "text",
|
||||
"validator": "string",
|
||||
"required": "true"
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
field1 = {
|
||||
"name": "sn" ,
|
||||
"label": "Last name:",
|
||||
"type": "text",
|
||||
"validator": "string",
|
||||
"required": "true"
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
field1 = {
|
||||
"name": "mail" ,
|
||||
"label": "E-mail address:",
|
||||
"type": "text",
|
||||
"validator": "email",
|
||||
"required": "true"
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
return fields
|
24
ipa-server/ipa-web/api/ipa.conf
Normal file
24
ipa-server/ipa-web/api/ipa.conf
Normal file
@ -0,0 +1,24 @@
|
||||
# LoadModule auth_kerb_module modules/mod_auth_kerb.so
|
||||
|
||||
Alias /ipa "/usr/share/ipa/XMLRPC"
|
||||
|
||||
<Directory "/usr/share/ipa">
|
||||
# AuthType Kerberos
|
||||
# AuthName "Kerberos Login"
|
||||
# KrbMethodNegotiate on
|
||||
# KrbMethodK5Passwd off
|
||||
# KrbServiceName HTTP
|
||||
# KrbAuthRealms GREYOAK.COM
|
||||
# Krb5KeyTab /etc/httpd/conf/ipa.keytab
|
||||
# KrbSaveCredentials on
|
||||
# Require valid-user
|
||||
ErrorDocument 401 /errors/unauthorized.html
|
||||
|
||||
SetHandler mod_python
|
||||
PythonHandler ipaxmlrpc
|
||||
|
||||
PythonDebug Off
|
||||
|
||||
# this is pointless to use since it would just reload ipaxmlrpc.py
|
||||
PythonAutoReload Off
|
||||
</Directory>
|
288
ipa-server/ipa-web/api/ipaxmlrpc.py
Normal file
288
ipa-server/ipa-web/api/ipaxmlrpc.py
Normal file
@ -0,0 +1,288 @@
|
||||
# mod_python script
|
||||
|
||||
# ipaxmlrpc - an XMLRPC interface for ipa.
|
||||
# Copyright (c) 2007 Red Hat
|
||||
#
|
||||
# IPA is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation;
|
||||
# version 2.1 of the License.
|
||||
#
|
||||
# This software 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this software; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Based on kojixmlrpc - an XMLRPC interface for koji by
|
||||
# Mike McLean <mikem@redhat.com>
|
||||
#
|
||||
# Authors:
|
||||
# Rob Crittenden <rcritten@redhat.com>
|
||||
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import pprint
|
||||
from xmlrpclib import Marshaller,loads,dumps,Fault
|
||||
from mod_python import apache
|
||||
|
||||
import ipa
|
||||
import funcs
|
||||
import string
|
||||
import base64
|
||||
|
||||
#
|
||||
# An override so we can base64 encode all outgoing values.
|
||||
# This is set by calling: Marshaller._Marshaller__dump = xmlrpclib_dump
|
||||
#
|
||||
# Not currently used.
|
||||
#
|
||||
def xmlrpclib_escape(s, replace = string.replace):
|
||||
"""
|
||||
xmlrpclib only handles certain characters. Lets encode the whole
|
||||
blob
|
||||
"""
|
||||
|
||||
return base64.encodestring(s)
|
||||
|
||||
def xmlrpclib_dump(self, value, write):
|
||||
"""
|
||||
xmlrpclib cannot marshal instances of subclasses of built-in
|
||||
types. This function overrides xmlrpclib.Marshaller.__dump so that
|
||||
any value that is an instance of one of its acceptable types is
|
||||
marshalled as that type.
|
||||
|
||||
xmlrpclib also cannot handle invalid 7-bit control characters. See
|
||||
above.
|
||||
"""
|
||||
|
||||
# Use our escape function
|
||||
args = [self, value, write]
|
||||
if isinstance(value, (str, unicode)):
|
||||
args.append(xmlrpclib_escape)
|
||||
|
||||
try:
|
||||
# Try for an exact match first
|
||||
f = self.dispatch[type(value)]
|
||||
except KeyError:
|
||||
# Try for an isinstance() match
|
||||
for Type, f in self.dispatch.iteritems():
|
||||
if isinstance(value, Type):
|
||||
f(*args)
|
||||
return
|
||||
raise TypeError, "cannot marshal %s objects" % type(value)
|
||||
else:
|
||||
f(*args)
|
||||
|
||||
|
||||
class ModXMLRPCRequestHandler(object):
|
||||
"""Simple XML-RPC handler for mod_python environment"""
|
||||
|
||||
def __init__(self):
|
||||
self.funcs = {}
|
||||
self.traceback = False
|
||||
#introspection functions
|
||||
self.register_function(self.list_api, name="_listapi")
|
||||
self.register_function(self.system_listMethods, name="system.listMethods")
|
||||
self.register_function(self.system_methodSignature, name="system.methodSignature")
|
||||
self.register_function(self.system_methodHelp, name="system.methodHelp")
|
||||
self.register_function(self.multiCall)
|
||||
|
||||
def register_function(self, function, name = None):
|
||||
if name is None:
|
||||
name = function.__name__
|
||||
self.funcs[name] = function
|
||||
|
||||
def register_module(self, instance, prefix=None):
|
||||
"""Register all the public functions in an instance with prefix prepended
|
||||
|
||||
For example
|
||||
h.register_module(exports,"pub.sys")
|
||||
will register the methods of exports with names like
|
||||
pub.sys.method1
|
||||
pub.sys.method2
|
||||
...etc
|
||||
"""
|
||||
for name in dir(instance):
|
||||
if name.startswith('_'):
|
||||
continue
|
||||
function = getattr(instance, name)
|
||||
if not callable(function):
|
||||
continue
|
||||
if prefix is not None:
|
||||
name = "%s.%s" %(prefix,name)
|
||||
self.register_function(function, name=name)
|
||||
|
||||
def register_instance(self,instance):
|
||||
self.register_module(instance)
|
||||
|
||||
def _marshaled_dispatch(self, data):
|
||||
"""Dispatches an XML-RPC method from marshalled (XML) data."""
|
||||
|
||||
params, method = loads(data)
|
||||
|
||||
# special case
|
||||
# if method == "get_user":
|
||||
# Marshaller._Marshaller__dump = xmlrpclib_dump
|
||||
|
||||
start = time.time()
|
||||
# generate response
|
||||
try:
|
||||
response = self._dispatch(method, params)
|
||||
# wrap response in a singleton tuple
|
||||
response = (response,)
|
||||
response = dumps(response, methodresponse=1, allow_none=1)
|
||||
except Fault, fault:
|
||||
self.traceback = True
|
||||
response = dumps(fault)
|
||||
except:
|
||||
self.traceback = True
|
||||
# report exception back to server
|
||||
e_class, e = sys.exc_info()[:2]
|
||||
faultCode = getattr(e_class,'faultCode',1)
|
||||
tb_str = ''.join(traceback.format_exception(*sys.exc_info()))
|
||||
faultString = tb_str
|
||||
response = dumps(Fault(faultCode, faultString))
|
||||
|
||||
return response
|
||||
|
||||
def _dispatch(self,method,params):
|
||||
func = self.funcs.get(method,None)
|
||||
if func is None:
|
||||
raise Fault(1, "Invalid method: %s" % method)
|
||||
params,opts = ipa.decode_args(*params)
|
||||
|
||||
ret = func(*params,**opts)
|
||||
|
||||
return ret
|
||||
|
||||
def multiCall(self, calls):
|
||||
"""Execute a multicall. Execute each method call in the calls list, collecting
|
||||
results and errors, and return those as a list."""
|
||||
results = []
|
||||
for call in calls:
|
||||
try:
|
||||
result = self._dispatch(call['methodName'], call['params'])
|
||||
except Fault, fault:
|
||||
results.append({'faultCode': fault.faultCode, 'faultString': fault.faultString})
|
||||
except:
|
||||
# transform unknown exceptions into XML-RPC Faults
|
||||
# don't create a reference to full traceback since this creates
|
||||
# a circular reference.
|
||||
exc_type, exc_value = sys.exc_info()[:2]
|
||||
faultCode = getattr(exc_type, 'faultCode', 1)
|
||||
faultString = ', '.join(exc_value.args)
|
||||
trace = traceback.format_exception(*sys.exc_info())
|
||||
# traceback is not part of the multicall spec, but we include it for debugging purposes
|
||||
results.append({'faultCode': faultCode, 'faultString': faultString, 'traceback': trace})
|
||||
else:
|
||||
results.append([result])
|
||||
|
||||
return results
|
||||
|
||||
def list_api(self):
|
||||
funcs = []
|
||||
for name,func in self.funcs.items():
|
||||
#the keys in self.funcs determine the name of the method as seen over xmlrpc
|
||||
#func.__name__ might differ (e.g. for dotted method names)
|
||||
args = self._getFuncArgs(func)
|
||||
funcs.append({'name': name,
|
||||
'doc': func.__doc__,
|
||||
'args': args})
|
||||
return funcs
|
||||
|
||||
def _getFuncArgs(self, func):
|
||||
args = []
|
||||
for x in range(0, func.func_code.co_argcount):
|
||||
if x == 0 and func.func_code.co_varnames[x] == "self":
|
||||
continue
|
||||
if func.func_defaults and func.func_code.co_argcount - x <= len(func.func_defaults):
|
||||
args.append((func.func_code.co_varnames[x], func.func_defaults[x - func.func_code.co_argcount len(func.func_defaults)]))
|
||||
else:
|
||||
args.append(func.func_code.co_varnames[x])
|
||||
return args
|
||||
|
||||
def system_listMethods(self):
|
||||
return self.funcs.keys()
|
||||
|
||||
def system_methodSignature(self, method):
|
||||
#it is not possible to autogenerate this data
|
||||
return 'signatures not supported'
|
||||
|
||||
def system_methodHelp(self, method):
|
||||
func = self.funcs.get(method)
|
||||
if func is None:
|
||||
return ""
|
||||
arglist = []
|
||||
for arg in self._getFuncArgs(func):
|
||||
if isinstance(arg,str):
|
||||
arglist.append(arg)
|
||||
else:
|
||||
arglist.append('%s=%s' % (arg[0], arg[1]))
|
||||
ret = '%s(%s)' % (method, ", ".join(arglist))
|
||||
if func.__doc__:
|
||||
ret = "\ndescription: %s" % func.__doc__
|
||||
return ret
|
||||
|
||||
def handle_request(self,req):
|
||||
"""Handle a single XML-RPC request"""
|
||||
|
||||
# XMLRPC uses POST only. Reject anything else
|
||||
if req.method != 'POST':
|
||||
req.allow_methods(['POST'],1)
|
||||
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
|
||||
|
||||
response = self._marshaled_dispatch(req.read())
|
||||
|
||||
req.content_type = "text/xml"
|
||||
req.set_content_length(len(response))
|
||||
req.write(response)
|
||||
|
||||
|
||||
#
|
||||
# mod_python handler
|
||||
#
|
||||
|
||||
def handler(req, profiling=False):
|
||||
if profiling:
|
||||
import profile, pstats, StringIO, tempfile
|
||||
global _profiling_req
|
||||
_profiling_req = req
|
||||
temp = tempfile.NamedTemporaryFile()
|
||||
profile.run("import ipxmlrpc; ipaxmlrpc.handler(ipaxmlrpc._profiling_req, False)", temp.name)
|
||||
stats = pstats.Stats(temp.name)
|
||||
strstream = StringIO.StringIO()
|
||||
sys.stdout = strstream
|
||||
stats.sort_stats("time")
|
||||
stats.print_stats()
|
||||
req.write("<pre>" strstream.getvalue() "</pre>")
|
||||
_profiling_req = None
|
||||
else:
|
||||
opts = req.get_options()
|
||||
try:
|
||||
h = ModXMLRPCRequestHandler()
|
||||
h.register_function(funcs.get_user)
|
||||
h.register_function(funcs.add_user)
|
||||
h.register_function(funcs.get_add_schema)
|
||||
h.handle_request(req)
|
||||
finally:
|
||||
pass
|
||||
return apache.OK
|
||||
diff -r 0afcf345979d ipa-server/ipa-web/client/Makefile
|
||||
--- a/dev/null Thu Jan 01 00:00:00 1970 0000
|
||||
b/ipa-server/ipa-web/client/Makefile Wed Jul 19 20:17:24 2007 -0400
|
||||
PYTHONLIBDIR ?= $(shell python -c "from distutils.sysconfig import *; print get_python_lib(1)")
|
||||
PACKAGEDIR ?= $(DESTDIR)/$(PYTHONLIBDIR)/ipa
|
||||
|
||||
all: ;
|
||||
|
||||
install:
|
||||
-mkdir -p $(PACKAGEDIR)
|
||||
install -m 644 *.py $(PACKAGEDIR)
|
||||
|
||||
clean:
|
||||
rm -f *~ *.pyc
|
11
ipa-server/ipa-web/client/Makefile
Normal file
11
ipa-server/ipa-web/client/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
PYTHONLIBDIR ?= $(shell python -c "from distutils.sysconfig import *; print get_python_lib(1)")
|
||||
PACKAGEDIR ?= $(DESTDIR)/$(PYTHONLIBDIR)/ipa
|
||||
|
||||
all: ;
|
||||
|
||||
install:
|
||||
-mkdir -p $(PACKAGEDIR)
|
||||
install -m 644 *.py $(PACKAGEDIR)
|
||||
|
||||
clean:
|
||||
rm -f *~ *.pyc
|
395
ipa-server/ipa-web/client/ipaldap.py
Normal file
395
ipa-server/ipa-web/client/ipaldap.py
Normal file
@ -0,0 +1,395 @@
|
||||
#! /usr/bin/python -E
|
||||
# Authors: Rich Megginson <richm@redhat.com>
|
||||
# Rob Crittenden <rcritten2redhat.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 or later
|
||||
#
|
||||
# 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
|
||||
import os
|
||||
import os.path
|
||||
import popen2
|
||||
import base64
|
||||
import urllib
|
||||
import urllib2
|
||||
import socket
|
||||
import ldif
|
||||
import re
|
||||
import ldap
|
||||
import cStringIO
|
||||
import time
|
||||
import operator
|
||||
|
||||
from ldap.ldapobject import SimpleLDAPObject
|
||||
|
||||
class Error(Exception): pass
|
||||
class InvalidArgumentError(Error):
|
||||
def __init__(self,message): self.message = message
|
||||
def __repr__(self): return message
|
||||
class NoSuchEntryError(Error):
|
||||
def __init__(self,message): self.message = message
|
||||
def __repr__(self): return message
|
||||
|
||||
class Entry:
|
||||
"""This class represents an LDAP Entry object. An LDAP entry consists of a DN
|
||||
and a list of attributes. Each attribute consists of a name and a list of
|
||||
values. In python-ldap, entries are returned as a list of 2-tuples.
|
||||
Instance variables:
|
||||
dn - string - the string DN of the entry
|
||||
data - cidict - case insensitive dict of the attributes and values"""
|
||||
|
||||
def __init__(self,entrydata):
|
||||
"""data is the raw data returned from the python-ldap result method, which is
|
||||
a search result entry or a reference or None.
|
||||
If creating a new empty entry, data is the string DN."""
|
||||
if entrydata:
|
||||
if isinstance(entrydata,tuple):
|
||||
self.dn = entrydata[0]
|
||||
self.data = ldap.cidict.cidict(entrydata[1])
|
||||
elif isinstance(entrydata,str):
|
||||
self.dn = entrydata
|
||||
self.data = ldap.cidict.cidict()
|
||||
else:
|
||||
self.dn = ''
|
||||
self.data = ldap.cidict.cidict()
|
||||
|
||||
def __nonzero__(self):
|
||||
"""This allows us to do tests like if entry: returns false if there is no data,
|
||||
true otherwise"""
|
||||
return self.data != None and len(self.data) > 0
|
||||
|
||||
def hasAttr(self,name):
|
||||
"""Return True if this entry has an attribute named name, False otherwise"""
|
||||
return self.data and self.data.has_key(name)
|
||||
|
||||
def __getattr__(self,name):
|
||||
"""If name is the name of an LDAP attribute, return the first value for that
|
||||
attribute - equivalent to getValue - this allows the use of
|
||||
entry.cn
|
||||
instead of
|
||||
entry.getValue('cn')
|
||||
This also allows us to return None if an attribute is not found rather than
|
||||
throwing an exception"""
|
||||
return self.getValue(name)
|
||||
|
||||
def getValues(self,name):
|
||||
"""Get the list (array) of values for the attribute named name"""
|
||||
return self.data.get(name)
|
||||
|
||||
def getValue(self,name):
|
||||
"""Get the first value for the attribute named name"""
|
||||
return self.data.get(name,[None])[0]
|
||||
|
||||
def setValue(self,name,*value):
|
||||
"""Value passed in may be a single value, several values, or a single sequence.
|
||||
For example:
|
||||
ent.setValue('name', 'value')
|
||||
ent.setValue('name', 'value1', 'value2', ..., 'valueN')
|
||||
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
|
||||
tuple as in the last two examples above"""
|
||||
if isinstance(value[0],list) or isinstance(value[0],tuple):
|
||||
self.data[name] = value[0]
|
||||
else:
|
||||
self.data[name] = value
|
||||
|
||||
setValues = setValue
|
||||
|
||||
def toTupleList(self):
|
||||
"""Convert the attrs and values to a list of 2-tuples. The first element
|
||||
of the tuple is the attribute name. The second element is either a
|
||||
single value or a list of values."""
|
||||
return self.data.items()
|
||||
|
||||
def __str__(self):
|
||||
"""Convert the Entry to its LDIF representation"""
|
||||
return self.__repr__()
|
||||
|
||||
# the ldif class base64 encodes some attrs which I would rather see in raw form - to
|
||||
# encode specific attrs as base64, add them to the list below
|
||||
ldif.safe_string_re = re.compile('^$')
|
||||
base64_attrs = ['nsstate', 'krbprincipalkey', 'krbExtraData']
|
||||
|
||||
def __repr__(self):
|
||||
"""Convert the Entry to its LDIF representation"""
|
||||
sio = cStringIO.StringIO()
|
||||
# what's all this then? the unparse method will currently only accept
|
||||
# a list or a dict, not a class derived from them. self.data is a
|
||||
# cidict, so unparse barfs on it. I've filed a bug against python-ldap,
|
||||
# but in the meantime, we have to convert to a plain old dict for printing
|
||||
# I also don't want to see wrapping, so set the line width really high (1000)
|
||||
newdata = {}
|
||||
newdata.update(self.data)
|
||||
ldif.LDIFWriter(sio,Entry.base64_attrs,1000).unparse(self.dn,newdata)
|
||||
return sio.getvalue()
|
||||
|
||||
def wrapper(f,name):
|
||||
"""This is the method that wraps all of the methods of the superclass. This seems
|
||||
to need to be an unbound method, that's why it's outside of IPAdmin. Perhaps there
|
||||
is some way to do this with the new classmethod or staticmethod of 2.4.
|
||||
Basically, we replace every call to a method in SimpleLDAPObject (the superclass
|
||||
of IPAdmin) with a call to inner. The f argument to wrapper is the bound method
|
||||
of IPAdmin (which is inherited from the superclass). Bound means that it will implicitly
|
||||
be called with the self argument, it is not in the args list. name is the name of
|
||||
the method to call. If name is a method that returns entry objects (e.g. result),
|
||||
we wrap the data returned by an Entry class. If name is a method that takes an entry
|
||||
argument, we extract the raw data from the entry object to pass in."""
|
||||
def inner(*args, **kargs):
|
||||
if name == 'result':
|
||||
type, data = f(*args, **kargs)
|
||||
# data is either a 2-tuple or a list of 2-tuples
|
||||
# print data
|
||||
if data:
|
||||
if isinstance(data,tuple):
|
||||
return type, Entry(data)
|
||||
elif isinstance(data,list):
|
||||
return type, [Entry(x) for x in data]
|
||||
else:
|
||||
raise TypeError, "unknown data type %s returned by result" % type(data)
|
||||
else:
|
||||
return type, data
|
||||
elif name.startswith('add'):
|
||||
# the first arg is self
|
||||
# the second and third arg are the dn and the data to send
|
||||
# We need to convert the Entry into the format used by
|
||||
# python-ldap
|
||||
ent = args[0]
|
||||
if isinstance(ent,Entry):
|
||||
return f(ent.dn, ent.toTupleList(), *args[2:])
|
||||
else:
|
||||
return f(*args, **kargs)
|
||||
else:
|
||||
return f(*args, **kargs)
|
||||
return inner
|
||||
|
||||
class IPAdmin(SimpleLDAPObject):
|
||||
CFGSUFFIX = "o=NetscapeRoot"
|
||||
DEFAULT_USER_ID = "nobody"
|
||||
|
||||
def __initPart2(self):
|
||||
if self.binddn and len(self.binddn) and not hasattr(self,'sroot'):
|
||||
try:
|
||||
ent = self.getEntry('cn=config', ldap.SCOPE_BASE, '(objectclass=*)',
|
||||
[ 'nsslapd-instancedir', 'nsslapd-errorlog' ])
|
||||
instdir = ent.getValue('nsslapd-instancedir')
|
||||
self.sroot, self.inst = re.match(r'(.*)[\/]slapd-(\w)$', instdir).groups()
|
||||
self.errlog = ent.getValue('nsslapd-errorlog')
|
||||
except (ldap.INSUFFICIENT_ACCESS, ldap.CONNECT_ERROR, NoSuchEntryError):
|
||||
pass # usually means
|
||||
# print "ignored exception"
|
||||
except ldap.LDAPError, e:
|
||||
print "caught exception ", e
|
||||
raise
|
||||
|
||||
def __localinit__(self):
|
||||
SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port))
|
||||
# see if binddn is a dn or a uid that we need to lookup
|
||||
if self.binddn and not IPAdmin.is_a_dn(self.binddn):
|
||||
self.simple_bind("","") # anon
|
||||
ent = self.getEntry(IPAdmin.CFGSUFFIX, ldap.SCOPE_SUBTREE,
|
||||
"(uid=%s)" % self.binddn,
|
||||
['uid'])
|
||||
if ent:
|
||||
self.binddn = ent.dn
|
||||
else:
|
||||
print "Error: could not find %s under %s" % (self.binddn, IPAdmin.CFGSUFFIX)
|
||||
self.simple_bind(self.binddn,self.bindpw)
|
||||
# self.__initPart2()
|
||||
|
||||
def __init__(self,host,port,binddn,bindpw):
|
||||
"""We just set our instance variables and wrap the methods - the real work is
|
||||
done in __localinit__ and __initPart2 - these are separated out this way so
|
||||
that we can call them from places other than instance creation e.g. when
|
||||
using the start command, we just need to reconnect, not create a new instance"""
|
||||
self.__wrapmethods()
|
||||
self.port = port or 389
|
||||
self.sslport = 0
|
||||
self.host = host
|
||||
self.binddn = binddn
|
||||
self.bindpw = bindpw
|
||||
# see if is local or not
|
||||
host1 = IPAdmin.getfqdn(host)
|
||||
host2 = IPAdmin.getfqdn()
|
||||
self.isLocal = (host1 == host2)
|
||||
self.suffixes = {}
|
||||
self.__localinit__()
|
||||
|
||||
def __str__(self):
|
||||
return self.host ":" str(self.port)
|
||||
|
||||
def toLDAPURL(self):
|
||||
return "ldap://%s:%d/" % (self.host,self.port)
|
||||
|
||||
def getEntry(self,*args):
|
||||
"""This wraps the search function. It is common to just get one entry"""
|
||||
res = self.search(*args)
|
||||
type, obj = self.result(res)
|
||||
if not obj:
|
||||
raise NoSuchEntryError("no such entry for " str(args))
|
||||
elif isinstance(obj,Entry):
|
||||
return obj
|
||||
else: # assume list/tuple
|
||||
return obj[0]
|
||||
|
||||
def addEntry(self,*args):
|
||||
"""This wraps the add function. It assumes that the entry is already
|
||||
populated with all of the desired objectclasses and attributes"""
|
||||
try:
|
||||
self.add_s(*args)
|
||||
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
|
||||
of lists of hashes of arrays as the entry object, we want to wrap entries
|
||||
in an Entry class that provides some useful methods"""
|
||||
for name in dir(self.__class__.__bases__[0]):
|
||||
attr = getattr(self, name)
|
||||
if callable(attr):
|
||||
setattr(self, name, wrapper(attr, name))
|
||||
|
||||
def exportLDIF(self, file, suffix, forrepl=False, verbose=False):
|
||||
cn = "export" str(int(time.time()))
|
||||
dn = "cn=%s, cn=export, cn=tasks, cn=config" % cn
|
||||
entry = Entry(dn)
|
||||
entry.setValues('objectclass', 'top', 'extensibleObject')
|
||||
entry.setValues('cn', cn)
|
||||
entry.setValues('nsFilename', file)
|
||||
entry.setValues('nsIncludeSuffix', suffix)
|
||||
if forrepl:
|
||||
entry.setValues('nsExportReplica', 'true')
|
||||
|
||||
rc = self.startTaskAndWait(entry, verbose)
|
||||
|
||||
if rc:
|
||||
if verbose:
|
||||
print "Error: export task %s for file %s exited with %d" % (cn,file,rc)
|
||||
else:
|
||||
if verbose:
|
||||
print "Export task %s for file %s completed successfully" % (cn,file)
|
||||
return rc
|
||||
|
||||
def waitForEntry(self, dn, timeout=7200, attr='', quiet=False):
|
||||
scope = ldap.SCOPE_BASE
|
||||
filter = "(objectclass=*)"
|
||||
attrlist = []
|
||||
if attr:
|
||||
filter = "(%s=*)" % attr
|
||||
attrlist.append(attr)
|
||||
timeout = int(time.time())
|
||||
|
||||
if isinstance(dn,Entry):
|
||||
dn = dn.dn
|
||||
|
||||
# wait for entry and/or attr to show up
|
||||
if not quiet:
|
||||
sys.stdout.write("Waiting for %s %s:%s " % (self,dn,attr))
|
||||
sys.stdout.flush()
|
||||
entry = None
|
||||
while not entry and int(time.time()) < timeout:
|
||||
try:
|
||||
entry = self.getEntry(dn, scope, filter, attrlist)
|
||||
except NoSuchEntryError: pass # found entry, but no attr
|
||||
except ldap.NO_SUCH_OBJECT: pass # no entry yet
|
||||
except ldap.LDAPError, e: # badness
|
||||
print "\nError reading entry", dn, e
|
||||
break
|
||||
if not entry:
|
||||
if not quiet:
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
time.sleep(1)
|
||||
|
||||
if not entry and int(time.time()) > timeout:
|
||||
print "\nwaitForEntry timeout for %s for %s" % (self,dn)
|
||||
elif entry and not quiet:
|
||||
print "\nThe waited for entry is:", entry
|
||||
else:
|
||||
print "\nError: could not read entry %s from %s" % (dn,self)
|
||||
|
||||
return entry
|
||||
|
||||
def addSchema(self, attr, val):
|
||||
dn = "cn=schema"
|
||||
self.modify_s(dn, [(ldap.MOD_ADD, attr, val)])
|
||||
|
||||
def addAttr(self, *args):
|
||||
return self.addSchema('attributeTypes', args)
|
||||
|
||||
def addObjClass(self, *args):
|
||||
return self.addSchema('objectClasses', args)
|
||||
|
||||
###########################
|
||||
# Static methods start here
|
||||
###########################
|
||||
def normalizeDN(dn):
|
||||
# not great, but will do until we use a newer version of python-ldap
|
||||
# that has DN utilities
|
||||
ary = ldap.explode_dn(dn.lower())
|
||||
return ",".join(ary)
|
||||
normalizeDN = staticmethod(normalizeDN)
|
||||
|
||||
def getfqdn(name=''):
|
||||
return socket.getfqdn(name)
|
||||
getfqdn = staticmethod(getfqdn)
|
||||
|
||||
def getdomainname(name=''):
|
||||
fqdn = IPAdmin.getfqdn(name)
|
||||
index = fqdn.find('.')
|
||||
if index >= 0:
|
||||
return fqdn[index1:]
|
||||
else:
|
||||
return fqdn
|
||||
getdomainname = staticmethod(getdomainname)
|
||||
|
||||
def getdefaultsuffix(name=''):
|
||||
dm = IPAdmin.getdomainname(name)
|
||||
if dm:
|
||||
return "dc=" dm.replace('.', ', dc=')
|
||||
else:
|
||||
return 'dc=localdomain'
|
||||
getdefaultsuffix = staticmethod(getdefaultsuffix)
|
||||
|
||||
def getnewhost(args):
|
||||
"""One of the arguments to createInstance is newhost. If this is specified, we need
|
||||
to convert it to the fqdn. If not given, we need to figure out what the fqdn of the
|
||||
local host is. This method sets newhost in args to the appropriate value and
|
||||
returns True if newhost is the localhost, False otherwise"""
|
||||
isLocal = False
|
||||
if args.has_key('newhost'):
|
||||
args['newhost'] = IPAdmin.getfqdn(args['newhost'])
|
||||
myhost = IPAdmin.getfqdn()
|
||||
if myhost == args['newhost']:
|
||||
isLocal = True
|
||||
elif args['newhost'] == 'localhost' or \
|
||||
args['newhost'] == 'localhost.localdomain':
|
||||
isLocal = True
|
||||
else:
|
||||
isLocal = True
|
||||
args['newhost'] = IPAdmin.getfqdn()
|
||||
return isLocal
|
||||
getnewhost = staticmethod(getnewhost)
|
||||
|
||||
def is_a_dn(dn):
|
||||
"""Returns True if the given string is a DN, False otherwise."""
|
||||
return (dn.find("=") > 0)
|
||||
is_a_dn = staticmethod(is_a_dn)
|
102
ipa-server/ipa-web/client/rpcclient.py
Normal file
102
ipa-server/ipa-web/client/rpcclient.py
Normal file
@ -0,0 +1,102 @@
|
||||
#! /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 or later
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
#!/usr/bin/python
|
||||
|
||||
try:
|
||||
import krbV
|
||||
except ImportError:
|
||||
pass
|
||||
import xmlrpclib
|
||||
import socket
|
||||
import os
|
||||
import base64
|
||||
|
||||
# Some errors to catch
|
||||
# http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto
|
||||
|
||||
# FIXME: do we want this set somewhere else?
|
||||
server = xmlrpclib.ServerProxy("http://localhost:80/ipa")
|
||||
|
||||
def get_user(username):
|
||||
"""Get a specific user"""
|
||||
|
||||
try:
|
||||
result = server.get_user(username)
|
||||
myuser = result
|
||||
except xmlrpclib.Fault, fault:
|
||||
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
|
||||
return None
|
||||
except socket.error, (value, msg):
|
||||
raise xmlrpclib.Fault(value, msg)
|
||||
return None
|
||||
|
||||
return myuser
|
||||
|
||||
def add_user(user):
|
||||
"""Add a new user"""
|
||||
|
||||
# FIXME: Get the realm from somewhere
|
||||
realm="GREYOAK.COM"
|
||||
|
||||
# FIXME: This should be dynamic and can include just about anything
|
||||
# Let us add in some missing attributes
|
||||
if user.get('homeDirectory') is None:
|
||||
user['homeDirectory'] ='/home/%s' % user['uid']
|
||||
if user.get('gecos') is None:
|
||||
user['gecos'] = user['uid']
|
||||
|
||||
# FIXME: This can be removed once the DS plugin is installed
|
||||
user['uidNumber'] ='501'
|
||||
|
||||
# FIXME: What is the default group for users?
|
||||
user['gidNumber'] ='501'
|
||||
user['krbPrincipalName'] = "%s@%s" % (user['uid'], realm)
|
||||
user['cn'] = "%s %s" % (user['gn'], user['sn'])
|
||||
if user.get('gn'):
|
||||
del user['gn']
|
||||
|
||||
try:
|
||||
result = server.add_user(user)
|
||||
return result
|
||||
except xmlrpclib.Fault, fault:
|
||||
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
|
||||
return None
|
||||
except socket.error, (value, msg):
|
||||
raise xmlrpclib.Fault(value, msg)
|
||||
return None
|
||||
|
||||
def get_add_schema():
|
||||
"""Get the list of attributes we need to ask when adding a new
|
||||
user.
|
||||
"""
|
||||
|
||||
# FIXME: Hardcoded and designed for the TurboGears GUI. Do we want
|
||||
# this for the CLI as well?
|
||||
try:
|
||||
result = server.get_add_schema()
|
||||
except xmlrpclib.Fault, fault:
|
||||
raise xmlrpclib.Fault(fault,faultCode, fault.faultString)
|
||||
return None
|
||||
except socket.error, (value, msg):
|
||||
raise xmlrpclib.Fault(value, msg)
|
||||
return None
|
||||
|
||||
return result
|
Loading…
Reference in New Issue
Block a user