added tickets
authorScott Baker <bakers@cs.arizona.edu>
Tue, 23 Sep 2008 05:45:43 +0000 (05:45 +0000)
committerScott Baker <bakers@cs.arizona.edu>
Tue, 23 Sep 2008 05:45:43 +0000 (05:45 +0000)
util/credential.py
util/geniclient.py
util/geniserver.py
util/geniticket.py [new file with mode: 0644]
util/hierarchy.py

index 7419076..c4e16ea 100644 (file)
@@ -27,6 +27,9 @@ class Credential(Certificate):
     def __init__(self, create=False, subject=None, string=None, filename=None):
         Certificate.__init__(self, create, subject, string, filename)
 
+    def create_similar(self):
+        return Credential()
+
     def set_gid_caller(self, gid):
         self.gidCaller = gid
 
@@ -83,9 +86,9 @@ class Credential(Certificate):
                 "privileges": None,
                 "delegate": self.delegate}
         if self.gidCaller:
-            dict["gidCaller"] = self.gidCaller.save_to_string()
+            dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True)
         if self.gidObject:
-            dict["gidObject"] = self.gidObject.save_to_string()
+            dict["gidObject"] = self.gidObject.save_to_string(save_parents=True)
         if self.privileges:
             dict["privileges"] = self.privileges.save_to_string()
         str = xmlrpclib.dumps((dict,), allow_none=True)
@@ -123,16 +126,16 @@ class Credential(Certificate):
         # do the normal certificate verification stuff
         Certificate.verify_chain(self, trusted_certs)
 
-        if parent:
+        if self.parent:
             # make sure the parent delegated rights to the child
-            if not parent.delegate:
-                raise MissingDelegateBit(self.get_subject())
+            if not self.parent.get_delegate():
+                raise MissingDelegateBit(self.parent.get_subject())
 
             # XXX todo: make sure child rights are a subset of parent rights
 
         return
 
-    def dump(self):
+    def dump(self, dump_parents=False):
         print "CREDENTIAL", self.get_subject()
 
         print "      privs:", self.get_privileges().save_to_string()
@@ -140,15 +143,19 @@ class Credential(Certificate):
         print "  gidCaller:"
         gidCaller = self.get_gid_caller()
         if gidCaller:
-            gidCaller.dump(indent=8)
+            gidCaller.dump(8, dump_parents)
 
         print "  gidObject:"
         gidObject = self.get_gid_object()
         if gidObject:
-            gidObject.dump(indent=8)
+            gidObject.dump(8, dump_parents)
 
         print "   delegate:", self.get_delegate()
 
+        if self.parent and dump_parents:
+           print "PARENT",
+           self.parent.dump(dump_parents)
+
 
 
 
index c85b8c1..2de52e2 100644 (file)
@@ -11,6 +11,22 @@ import xmlrpclib
 from gid import *
 from credential import *
 from record import *
+from geniticket import *
+
+# ServerException, ExceptionUnmarshaller
+#
+# Used to convert server exception strings back to an exception.
+#    from usenet, Raghuram Devarakonda
+
+class ServerException(Exception):
+    pass
+
+class ExceptionUnmarshaller(xmlrpclib.Unmarshaller):
+    def close(self):\r
+        try:\r
+            return xmlrpclib.Unmarshaller.close(self)\r
+        except xmlrpclib.Fault, e:\r
+            raise ServerException(e.faultString)
 
 # GeniTransport
 #
@@ -33,6 +49,11 @@ class GeniTransport(xmlrpclib.Transport):
         else:\r
             return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {}))\r
 \r
+    def getparser(self): \r
+        unmarshaller = ExceptionUnmarshaller() \r
+        parser = xmlrpclib.ExpatParser(unmarshaller) \r
+        return parser, unmarshaller\r
+\r
 # GeniClient:\r
 #\r
 # Class for performing GeniClient operations.\r
@@ -48,7 +69,15 @@ class GeniClient():
        self.transport = GeniTransport()
        self.transport.key_file = self.key_file
        self.transport.cert_file = self.cert_file
-       self.server = xmlrpclib.ServerProxy(self.url, self.transport)
+       self.server = xmlrpclib.ServerProxy(self.url, self.transport, allow_none=True)
+
+    # -------------------------------------------------------------------------
+    # Registry Interface
+    # -------------------------------------------------------------------------
+
+    def create_gid(self, cred, name, uuid, pkey_string):
+        gid_str = self.server.create_gid(cred.save_to_string(save_parents=True), name, uuid, pkey_string)
+        return GID(string=gid_str)
 
     def get_gid(self, name):
        gid_str_list = self.server.get_gid(name)
@@ -64,17 +93,72 @@ class GeniClient():
     # get_credential(..., cred=None,...)
 
     def get_self_credential(self, type, name):
-       cred_str = self.server.get_self_credential(type, name)
-       return Credential(string = cred_str)
+        cred_str = self.server.get_self_credential(type, name)
+        return Credential(string = cred_str)
 
     def get_credential(self, cred, type, name):
-       cred_str = self.server.get_credential(cred.save_to_string(), type, name)
-       return Credential(string = cred_str)
+        if cred == None:
+            return self.get_self_credential(type, name)
+        cred_str = self.server.get_credential(cred.save_to_string(save_parents=True), type, name)
+        return Credential(string = cred_str)
+
+    def list(self, cred):
+        result_dict_list = self.server.list(cred.save_to_string(save_parents=True))
+        result_rec_list = []
+        for dict in result_dict_list:
+             result_rec_list.append(GeniRecord(dict=dict))
+        return result_rec_list
+
+    def register(self, cred, record):
+        gid_str = self.server.register(cred.save_to_string(save_parents=True), record.as_dict())
+        return GID(string = gid_str)
+
+    def remove(self, cred, record):
+        result = self.server.remove(cred.save_to_string(save_parents=True), record.as_dict())
+        return result
 
     def resolve(self, cred, name):
-       result_dict_list = self.server.resolve(cred.save_to_string(), name)
-       result_rec_list = []
-       for dict in result_dict_list:
-            result_rec_list.append(GeniRecord(dict=dict))
-       return result_rec_list
+        result_dict_list = self.server.resolve(cred.save_to_string(save_parents=True), name)
+        result_rec_list = []
+        for dict in result_dict_list:
+             result_rec_list.append(GeniRecord(dict=dict))
+        return result_rec_list
+
+    def update(self, cred, record):
+        result = self.server.update(cred.save_to_string(save_parents=True), record.as_dict())
+        return result
+
+    # ------------------------------------------------------------------------
+    # Slice Interface
+    # ------------------------------------------------------------------------
+
+    def start_slice(self, cred):
+        result = self.server.start_slice(cred.save_to_string(save_parents=True))
+        return result
+
+    def stop_slice(self, cred):
+        result = self.server.stop_slice(cred.save_to_string(save_parents=True))
+        return result
+
+    def reset_slice(self, cred):
+        result = self.server.reset_slice(cred.save_to_string(save_parents=True))
+        return result
+
+    def delete_slice(self, cred):
+        result = self.server.delete_slice(cred.save_to_string(save_parents=True))
+        return result
+
+    def list_slices(self, cred):
+        result = self.server.list_slices(cred.save_to_string(save_parents=True))
+        return result
+
+    def get_ticket(self, cred, name, rspec):
+        ticket_str = self.server.get_ticket(cred.save_to_string(save_parents=True), name, rspec)
+        ticket = Ticket(string=ticket_str)
+        return ticket
+
+    def redeem_ticket(self, ticket):
+        result = self.server.redeem_ticket(ticket.save_to_string(save_parents=True))
+        return result
+
 
index c4cd60a..b58080d 100644 (file)
@@ -9,6 +9,8 @@
 
 import SimpleXMLRPCServer
 
+import sys
+import traceback
 import SocketServer
 import BaseHTTPServer\r
 import SimpleHTTPServer\r
@@ -84,7 +86,7 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
         """\r
         self.logRequests = logRequests\r
 \r
-        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, None, None)\r
+        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, True, None)\r
         SocketServer.BaseServer.__init__(self, server_address, HandlerClass)\r
         ctx = SSL.Context(SSL.SSLv23_METHOD)\r
         ctx.use_privatekey_file(key_file)\r
@@ -96,6 +98,20 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
         self.server_bind()\r
         self.server_activate()\r
 \r
+    # _dispatch\r
+    #
+    # Convert an exception on the server to a full stack trace and send it to
+    # the client.
+
+    def _dispatch(self, method, params):\r
+        try:\r
+            return SimpleXMLRPCServer.SimpleXMLRPCDispatcher._dispatch(self, method, params)\r
+        except:\r
+            # can't use format_exc() as it is not available in jython yet (even\r
+            # in trunk).\r
+            type, value, tb = sys.exc_info()\r
+            raise xmlrpclib.Fault(1,''.join(traceback.format_exception(type, value, tb)))\r
+\r
 # SecureXMLRpcRequestHandler\r
 #\r
 # taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler\r
@@ -130,6 +146,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
         except: # This should only happen if the module is buggy\r
             # internal error, report as HTTP server error\r
             self.send_response(500)\r
+\r
             self.end_headers()\r
         else:\r
             # got a valid XML RPC response\r
@@ -158,9 +175,10 @@ class GeniServer():
         self.key = Keypair(filename = key_file)
         self.cert = Certificate(filename = cert_file)
         self.server = SecureXMLRPCServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file)
+        self.trusted_cert_list = None
         self.register_functions()
 
-    def decode_authentication(self, cred_string):
+    def decode_authentication(self, cred_string, operation):
         self.client_cred = Credential(string = cred_string)
         self.client_gid = self.client_cred.get_gid_caller()
         self.object_gid = self.client_cred.get_gid_object()
@@ -174,6 +192,17 @@ class GeniServer():
         if not peer_cert.is_pubkey(self.client_gid.get_pubkey()):
             raise ConnectionKeyGIDMismatch(self.client_gid.get_subject())
 
+        # make sure the client is allowed to perform the operation
+        if not self.client_cred.can_perform(operation):
+            raise InsufficientRights(operation)
+
+        if self.trusted_cert_list:
+            self.client_cred.verify_chain(self.trusted_cert_list)
+            if self.client_gid:
+                self.client_gid.verify_chain(self.trusted_cert_list)
+            if self.object_gid:
+                self.object_gid.verify_chain(self.trusted_cert_list)
+
     # register_functions override this to add more functions
     def register_functions(self):
         self.server.register_function(self.noop)
diff --git a/util/geniticket.py b/util/geniticket.py
new file mode 100644 (file)
index 0000000..1869e58
--- /dev/null
@@ -0,0 +1,128 @@
+# tickets.py
+#
+# implements GENI tickets
+#
+
+from cert import *
+from rights import *
+from gid import *
+import xmlrpclib
+
+# Ticket is tuple:
+#   (gidCaller, gidObject, attributes, rspec, delegate)
+#
+#    gidCaller = GID of the caller performing the operation
+#    gidObject = GID of the slice
+#    attributes = slice attributes (keys, vref, instantiation, etc)
+#    rspec = resources
+
+class Ticket(Certificate):
+    gidCaller = None
+    gidObject = None
+    attributes = {}
+    rspec = {}
+    delegate = False
+
+    def __init__(self, create=False, subject=None, string=None, filename=None):
+        Certificate.__init__(self, create, subject, string, filename)
+
+    def set_gid_caller(self, gid):
+        self.gidCaller = gid
+
+    def get_gid_caller(self):
+        if not self.gidCaller:
+            self.decode()
+        return self.gidCaller
+
+    def set_gid_object(self, gid):
+        self.gidObject = gid
+
+    def get_gid_object(self):
+        if not self.gidObject:
+            self.decode()
+        return self.gidObject
+
+    def set_attributes(self, gid):
+        self.attributes = gid
+
+    def get_attributes(self):
+        if not self.attributes:
+            self.decode()
+        return self.attributes
+
+    def set_rspec(self, gid):
+        self.rspec = gid
+
+    def get_rspec(self):
+        if not self.rspec:
+            self.decode()
+        return self.rspec
+
+    def set_delegate(self, delegate):
+        self.delegate = delegate
+
+    def get_delegate(self):
+        if not self.delegate:
+            self.decode()
+        return self.delegate
+
+    def encode(self):
+        dict = {"gidCaller": None,
+                "gidObject": None,
+                "attributes": self.attributes,
+                "rspec": self.rspec,
+                "delegate": self.delegate}
+        if self.gidCaller:
+            dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True)
+        if self.gidObject:
+            dict["gidObject"] = self.gidObject.save_to_string(save_parents=True)
+        str = xmlrpclib.dumps((dict,), allow_none=True)
+        self.set_data(str)
+
+    def decode(self):
+        data = self.get_data()
+        if data:
+            dict = xmlrpclib.loads(self.get_data())[0][0]
+        else:
+            dict = {}
+
+        self.attributes = dict.get("attributes", {})
+        self.rspec = dict.get("rspec", {})
+        self.delegate = dict.get("delegate", False)
+
+        gidCallerStr = dict.get("gidCaller", None)
+        if gidCallerStr:
+            self.gidCaller = GID(string=gidCallerStr)
+        else:
+            self.gidCaller = None
+
+        gidObjectStr = dict.get("gidObject", None)
+        if gidObjectStr:
+            self.gidObject = GID(string=gidObjectStr)
+        else:
+            self.gidObject = None
+
+    def dump(self, dump_parents=False):
+        print "TICKET", self.get_subject()
+
+        print "  gidCaller:"
+        gidCaller = self.get_gid_caller()
+        if gidCaller:
+            gidCaller.dump(8, dump_parents)
+
+        print "  gidObject:"
+        gidObject = self.get_gid_object()
+        if gidObject:
+            gidObject.dump(8, dump_parents)
+
+        print "  attributes:"
+        for attrname in self.get_attributes().keys():
+            print "        ", attrname, self.get_attributes()[attrname]
+
+        print "       rspec:"
+        for attrname in self.get_rspec().keys():
+            print "        ", attrname, self.get_rspec()[attrname]
+
+        if self.parent and dump_parents:
+           print "PARENT",
+           self.parent.dump(dump_parents)
index f76f819..e83b4d4 100644 (file)
 import os
 import report
 from cert import *
+from credential import *
 from gid import *
 from misc import *
 from config import *
+from geniticket import *
 
 class AuthInfo():
     hrn = None
+    gid_object = None
     gid_filename = None
     privkey_filename = None
     dbinfo_filename = None
 
     def __init__(self, hrn, gid_filename, privkey_filename, dbinfo_filename):
         self.hrn = hrn
-        self.gid_filename = gid_filename
+        self.set_gid_filename(gid_filename)
         self.privkey_filename = privkey_filename
         self.dbinfo_filename = dbinfo_filename
 
+    def set_gid_filename(self, fn):
+        self.gid_filename = fn
+        self.gid_object = None
+
     def get_gid_object(self):
-        return GID(filename = self.gid_filename)
+        if not self.gid_object:
+            self.gid_object = GID(filename = self.gid_filename)
+        return self.gid_object
 
     def get_pkey_object(self):
         return Keypair(filename = self.privkey_filename)
@@ -39,10 +48,14 @@ class AuthInfo():
         dict = eval(f.read())\r
         f.close()\r
         return dict\r
+\r
+    def update_gid_object(self, gid):\r
+        gid.save_to_file(self.gid_filename)\r
+        self.gid_object = gid\r
 
 class Hierarchy():
     def __init__(self, basedir="."):
-        self.basedir = basedir
+        self.basedir = os.path.join(basedir, "authorities")
 
     def get_auth_filenames(self, hrn):
         leaf = get_leaf(hrn)
@@ -86,7 +99,7 @@ class Hierarchy():
         pkey.save_to_file(privkey_filename)
 
         gid = self.create_gid(hrn, create_uuid(), pkey)
-        gid.save_to_file(gid_filename)
+        gid.save_to_file(gid_filename, save_parents=True)
 
         # XXX TODO: think up a better way for the dbinfo to work
 
@@ -106,13 +119,18 @@ class Hierarchy():
 
         auth_info = AuthInfo(hrn, gid_filename, privkey_filename, dbinfo_filename)
 
+        # check the GID and see if it needs to be refreshed
+        gid = auth_info.get_gid_object()
+        gid_refreshed = self.refresh_gid(gid)
+        if gid != gid_refreshed:
+            auth_info.update_gid_object(gid_refreshed)
+
         return auth_info
 
     def create_gid(self, hrn, uuid, pkey):
-        parent_hrn = get_authority(hrn)
-
         gid = GID(subject=hrn, uuid=uuid, hrn=hrn)
 
+        parent_hrn = get_authority(hrn)
         if not parent_hrn:
             # if there is no parent hrn, then it must be self-signed. this
             # is where we terminate the recursion
@@ -146,3 +164,58 @@ class Hierarchy():
 
         return gid
 
+    def get_auth_cred(self, hrn):
+        auth_info = self.get_auth_info(hrn)
+        gid = auth_info.get_gid_object()
+
+        cred = Credential(subject=hrn)
+        cred.set_gid_caller(gid)
+        cred.set_gid_object(gid)
+        cred.set_privileges("authority")
+        cred.set_delegate(True)
+        cred.set_pubkey(auth_info.get_gid_object().get_pubkey())
+
+        parent_hrn = get_authority(hrn)
+        if not parent_hrn:
+            # if there is no parent hrn, then it must be self-signed. this
+            # is where we terminate the recursion
+            cred.set_issuer(auth_info.get_pkey_object(), hrn)
+        else:
+            # we need the parent's private key in order to sign this GID
+            parent_auth_info = self.get_auth_info(parent_hrn)
+            cred.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn)
+            cred.set_parent(self.get_auth_cred(parent_hrn))
+
+        cred.encode()
+        cred.sign()
+
+        return cred
+
+    # this looks almost the same as get_auth_cred, but works for tickets
+    # XXX does similarity imply there should be more code re-use?
+    def get_auth_ticket(self, hrn):
+        auth_info = self.get_auth_info(hrn)
+        gid = auth_info.get_gid_object()
+
+        ticket = Ticket(subject=hrn)
+        ticket.set_gid_caller(gid)
+        ticket.set_gid_object(gid)
+        ticket.set_delegate(True)
+        ticket.set_pubkey(auth_info.get_gid_object().get_pubkey())
+
+        parent_hrn = get_authority(hrn)
+        if not parent_hrn:
+            # if there is no parent hrn, then it must be self-signed. this
+            # is where we terminate the recursion
+            ticket.set_issuer(auth_info.get_pkey_object(), hrn)
+        else:
+            # we need the parent's private key in order to sign this GID
+            parent_auth_info = self.get_auth_info(parent_hrn)
+            ticket.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn)
+            ticket.set_parent(self.get_auth_cred(parent_hrn))
+
+        ticket.encode()
+        ticket.sign()
+
+        return ticket
+