--- /dev/null
+FAULT_UNHANDLEDSERVEREXCEPTION = 901
+FAULT_BADREQUESTHASH = 902
--- /dev/null
+import traceback
+import xmlrpclib
+
+FAULTCODE = 900
+
+class UnhandledServerException(xmlrpclib.Fault):
+ def __init__(self, type, value, tb):
+ exc_str = ''.join(traceback.format_exception(type, value, tb))
+ faultString = exc_str # "Unhandled exception: " + str(type) + "\n" + exc_str
+ xmlrpclib.Fault.__init__(self, FAULTCODE + 1, faultString)
+
+class BadRequestHash(xmlrpclib.Fault):
+ def __init__(self, hash = None):
+ faultString = "bad request hash: " + str(hash)
+ xmlrpclib.Fault.__init__(self, FAULTCODE + 2, faultString)
--- /dev/null
+import xmlrpclib
+
+from BaseApi import BaseApi
+
+from sfa.trust.credential import Credential
+from sfa.trust.gid import GID
+from sfa.trust.trustedroot import TrustedRootList
+
+from ApiExceptionCodes import *
+
+class BadRequestHash(xmlrpclib.Fault):
+ def __init__(self, hash = None):
+ faultString = "bad request hash: " + str(hash)
+ xmlrpclib.Fault.__init__(self, FAULT_BADREQUESTHASH, faultString)
+
+class AuthenticatedApi(BaseApi):
+ def __init__(self, encoding = "utf-8", trustedRootsDir=None):
+ BaseApi.__init__(self, encoding)
+ if trustedRootsDir:
+ self.trusted_cert_list = TrustedRootList(trustedRootsDir).get_list()
+ else:
+ self.trusted_cert_list = None
+
+ def register_functions(self):
+ BaseApi.register_functions(self)
+ self.register_function(self.gidNoop)
+
+ def verifyGidRequestHash(self, gid, hash, arglist):
+ key = gid.get_pubkey()
+ if not key.verify_string(str(arglist), hash):
+ raise BadRequestHash(hash)
+
+ def verifyCredRequestHash(self, cred, hash, arglist):
+ gid = cred.get_gid_caller()
+ self.verifyGidRequestHash(gid, hash, arglist)
+
+ def validateGid(self, gid):
+ if self.trusted_cert_list:
+ gid.verify_chain(self.trusted_cert_list)
+
+ def validateCred(self, cred):
+ if self.trusted_cert_list:
+ cred.verify_chain(self.trusted_cert_list)
+ caller_gid = cred.get_gid_caller()
+ object_gid = cred.get_gid_object()
+ if caller_gid:
+ caller_gid.verify_chain(self.trusted_cert_list)
+ if object_gid:
+ object_gid.verify_chain(self.trusted_cert_list)
+
+ def authenticateGid(self, gidStr, argList, requestHash):
+ gid = GID(string = gidStr)
+ self.validateGid(gid)
+ self.verifyGidRequestHash(gid, requestHash, argList)
+ return gid
+
+ def authenticateCred(self, credStr, argList, requestHash):
+ cred = Credential(string = credStr)
+ self.validateCred(cred)
+ self.verifyCredRequestHash(cred, requestHash, argList)
+ return cred
+
+ def gidNoop(self, gidStr, value, requestHash):
+ self.authenticateGid(gidStr, [gidStr, value], requestHash)
+ return value
+
+ def credNoop(self, credStr, value, requestHash):
+ self.authenticateCred(credStr, [credStr, value], requestHash)
+ return value
+
+
--- /dev/null
+from sfa.trust.certificate import Keypair
+from sfa.trust.gid import GID
+
+from BaseClient import BaseClient
+
+class AuthenticatedClient(BaseClient):
+ def __init__(self, url, private_key_file, gid_file=None, cred_file=None):
+ BaseClient.__init__(self, url)
+ self.private_key_file = private_key_file
+ self.gid_file = gid_file
+ self.cred_file = cred_file
+ self.private_key = Keypair(filename = self.private_key_file)
+ if gid_file:
+ self.gid = GID(filename = self.gid_file)
+ if cred_file:
+ self.cred = Credential(filename = self.cred_file)
+
+ def computeRequestHash(self, argList):
+ return self.private_key.sign_string(str(argList))
+
+ def gidNoop(self, value):
+ gidStr = self.gid.save_to_string(True)
+ reqHash = self.computeRequestHash([gidStr, value])
+ return self.server.gidNoop(gidStr, value, reqHash)
--- /dev/null
+#
+# PLCAPI XML-RPC and SOAP interfaces
+#
+# Aaron Klingaman <alk@absarokasoft.com>
+# Mark Huang <mlhuang@cs.princeton.edu>
+#
+# Copyright (C) 2004-2006 The Trustees of Princeton University
+# $Id: API.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: https://svn.planet-lab.org/svn/PLCAPI/trunk/PLC/API.py $
+#
+
+import sys
+import traceback
+import string
+
+import xmlrpclib
+import logging
+import logging.handlers
+
+from ApiExceptionCodes import *
+
+# Wrapper around xmlrpc fault to include a traceback of the server to the
+# client. This is done to aid in debugging from a client perspective.
+
+class FaultWithTraceback(xmlrpclib.Fault):
+ def __init__(self, code, faultString, exc_info):
+ type, value, tb = exc_info
+ exc_str = ''.join(traceback.format_exception(type, value, tb))
+ faultString = faultString + "\nFAULT_TRACEBACK:" + exc_str
+ xmlrpclib.Fault.__init__(self, code, faultString)
+
+# Exception to report to the caller when some non-XMLRPC fault occurs on the
+# server. For example a TypeError.
+
+class UnhandledServerException(FaultWithTraceback):
+ def __init__(self, exc_info):
+ type, value, tb = exc_info
+ faultString = "Unhandled exception: " + str(type)
+ FaultWithTraceback.__init__(self, FAULT_UNHANDLEDSERVEREXCEPTION, faultString, exc_info)
+
+# See "2.2 Characters" in the XML specification:
+#
+# #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
+# avoiding
+# [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
+
+invalid_xml_ascii = map(chr, range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F))
+xml_escape_table = string.maketrans("".join(invalid_xml_ascii), "?" * len(invalid_xml_ascii))
+
+def xmlrpclib_escape(s, replace = string.replace):
+ """
+ xmlrpclib does not handle invalid 7-bit control characters. This
+ function augments xmlrpclib.escape, which by default only replaces
+ '&', '<', and '>' with entities.
+ """
+
+ # This is the standard xmlrpclib.escape function
+ s = replace(s, "&", "&")
+ s = replace(s, "<", "<")
+ s = replace(s, ">", ">",)
+
+ # Replace invalid 7-bit control characters with '?'
+ return s.translate(xml_escape_table)
+
+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)
+
+# You can't hide from me!
+xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump
+
+# SOAP support is optional
+try:
+ import SOAPpy
+ from SOAPpy.Parser import parseSOAPRPC
+ from SOAPpy.Types import faultType
+ from SOAPpy.NS import NS
+ from SOAPpy.SOAPBuilder import buildSOAP
+except ImportError:
+ SOAPpy = None
+
+def import_deep(name):
+ mod = __import__(name)
+ components = name.split('.')
+ for comp in components[1:]:
+ mod = getattr(mod, comp)
+ return mod
+
+class BaseApi:
+ def __init__(self, encoding = "utf-8"):
+ self.encoding = encoding
+ self.init_logger()
+ self.funcs = {}
+ self.register_functions()
+
+ def init_logger(self):
+ self.logger = logging.getLogger("ApiLogger")
+ self.logger.setLevel(logging.INFO)
+ self.logger.addHandler(logging.handlers.RotatingFileHandler(self.get_log_name(), maxBytes=100000, backupCount=5))
+
+ def get_log_name(self):
+ return "/tmp/apilogfile.txt"
+
+ def register_functions(self):
+ self.register_function(self.noop)
+
+ def register_function(self, function, name = None):
+ if name is None:
+ name = function.__name__
+ self.funcs[name] = function
+
+ def call(self, source, method, *args):
+ """
+ Call the named method from the specified source with the
+ specified arguments.
+ """
+
+ if not method in self.funcs:
+ raise "Unknown method: " + method
+
+ return self.funcs[method](*args)
+
+ def handle(self, source, data):
+ """
+ Handle an XML-RPC or SOAP request from the specified source.
+ """
+
+ # Parse request into method name and arguments
+ try:
+ interface = xmlrpclib
+ (args, method) = xmlrpclib.loads(data)
+ methodresponse = True
+ except Exception, e:
+ if SOAPpy is not None:
+ interface = SOAPpy
+ (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
+ method = r._name
+ args = r._aslist()
+ # XXX Support named arguments
+ else:
+ raise e
+
+ self.logger.debug("OP:" + str(method) + " from " + str(source))
+
+ try:
+ result = self.call(source, method, *args)
+ except xmlrpclib.Fault, fault:
+ self.logger.warning("FAULT: " + str(fault.faultCode) + " " + str(fault.faultString))
+ self.logger.info(traceback.format_exc())
+ # Handle expected faults
+ if interface == xmlrpclib:
+ result = FaultWithTraceback(fault.faultCode, fault.faultString, sys.exc_info())
+ methodresponse = None
+ elif interface == SOAPpy:
+ result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
+ result._setDetail("Fault %d: %s" % (fault.faultCode, fault.faultString))
+ self.logger.debug
+ except:
+ self.logger.warning("EXCEPTION: " + str(sys.exc_info()[0]))
+ self.logger.info(traceback.format_exc())
+ result = UnhandledServerException(sys.exc_info())
+ methodresponse = None
+
+ # Return result
+ if interface == xmlrpclib:
+ if not isinstance(result, xmlrpclib.Fault):
+ result = (result,)
+ data = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
+ elif interface == SOAPpy:
+ data = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
+
+ return data
+
+ def noop(self, value):
+ return value
+
+
+
--- /dev/null
+import xmlrpclib
+
+from ApiExceptionCodes import *
+
+VerboseExceptions = False
+
+def EnableVerboseExceptions(x=True):
+ global VerboseExceptions
+ VerboseExceptions = x
+
+class ExceptionUnmarshaller(xmlrpclib.Unmarshaller):
+ def close(self):
+ try:\r
+ return xmlrpclib.Unmarshaller.close(self)\r
+ except xmlrpclib.Fault, e:\r
+ # if the server tagged some traceback info onto the end of the\r
+ # exception text, then print it out on the client.\r
+\r
+ if "\nFAULT_TRACEBACK:" in e.faultString:\r
+ parts = e.faultString.split("\nFAULT_TRACEBACK:")\r
+ e.faultString = parts[0]\r
+ if VerboseExceptions:\r
+ print "\n|Server Traceback:", "\n|".join(parts[1].split("\n"))\r
+\r
+ raise e\r
+\r
+class ExceptionReportingTransport(xmlrpclib.Transport):
+ def make_connection(self, host):
+ import httplib\r
+ if host.startswith("https:"):\r
+ return httplib.HTTPS(host)\r
+ else:\r
+ return httplib.HTTP(host)\r
+\r
+ def getparser(self):\r
+ unmarshaller = ExceptionUnmarshaller()\r
+ parser = xmlrpclib.ExpatParser(unmarshaller)\r
+ return parser, unmarshaller
+
+class BaseClient():
+ def __init__(self, url):
+ self.url = url
+ self.server = xmlrpclib.ServerProxy(self.url, ExceptionReportingTransport())
+
+ def noop(self, value):
+ return self.server.noop(value)
--- /dev/null
+#
+# Apache mod_python interface
+#
+# Aaron Klingaman <alk@absarokasoft.com>
+# Mark Huang <mlhuang@cs.princeton.edu>
+#
+# Copyright (C) 2004-2006 The Trustees of Princeton University
+#
+
+import sys
+import traceback
+import xmlrpclib
+from mod_python import apache
+
+from API import RemoteApi
+api = RemoteApi()
+
+class unbuffered:
+ """\r
+ Write to /var/log/httpd/error_log. See\r
+\r
+ http://www.modpython.org/FAQ/faqw.py?req=edit&file=faq02.003.htp\r
+ """\r
+\r
+ def write(self, data):\r
+ sys.stderr.write(data)\r
+ sys.stderr.flush()\r
+\r
+#log = unbuffered()
+
+def handler(req):
+ try:
+ if req.method != "POST":
+ req.content_type = "text/html"
+ req.send_http_header()
+ req.write("""
+<html><head>
+<title>PLCAPI XML-RPC/SOAP Interface</title>
+</head><body>
+<h1>PLCAPI XML-RPC/SOAP Interface</h1>
+<p>Please use XML-RPC or SOAP to access the PLCAPI.</p>
+</body></html>
+""")
+ return apache.OK
+
+ # Read request
+ request = req.read(int(req.headers_in['content-length']))
+
+ # mod_python < 3.2: The IP address portion of remote_addr is
+ # incorrect (always 0.0.0.0) when IPv6 is enabled.
+ # http://issues.apache.org/jira/browse/MODPYTHON-64?page=all
+ (remote_ip, remote_port) = req.connection.remote_addr
+ remote_addr = (req.connection.remote_ip, remote_port)
+
+ # Handle request
+ response = api.handle(remote_addr, request)
+
+ # Write response
+ req.content_type = "text/xml; charset=" + api.encoding
+ req.send_http_header()
+ req.write(response)
+
+ return apache.OK
+
+ except Exception, err:
+ # Log error in /var/log/httpd/(ssl_)?error_log
+ print >> log, err, traceback.format_exc()
+ return apache.HTTP_INTERNAL_SERVER_ERROR
--- /dev/null
+from AuthenticatedApi import AuthenticatedApi, BadRequestHash
+
+class RemoteApi(AuthenticatedApi):
+ def __init__(self, encoding="utf-8", trustedRootsDir="/usr/local/testapi/var/trusted_roots"):
+ return AuthenticatedApi.__init__(self, encoding)
+
+ def get_log_name(self):
+ return "/usr/local/testapi/var/logfile.txt"
+
+ def register_functions(self):
+ AuthenticatedApi.register_functions(self)
+ self.register_function(self.typeError)
+ self.register_function(self.badRequestHash)
+
+ def typeError(self):
+ raise TypeError()
+
+ def badRequestHash(self):
+ raise BadRequestHash("somehashvalue")
--- /dev/null
+GENI_SRC_DIR=/home/smbaker/projects/geniwrapper/trunk
+
+mkdir -p /usr/local/testapi/bin
+mkdir -p /usr/local/testapi/bin/sfa/trust
+mkdir -p /usr/local/testapi/bin/sfa/util
+mkdir -p /usr/local/testapi/var/trusted_roots
+mkdir -p /repository/testapi
+
+# source code for the API
+cp BaseApi.py /usr/local/testapi/bin/
+cp AuthenticatedApi.py /usr/local/testapi/bin/
+cp TestApi.py /usr/local/testapi/bin/API.py
+cp ModPython.py /usr/local/testapi/bin/
+cp ApiExceptionCodes.py /usr/local/testapi/bin/
+
+# trusted root certificates that match gackstestuser.*
+cp trusted_roots/*.gid /usr/local/testapi/var/trusted_roots/
+
+# apache config file to enable the api
+cp testapi.conf /etc/httpd/conf.d/
+
+# copy over geniwrapper stuff that we need
+echo > /usr/local/testapi/bin/sfa/__init__.py
+echo > /usr/local/testapi/bin/sfa/trust/__init__.py
+echo > /usr/local/testapi/bin/sfa/util/__init__.py
+cp $GENI_SRC_DIR/sfa/trust/gid.py /usr/local/testapi/bin/sfa/trust/
+cp $GENI_SRC_DIR/sfa/trust/certificate.py /usr/local/testapi/bin/sfa/trust/
+cp $GENI_SRC_DIR/sfa/trust/trustedroot.py /usr/local/testapi/bin/sfa/trust/
+cp $GENI_SRC_DIR/sfa/trust/credential.py /usr/local/testapi/bin/sfa/trust/
+cp $GENI_SRC_DIR/sfa/trust/rights.py /usr/local/testapi/bin/sfa/trust/
+cp $GENI_SRC_DIR/sfa/util/faults.py /usr/local/testapi/bin/sfa/util/
+
+# make everything owned by apache
+chown -R apache /usr/local/testapi
+chown apache /etc/httpd/conf.d/testapi.conf
+
+/etc/init.d/httpd restart
\ No newline at end of file
--- /dev/null
+import sys
+import traceback
+
+from BaseClient import BaseClient, EnableVerboseExceptions
+from AuthenticatedClient import AuthenticatedClient
+
+EnableVerboseExceptions(True)
+
+HOST = "localhost"
+URL = "http://" + HOST + "/TESTAPI/"
+SURL = "https://" + HOST + "/TESTAPI/"
+
+print "*** testing some valid ops; these should print \"Hello, World\" ***"
+
+bc = BaseClient(URL)
+print "HTTP noop:", bc.noop("Hello, World")
+
+ac = AuthenticatedClient(URL, "gackstestuser.pkey", "gackstestuser.gid")
+print "HTTP gidNoop:", ac.gidNoop("Hello, World")
+
+bc = BaseClient(SURL)
+print "HTTPS noop:", bc.noop("Hello, World")
+
+ac = AuthenticatedClient(URL, "gackstestuser.pkey", "gackstestuser.gid")
+print "HTTPS gidNoop:", ac.gidNoop("Hello, World")
+
+print
+print "*** testing some exception handling: ***"
+
+bc = BaseClient(URL)
+print "HTTP typeError:",
+try:
+ result = bc.server.typeError()
+ print result
+except Exception, e:
+ print ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
+
+print "HTTP badrequesthash:",
+try:
+ result = bc.server.badRequestHash()
+ print result
+except:
+ print ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
+
--- /dev/null
+export PYTHONPATH=/home/smbaker/projects/geniwrapper/trunk
+
+python ./test.py
--- /dev/null
+<Location /TESTAPI/>
+ SetHandler mod_python
+ PythonPath "sys.path + ['/usr/local/testapi/bin/']"
+ PythonHandler ModPython
+</Location>
\ No newline at end of file