merging with geni-api branch
authorTony Mack <tmack@cs.princeton.edu>
Wed, 2 Jun 2010 20:42:30 +0000 (20:42 +0000)
committerTony Mack <tmack@cs.princeton.edu>
Wed, 2 Jun 2010 20:42:30 +0000 (20:42 +0000)
35 files changed:
sfa/client/sfi.py
sfa/init.d/sfa
sfa/managers/aggregate_manager_pl.py
sfa/managers/registry_manager_pl.py
sfa/methods/CreateSliver.py [new file with mode: 0644]
sfa/methods/DeleteSliver.py [new file with mode: 0644]
sfa/methods/GetVersion.py [new file with mode: 0644]
sfa/methods/ListResources.py [new file with mode: 0644]
sfa/methods/RenewSliver.py [new file with mode: 0644]
sfa/methods/ResolveGENI.py [new file with mode: 0644]
sfa/methods/Shutdown.py [new file with mode: 0644]
sfa/methods/SliverStatus.py [new file with mode: 0644]
sfa/methods/get_geni_aggregates.py [new file with mode: 0644]
sfa/plc/api-dev.py [new file with mode: 0644]
sfa/plc/api.py
sfa/plc/sfa-import-plc.py
sfa/plc/sfaImport.py
sfa/plc/slices.py
sfa/server/interface.py
sfa/server/modpythonapi/AuthenticatedApi.py
sfa/server/sfa-server.py
sfa/trust/auth.py
sfa/trust/certificate.py
sfa/trust/credential.py
sfa/trust/credential_legacy.py [new file with mode: 0644]
sfa/trust/gid.py
sfa/trust/hierarchy.py
sfa/trust/rights.py
sfa/trust/trustedroot.py
sfa/util/api.py
sfa/util/faults.py
sfa/util/namespace.py
sfa/util/server.py
sfa/util/sfalogging.py
sfa/util/table.py

index 9e18bd7..8592ec1 100755 (executable)
@@ -8,6 +8,7 @@ import os, os.path
 import tempfile
 import traceback
 import socket
+import random
 from lxml import etree
 from StringIO import StringIO
 from types import StringTypes, ListType
@@ -20,11 +21,12 @@ from sfa.util.namespace import *
 from sfa.util.xmlrpcprotocol import ServerException
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
 from sfa.util.config import Config
+import zlib
 
 
 # utility methods here
 # display methods
-def display_rspec(rspec, format = 'rspec'):
+def display_rspec(rspec, format='rspec'):
     if format in ['dns']:
         tree = etree.parse(StringIO(rspec))
         root = tree.getroot()
@@ -46,12 +48,12 @@ def display_list(results):
         print result
 
 
-def display_records(recordList, dump = False):
+def display_records(recordList, dump=False):
     ''' Print all fields in the record'''
     for record in recordList:
         display_record(record, dump)
 
-def display_record(record, dump = False):
+def display_record(record, dump=False):
     if dump:
         record.dump()
     else:
@@ -81,7 +83,7 @@ def save_rspec_to_file(rspec, filename):
 def save_records_to_file(filename, recordList):
     index = 0
     for record in recordList:
-        if index>0:
+        if index > 0:
             save_record_to_file(filename + "." + str(index), record)
         else:
             save_record_to_file(filename, record)
@@ -89,15 +91,15 @@ def save_records_to_file(filename, recordList):
 
 def save_record_to_file(filename, record):
     if record['type'] in ['user']:
-        record = UserRecord(dict = record)
+        record = UserRecord(dict=record)
     elif record['type'] in ['slice']:
-        record = SliceRecord(dict = record)
+        record = SliceRecord(dict=record)
     elif record['type'] in ['node']:
-        record = NodeRecord(dict = record)
+        record = NodeRecord(dict=record)
     elif record['type'] in ['authority', 'ma', 'sa']:
-        record = AuthorityRecord(dict = record)
+        record = AuthorityRecord(dict=record)
     else:
-        record = SfaRecord(dict = record)
+        record = SfaRecord(dict=record)
     str = record.save_to_string()
     file(filename, "w").write(str)
     return
@@ -112,7 +114,8 @@ def load_record_from_file(filename):
 
 
 class Sfi:
-    
+
+    geni_am = None
     slicemgr = None
     registry = None
     user = None
@@ -120,7 +123,7 @@ class Sfi:
     options = None
     hashrequest = False
    
-    def create_cmd_parser(self,command, additional_cmdargs = None):
+    def create_cmd_parser(self, command, additional_cmdargs=None):
         cmdargs = {"gid": "",
                   "list": "name",
                   "show": "name",
@@ -134,12 +137,20 @@ class Sfi:
                   "create": "name rspec",
                   "get_trusted_certs": "cred",
                   "get_ticket": "name rspec",
-                  "redeem_ticket": "ticket",  
+                  "redeem_ticket": "ticket",
                   "delete": "name",
                   "reset": "name",
                   "start": "name",
                   "stop": "name",
-                  "delegate": "name"
+                  "delegate": "name",
+                  "GetVersion": "name",
+                  "ListResources": "name",
+                  "CreateSliver": "name",
+                  "get_geni_aggregates": "name",
+                  "DeleteSliver": "name",
+                  "SliverStatus": "name",
+                  "RenewSliver": "name",
+                  "Shutdown": "name"
                  }
 
         if additional_cmdargs:
@@ -149,7 +160,7 @@ class Sfi:
             print "Invalid command\n"
             print "Commands: ",
             for key in cmdargs.keys():
-                print key+",",
+                print key + ",",
             print ""
             sys.exit(2)
 
@@ -157,24 +168,24 @@ class Sfi:
                                      % (command, cmdargs[command]))
 
         if command in ("resources"):
-            parser.add_option("-f", "--format", dest="format",type="choice",
-                             help="display format ([xml]|dns|ip)",default="xml",
-                             choices=("xml","dns","ip"))
+            parser.add_option("-f", "--format", dest="format", type="choice",
+                             help="display format ([xml]|dns|ip)", default="xml",
+                             choices=("xml", "dns", "ip"))
             parser.add_option("-a", "--aggregate", dest="aggregate",
                              default=None, help="aggregate hrn")
 
         if command in ("create", "get_ticket"):
-            parser.add_option("-a", "--aggregate", dest="aggregate",default=None,
+            parser.add_option("-a", "--aggregate", dest="aggregate", default=None,
                              help="aggregate hrn")
 
         if command in ("start", "stop", "reset", "delete", "slices"):
-            parser.add_option("-c", "--component", dest="component",default=None,
+            parser.add_option("-c", "--component", dest="component", default=None,
                              help="component hrn")
             
         if command in ("list", "show", "remove"):
-            parser.add_option("-t", "--type", dest="type",type="choice",
+            parser.add_option("-t", "--type", dest="type", type="choice",
                             help="type filter ([all]|user|slice|sa|ma|node|aggregate)",
-                            choices=("all","user","slice","sa","ma","node","aggregate"),
+                            choices=("all", "user", "slice", "sa", "ma", "node", "aggregate"),
                             default="all")
 
         if command in ("resources", "show", "list"):
@@ -183,8 +194,8 @@ class Sfi:
         
         if command in ("show", "list"):
            parser.add_option("-f", "--format", dest="format", type="choice",
-                             help="display format ([text]|xml)",default="text",
-                             choices=("text","xml"))
+                             help="display format ([text]|xml)", default="text",
+                             choices=("text", "xml"))
 
         if command in ("delegate"):
            parser.add_option("-u", "--user",
@@ -192,6 +203,7 @@ class Sfi:
                             help="delegate user credential")
            parser.add_option("-s", "--slice", dest="delegate_slice",
                             help="delegate slice credential", metavar="HRN", default=None)
+
         return parser
 
         
@@ -200,14 +212,16 @@ class Sfi:
         # Generate command line parser
         parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
                              description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
+        parser.add_option("-g", "--geni_am", dest="geni_am",
+                          help="geni am", metavar="URL", default=None)
         parser.add_option("-r", "--registry", dest="registry",
                          help="root registry", metavar="URL", default=None)
         parser.add_option("-s", "--slicemgr", dest="sm",
                          help="slice manager", metavar="URL", default=None)
-        default_sfi_dir=os.path.expanduser("~/.sfi/")
+        default_sfi_dir = os.path.expanduser("~/.sfi/")
         parser.add_option("-d", "--dir", dest="sfi_dir",
                          help="config & working directory - default is " + default_sfi_dir,
-                         metavar="PATH", default = default_sfi_dir)
+                         metavar="PATH", default=default_sfi_dir)
         parser.add_option("-u", "--user", dest="user",
                          help="user name", metavar="HRN", default=None)
         parser.add_option("-a", "--auth", dest="auth",
@@ -237,7 +251,7 @@ class Sfi:
        try:
           config = Config (config_file)
        except:
-          print "Failed to read configuration file",config_file
+          print "Failed to read configuration file", config_file
           print "Make sure to remove the export clauses and to add quotes"
           if not self.options.verbose:
              print "Re-run with -v for more details"
@@ -245,42 +259,48 @@ class Sfi:
              traceback.print_exc()
           sys.exit(1)
     
-       errors=0
+       errors = 0
        # Set SliceMgr URL
        if (self.options.sm is not None):
           sm_url = self.options.sm
-       elif hasattr(config,"SFI_SM"):
+       elif hasattr(config, "SFI_SM"):
           sm_url = config.SFI_SM
        else:
-          print "You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s"%config_file
-          errors +=1 
+          print "You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file
+          errors += 
     
        # Set Registry URL
        if (self.options.registry is not None):
           reg_url = self.options.registry
-       elif hasattr(config,"SFI_REGISTRY"):
+       elif hasattr(config, "SFI_REGISTRY"):
           reg_url = config.SFI_REGISTRY
        else:
-          print "You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s"%config_file
-          errors +=1 
-    
+          print "You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file
+          errors += 1 
+          
+
+       if (self.options.geni_am is not None):
+           geni_am_url = self.options.geni_am
+       elif hasattr(config, "SFI_GENI_AM"):
+           geni_am_url = config.SFI_GENI_AM
+           
        # Set user HRN
        if (self.options.user is not None):
           self.user = self.options.user
-       elif hasattr(config,"SFI_USER"):
+       elif hasattr(config, "SFI_USER"):
           self.user = config.SFI_USER
        else:
-          print "You need to set e.g. SFI_USER='plc.princeton.username' in %s"%config_file
-          errors +=1 
+          print "You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file
+          errors += 
     
        # Set authority HRN
        if (self.options.auth is not None):
           self.authority = self.options.auth
-       elif hasattr(config,"SFI_AUTH"):
+       elif hasattr(config, "SFI_AUTH"):
           self.authority = config.SFI_AUTH
        else:
-          print "You need to set e.g. SFI_AUTH='plc.princeton' in %s"%config_file
-          errors +=1 
+          print "You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file
+          errors += 
     
        if errors:
           sys.exit(1)
@@ -298,7 +318,9 @@ class Sfi:
        self.cert = Certificate(filename=cert_file) 
        # Establish connection to server(s)
        self.registry = xmlrpcprotocol.get_server(reg_url, key_file, cert_file, self.options.debug)  
-       self.slicemgr = xmlrpcprotocol.get_server(sm_url, key_file, cert_file, self.options.debug)  
+       self.slicemgr = xmlrpcprotocol.get_server(sm_url, key_file, cert_file, self.options.debug)
+       self.geni_am = xmlrpcprotocol.get_server(geni_am_url, key_file, cert_file, self.options.debug)
+
        return
     
     #
@@ -316,7 +338,7 @@ class Sfi:
     
     
     def get_key_file(self):
-       file=os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
+       file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
        #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".pkey")
        if (os.path.isfile(file)):
           return file
@@ -325,14 +347,14 @@ class Sfi:
           sys.exit(-1)
        return
     
-    def get_cert_file(self,key_file):
+    def get_cert_file(self, key_file):
     
        #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".cert")
-       file=os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
+       file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
        if (os.path.isfile(file)):
           return file
        else:
-          k = Keypair(filename = key_file)
+          k = Keypair(filename=key_file)
           cert = Certificate(subject=self.user)
           cert.set_pubkey(k)
           cert.set_issuer(k, self.user)
@@ -344,7 +366,7 @@ class Sfi:
    
     def get_gid(self):
         #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".gid")
-        file=os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".gid")
+        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".gid")
         if (os.path.isfile(file)):
             gid = GID(filename=file)
             return gid
@@ -359,17 +381,17 @@ class Sfi:
  
     def get_user_cred(self):
         #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".cred")
-        file=os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
+        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
         if (os.path.isfile(file)):
             user_cred = Credential(filename=file)
             return user_cred
         else:
             # bootstrap user credential
             cert_string = self.cert.save_to_string(save_parents=True)
-            user_name=self.user.replace(self.authority+".", '')
+            user_name = self.user.replace(self.authority + ".", '')
             if user_name.count(".") > 0:
                 user_name = user_name.replace(".", '_')
-                self.user=self.authority + "." + user_name
+                self.user = self.authority + "." + user_name
 
             user_cred = self.registry.get_self_credential(cert_string, "user", self.user)
             if user_cred:
@@ -387,7 +409,7 @@ class Sfi:
             print "no authority specified. Use -a or set SF_AUTH"
             sys.exit(-1)
     
-        file = os.path.join(self.options.sfi_dir, get_leaf("authority") +".cred")
+        file = os.path.join(self.options.sfi_dir, get_leaf("authority") + ".cred")
         if (os.path.isfile(file)):
             auth_cred = Credential(filename=file)
             return auth_cred
@@ -405,7 +427,7 @@ class Sfi:
                 print "Failed to get authority credential"
                 sys.exit(-1)
     
-    def get_slice_cred(self,name):
+    def get_slice_cred(self, name):
         file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
         if (os.path.isfile(file)):
             slice_cred = Credential(filename=file)
@@ -425,7 +447,7 @@ class Sfi:
                 print "Failed to get slice credential"
                 sys.exit(-1)
     
-    def delegate_cred(self,cred, hrn, type = 'authority'):
+    def delegate_cred(self, cred, hrn, type='authority'):
         # the gid and hrn of the object we are delegating
         user_cred = Credential(string=cred)
         object_gid = user_cred.get_gid_object()
@@ -447,23 +469,31 @@ class Sfi:
         delegee_hrn = delegee_gid.get_hrn()
         
         # the key and hrn of the user who will be delegating
-        user_key = Keypair(filename = self.get_key_file())
+        user_key = Keypair(filename=self.get_key_file())
         user_hrn = user_cred.get_gid_caller().get_hrn()
     
         dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
         dcred.set_gid_caller(delegee_gid)
         dcred.set_gid_object(object_gid)
         dcred.set_privileges(user_cred.get_privileges())
-        dcred.set_delegate(True)
-        dcred.set_pubkey(object_gid.get_pubkey())
-        dcred.set_issuer(user_key, user_hrn)
+        dcred.get_privileges().delegate_all_privileges(True)
+        
+
+        # Save the issuer's gid to a file
+        fname = self.options.sfi_dir + os.sep + "gid_%d" % random.randint(0, 999999999)
+        f = open(fname, "w")
+        f.write(user_cred.get_gid_caller().save_to_string())
+        f.close()
+        dcred.set_issuer_keys(self.get_key_file(), fname)
+        os.remove(fname)
+        
         dcred.set_parent(user_cred)
         dcred.encode()
         dcred.sign()
     
         return dcred.save_to_string(save_parents=True)
     
-    def get_rspec_file(self,rspec):
+    def get_rspec_file(self, rspec):
        if (os.path.isabs(rspec)):
           file = rspec
        else:
@@ -474,7 +504,7 @@ class Sfi:
           print "No such rspec file", rspec
           sys.exit(1)
     
-    def get_record_file(self,record):
+    def get_record_file(self, record):
        if (os.path.isabs(record)):
           file = record
        else:
@@ -485,8 +515,8 @@ class Sfi:
           print "No such registry record file", record
           sys.exit(1)
     
-    def load_publickey_string(self,fn):
-       f = file(fn,"r")
+    def load_publickey_string(self, fn):
+       f = file(fn, "r")
        key_string = f.read()
     
        # if the filename is a private key file, then extract the public key
@@ -518,8 +548,8 @@ class Sfi:
     # Registry-related commands
     #
   
-    def dispatch(self,command, cmd_opts, cmd_args):
-        getattr(self,command)(cmd_opts, cmd_args)
+    def dispatch(self, command, cmd_opts, cmd_args):
+        getattr(self, command)(cmd_opts, cmd_args)
  
     def gid(self, opts, args):
         gid = self.get_gid()
@@ -527,7 +557,7 @@ class Sfi:
         return   
  
     # list entires in named authority registry
-    def list(self,opts, args):
+    def list(self, opts, args):
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
         hrn = args[0]
         try:
@@ -548,7 +578,7 @@ class Sfi:
         return
     
     # show named registry record
-    def show(self,opts, args):
+    def show(self, opts, args):
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
         hrn = args[0]
         records = self.registry.resolve(user_cred, hrn)
@@ -557,16 +587,16 @@ class Sfi:
             print "No record of type", opts.type
         for record in records:
             if record['type'] in ['user']:
-                record = UserRecord(dict = record)
+                record = UserRecord(dict=record)
             elif record['type'] in ['slice']:
-                record = SliceRecord(dict = record)
+                record = SliceRecord(dict=record)
             elif record['type'] in ['node']:
-                record = NodeRecord(dict = record)
+                record = NodeRecord(dict=record)
             elif record['type'] in ['authority', 'ma', 'sa']:
-                record = AuthorityRecord(dict = record)
+                record = AuthorityRecord(dict=record)
             else:
-                record = SfaRecord(dict = record)
-            if (opts.format=="text"): 
+                record = SfaRecord(dict=record)
+            if (opts.format == "text"): 
                 record.dump()  
             else:
                 print record.save_to_string() 
@@ -578,7 +608,7 @@ class Sfi:
             save_records_to_file(file, records)
         return
     
-    def delegate(self,opts, args):
+    def delegate(self, opts, args):
        user_cred = self.get_user_cred()
        if opts.delegate_user:
            object_cred = user_cred
@@ -592,7 +622,7 @@ class Sfi:
        object_gid = object_cred.get_gid_object()
        object_hrn = object_gid.get_hrn()
     
-       if not object_cred.get_delegate():
+       if not object_cred.get_privileges().get_all_delegate():
            print "Error: Object credential", object_hrn, "does not have delegate bit set"
            return
     
@@ -603,19 +633,20 @@ class Sfi:
            print "Error: Didn't find a user record for", args[0]
            return
     
-       # the gid of the user who will be delegated too
+       # the gid of the user who will be delegated to
        delegee_gid = GID(string=records[0]['gid'])
        delegee_hrn = delegee_gid.get_hrn()
    
        # the key and hrn of the user who will be delegating
-       user_key = Keypair(filename = self.get_key_file())
+       user_key = Keypair(filename=self.get_key_file())
        user_hrn = user_cred.get_gid_caller().get_hrn()
        subject_string = "%s delegated to %s" % (object_hrn, delegee_hrn)
        dcred = Credential(subject=subject_string)
        dcred.set_gid_caller(delegee_gid)
        dcred.set_gid_object(object_gid)
+       privs = object_cred.get_privileges()
        dcred.set_privileges(object_cred.get_privileges())
-       dcred.set_delegate(True)
+       dcred.get_privileges().delegate_all_privileges(True)
        dcred.set_pubkey(object_gid.get_pubkey())
        dcred.set_issuer(user_key, user_hrn)
        dcred.set_parent(object_cred)
@@ -629,13 +660,13 @@ class Sfi:
            dest_fn = os.path_join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_" 
                                   + get_leaf(object_hrn) + ".cred")
     
-       dcred.save_to_file(dest_fn, save_parents = True)
+       dcred.save_to_file(dest_fn, save_parents=True)
     
        print "delegated credential for", object_hrn, "to", delegee_hrn, "and wrote to", dest_fn
     
     # removed named registry record
     #   - have to first retrieve the record to be removed
-    def remove(self,opts, args):
+    def remove(self, opts, args):
         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
         hrn = args[0]
         type = opts.type 
@@ -644,7 +675,7 @@ class Sfi:
         return self.registry.remove(auth_cred, type, hrn)
     
     # add named registry record
-    def add(self,opts, args):
+    def add(self, opts, args):
         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
         record_filepath = args[0]
         rec_file = self.get_record_file(record_filepath)
@@ -652,7 +683,7 @@ class Sfi:
         return self.registry.register(auth_cred, record)
     
     # update named registry entry
-    def update(self,opts, args):
+    def update(self, opts, args):
         user_cred = self.get_user_cred()
         rec_file = self.get_record_file(args[0])
         record = load_record_from_file(rec_file)
@@ -696,12 +727,27 @@ class Sfi:
         """
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
         hrn = None
-        if args: 
+        if args:
             hrn = args[0]
+
         result = self.registry.get_aggregates(user_cred, hrn)
         display_list(result)
         return 
 
+    def get_geni_aggregates(self, opts, args):
+        """
+        return a list of details about known aggregates
+        """
+        user_cred = self.get_user_cred().save_to_string(save_parents=True)
+        hrn = None
+        if args:
+            hrn = args[0]
+
+        result = self.registry.get_geni_aggregates(user_cred, hrn)
+        display_list(result)
+        return 
+
+
     def registries(self, opts, args):
         """
         return a list of details about known registries
@@ -722,7 +768,7 @@ class Sfi:
     # list available nodes -- use 'resources' w/ no argument instead
 
     # list instantiated slices
-    def slices(self,opts, args):
+    def slices(self, opts, args):
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
         server = self.slicemgr
         # direct connection to the nodes component manager interface
@@ -733,7 +779,7 @@ class Sfi:
         return
     
     # show rspec for named slice
-    def resources(self,opts, args):
+    def resources(self, opts, args):
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
         server = self.slicemgr
         if opts.aggregate:
@@ -763,12 +809,12 @@ class Sfi:
         return
     
     # created named slice with given rspec
-    def create(self,opts, args):
+    def create(self, opts, args):
         slice_hrn = args[0]
         user_cred = self.get_user_cred()
         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
         rspec_file = self.get_rspec_file(args[1])
-        rspec=open(rspec_file).read()
+        rspec = open(rspec_file).read()
         server = self.slicemgr
         if opts.aggregate:
             aggregates = self.registry.get_aggregates(user_cred, opts.aggregate)
@@ -785,7 +831,7 @@ class Sfi:
         user_cred = self.get_user_cred()
         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
         rspec_file = self.get_rspec_file(rspec_path) 
-        rspec=open(rspec_file).read()
+        rspec = open(rspec_file).read()
         server = self.slicemgr
         if opts.aggregate:
             aggregates = self.registry.get_aggregates(user_cred, opts.aggregate)
@@ -807,7 +853,7 @@ class Sfi:
         # use this to get the right slice credential 
         ticket = SfaTicket(filename=ticket_file)
         ticket.decode()
-       slice_hrn=ticket.gidObject.get_hrn()
+       slice_hrn = ticket.gidObject.get_hrn()
         #slice_hrn = ticket.attributes['slivers'][0]['hrn']
         user_cred = self.get_user_cred()
         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
@@ -824,7 +870,7 @@ class Sfi:
             try:
                 cm_port = "12346" 
                 url = "https://%(hostname)s:%(cm_port)s" % locals() 
-                print "Calling redeem_ticket at %(url)s " % locals(),  
+                print "Calling redeem_ticket at %(url)s " % locals(),
                 cm = xmlrpcprotocol.get_server(url, self.key_file, self.cert_file, self.options.debug)
                 cm.redeem_ticket(slice_cred, ticket.save_to_string(save_parents=True))
                 print "Success"
@@ -837,7 +883,7 @@ class Sfi:
         return
  
     # delete named slice
-    def delete(self,opts, args):
+    def delete(self, opts, args):
         slice_hrn = args[0]
         server = self.slicemgr
         # direct connection to the nodes component manager interface
@@ -848,7 +894,7 @@ class Sfi:
         return server.delete_slice(slice_cred, slice_hrn)
     
     # start named slice
-    def start(self,opts, args):
+    def start(self, opts, args):
         slice_hrn = args[0]
         server = self.slicemgr
         # direct connection to the nodes component manager interface
@@ -859,7 +905,7 @@ class Sfi:
         return server.start_slice(slice_cred, slice_hrn)
     
     # stop named slice
-    def stop(self,opts, args):
+    def stop(self, opts, args):
         slice_hrn = args[0]
         server = self.slicemgr
         # direct connection to the nodes component manager interface
@@ -870,7 +916,7 @@ class Sfi:
         return server.stop_slice(slice_cred, slice_hrn)
     
     # reset named slice
-    def reset(self,opts, args):
+    def reset(self, opts, args):
         slice_hrn = args[0]
         server = self.slicemgr
         # direct connection to the nodes component manager interface
@@ -878,6 +924,63 @@ class Sfi:
             server = self.get_component_server_from_hrn(opts.component)
         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
         return server.reset_slice(slice_cred, slice_hrn)
+
+
+    # GENI AM related calls
+
+    def GetVersion(self, opts, args):
+        server = self.geni_am
+        print server.GetVersion()
+
+    def ListResources(self, opts, args):
+        user_cred = self.get_user_cred().save_to_string(save_parents=True)
+        server = self.geni_am
+        call_options = {'geni_compressed': True}
+        xrn = None
+        cred = user_cred
+        if args:
+            xrn = args[0]
+            cred = self.get_slice_cred(xrn).save_to_string(save_parents=True)
+
+        if xrn:
+            call_options['geni_slice_urn'] = xrn
+            
+        rspec = server.ListResources([cred], call_options)
+        rspec = zlib.decompress(rspec.decode('base64'))
+        print rspec
+        
+    def CreateSliver(self, opts, args):
+        slice_xrn = args[0]
+        slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
+        rspec_file = self.get_rspec_file(args[1])
+        rspec = open(rspec_file).read()
+        server = self.geni_am
+        return server.CreateSliver(slice_xrn, [slice_cred], rspec, [])
+    
+    def DeleteSliver(self, opts, args):
+        slice_xrn = args[0]
+        slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
+        server = self.geni_am
+        return server.DeleteSliver(slice_xrn, [slice_cred])    
+
+    def SliverStatus(self, opts, args):
+        slice_xrn = args[0]
+        slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
+        server = self.geni_am
+        print server.SliverStatus(slice_xrn, [slice_cred])
+    
+    def RenewSliver(self, opts, args):
+        slice_xrn = args[0]
+        slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
+        time = args[1]
+        server = self.geni_am
+        return server.RenewSliver(slice_xrn, [slice_cred], time)   
+
+    def Shutdown(self, opts, args):
+        slice_xrn = args[0]
+        slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
+        server = self.geni_am
+        return server.Shutdown(slice_xrn, [slice_cred])         
     
     #
     # Main: parse arguments and dispatch to command
@@ -888,11 +991,11 @@ class Sfi:
         self.options = options
    
         if options.hashrequest:
-            self.hashrequest=True
+            self.hashrequest = True
  
         if len(args) <= 0:
             print "No command given. Use -h for help."
-            return -1
+            return - 1
     
         command = args[0]
         (cmd_opts, cmd_args) = self.create_cmd_parser(command).parse_args(args[1:])
@@ -900,12 +1003,12 @@ class Sfi:
             print "Registry %s, sm %s, dir %s, user %s, auth %s" % (options.registry, options.sm,
                                                                    options.sfi_dir, options.user,
                                                                    options.auth)
-            print "Command %s" %command
+            print "Command %s" % command
             if command in ("resources"):
-                print "resources cmd_opts %s" %cmd_opts.format
-            elif command in ("list","show","remove"):
-                print "cmd_opts.type %s" %cmd_opts.type
-            print "cmd_args %s" %cmd_args
+                print "resources cmd_opts %s" % cmd_opts.format
+            elif command in ("list", "show", "remove"):
+                print "cmd_opts.type %s" % cmd_opts.type
+            print "cmd_args %s" % cmd_args
     
         self.set_servers()
     
@@ -918,5 +1021,5 @@ class Sfi:
     
         return
     
-if __name__=="__main__":
+if __name__ == "__main__":
    Sfi().main()
index b3b041c..6e81c63 100755 (executable)
@@ -75,6 +75,10 @@ start() {
         action "SFA SliceMgr" daemon /usr/bin/sfa-server.py -s -d $OPTIONS
     fi
 
+               if [ "$SFA_GENI_AGGREGATE_ENABLED" ]; then
+                               action $"SFA GENI Aggregate" daemon /usr/bin/sfa-server.py -g -d $OPTIONS
+               fi
+
     RETVAL=$?
     [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sfa
 
index 417cd7c..76c576b 100644 (file)
@@ -45,7 +45,7 @@ def __get_hostnames(nodes):
         hostnames.append(node.hostname)
     return hostnames
     
-def create_slice(api, xrn, xml):
+def create_slice(api, xrn, xml, reg_objects=None):
     """
     Verify HRN and initialize the slice record in PLC if necessary.
     """
@@ -58,9 +58,10 @@ def create_slice(api, xrn, xml):
     registry = api.registries[api.hrn]
     credential = api.getCredential()
     site_id, remote_site_id = slices.verify_site(registry, credential, hrn, 
-                                                 peer, sfa_peer)
+                                                 peer, sfa_peer, reg_objects)
+
     slice = slices.verify_slice(registry, credential, hrn, site_id, 
-                                remote_site_id, peer, sfa_peer)
+                                remote_site_id, peer, sfa_peer, reg_objects)
 
     network = Network(api)
 
@@ -68,7 +69,6 @@ def create_slice(api, xrn, xml):
     current = __get_hostnames(slice.get_nodes())
     
     network.addRSpec(xml, api.config.SFA_AGGREGATE_RSPEC_SCHEMA)
-    
     request = __get_hostnames(network.nodesWithSlivers())
     
     # remove nodes not in rspec
@@ -76,6 +76,8 @@ def create_slice(api, xrn, xml):
 
     # add nodes from rspec
     added_nodes = list(set(request).difference(current))
+    
+
 
     if peer:
         api.plshell.UnBindObjectFromPeer(api.plauth, 'slice', slice.id, peer)
@@ -146,7 +148,7 @@ def start_slice(api, xrn):
         raise RecordNotFound(hrn)
     slice_id = slices[0]
     attributes = api.plshell.GetSliceTags(api.plauth, {'slice_id': slice_id, 'name': 'enabled'}, ['slice_attribute_id'])
-    attribute_id = attreibutes[0]['slice_attribute_id']
+    attribute_id = attributes[0]['slice_attribute_id']
     api.plshell.UpdateSliceTag(api.plauth, attribute_id, "1" )
 
     return 1
index 2486738..50776c0 100644 (file)
@@ -10,12 +10,15 @@ from sfa.trust.credential import *
 from sfa.trust.certificate import *
 from sfa.util.faults import *
 
+    
+
 def get_credential(api, xrn, type, is_self=False):
     # convert xrn to hrn     
     if type:
         hrn = urn_to_hrn(xrn)[0]
     else:
         hrn, type = urn_to_hrn(xrn)
+        
     # Is this a root or sub authority
     auth_hrn = api.auth.get_authority(hrn)
     if not auth_hrn or hrn == api.config.SFA_INTERFACE_HRN:
@@ -54,17 +57,42 @@ def get_credential(api, xrn, type, is_self=False):
     new_cred = Credential(subject = object_gid.get_subject())
     new_cred.set_gid_caller(caller_gid)
     new_cred.set_gid_object(object_gid)
-    new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
-    new_cred.set_pubkey(object_gid.get_pubkey())
+    new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
+    #new_cred.set_pubkey(object_gid.get_pubkey())
     new_cred.set_privileges(rights)
-    new_cred.set_delegate(True)
+    new_cred.get_privileges().delegate_all_privileges(True)
     auth_kind = "authority,ma,sa"
-    new_cred.set_parent(api.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
+    # Parent not necessary, verify with certs
+    #new_cred.set_parent(api.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
     new_cred.encode()
     new_cred.sign()
 
     return new_cred.save_to_string(save_parents=True)
 
+
+# The GENI GetVersion call
+def GetVersion():
+    version = {}
+    version['geni_api'] = 1
+    return version
+
+
+
+# The GENI resolve call
+def Resolve(api, xrn, creds):
+    records = resolve(api, xrn)
+    
+    if len(records) == 0:
+        return {}
+    
+    record = records[0]
+    if record.type == 'slice':
+        return {'geni_urn': xrn, 'geni_creator': " ".join(record.PI)}
+    if record.type == 'user':
+        return {'geni_urn': xrn, 'geni_certificate': record.gid}
+    
+    
+
 def resolve(api, xrns, type=None, origin_hrn=None, full=True):
 
     # load all know registry names into a prefix tree and attempt to find
@@ -156,7 +184,7 @@ def list(api, xrn, origin_hrn=None):
 def register(api, record):
 
     hrn, type = record['hrn'], record['type']
-
+    urn = hrn_to_urn(hrn,type)
     # validate the type
     if type not in ['authority', 'slice', 'node', 'user']:
         raise UnknownSfaType(type) 
@@ -185,7 +213,7 @@ def register(api, record):
                 pub_key = record['key']
             pkey = convert_public_key(pub_key)
 
-        gid_object = api.auth.hierarchy.create_gid(hrn, uuid, pkey)
+        gid_object = api.auth.hierarchy.create_gid(urn, uuid, pkey)
         gid = gid_object.save_to_string(save_parents=True)
         record['gid'] = gid
         record.set_gid(gid)
@@ -193,7 +221,7 @@ def register(api, record):
     if type in ["authority"]:
         # update the tree
         if not api.auth.hierarchy.auth_exists(hrn):
-            api.auth.hierarchy.create_auth(hrn)
+            api.auth.hierarchy.create_auth(hrn_to_urn(hrn,'authority'))
 
         # get the GID from the newly created authority
         gid = auth_info.get_gid_object()
@@ -266,6 +294,7 @@ def update(api, record_dict):
     new_record = SfaRecord(dict = record_dict)
     type = new_record['type']
     hrn = new_record['hrn']
+    urn = hrn_to_urn(hrn,type)
     api.auth.verify_object_permission(hrn)
     table = SfaTable()
     # make sure the record exists
@@ -330,7 +359,7 @@ def update(api, record_dict):
             # update the openssl key and gid
             pkey = convert_public_key(new_key)
             uuid = create_uuid()
-            gid_object = api.auth.hierarchy.create_gid(hrn, uuid, pkey)
+            gid_object = api.auth.hierarchy.create_gid(urn, uuid, pkey)
             gid = gid_object.save_to_string(save_parents=True)
             record['gid'] = gid
             record = SfaRecord(dict=record)
diff --git a/sfa/methods/CreateSliver.py b/sfa/methods/CreateSliver.py
new file mode 100644 (file)
index 0000000..651a77d
--- /dev/null
@@ -0,0 +1,62 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+from sfatables.runtime import SFATablesRules
+import sys
+from sfa.trust.credential import Credential
+from sfa.util.sfalogging import logger
+
+class CreateSliver(Method):
+    """
+    Allocate resources to a slice.  This operation is expected to
+    start the allocated resources asynchornously after the operation
+    has successfully completed.  Callers can check on the status of
+    the resources using SliverStatus.
+
+    @param slice_urn (string) URN of slice to allocate to
+    @param credentials ([string]) of credentials
+    @param rspec (string) rspec to allocate
+    
+    """
+    interfaces = ['geni_am']
+    accepts = [
+        Parameter(str, "Slice URN"),
+        Parameter(type([str]), "List of credentials"),
+        Parameter(str, "RSpec"),
+        Parameter(type([]), "List of user information")
+        ]
+    returns = Parameter(str, "Allocated RSpec")
+
+    def __run_sfatables(self, manager, rules, hrn, origin_hrn, rspec):
+        if rules.sorted_rule_list:
+            contexts = rules.contexts
+            request_context = manager.fetch_context(hrn, origin_hrn, contexts)
+            rules.set_context(request_context)
+            newrspec = rules.apply(rspec)
+        else:    
+            newrspec = rspec
+        return newrspec
+
+
+    def call(self, slice_xrn, creds, rspec, users):
+        hrn, type = urn_to_hrn(slice_xrn)
+
+        self.api.logger.info("interface: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, hrn, self.name))
+
+        # Find the valid credentials
+        ValidCreds = self.api.auth.checkCredentials(creds, 'createsliver', hrn)
+        origin_hrn = Credential(string=ValidCreds[0]).get_gid_caller().get_hrn()
+
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            rspec = self.__run_sfatables(manager, SFATablesRules('INCOMING'),
+                                         hrn, origin_hrn, rspec)
+
+            return manager.CreateSliver(self.api, slice_xrn, ValidCreds, rspec, users)            
+        return ''
+    
diff --git a/sfa/methods/DeleteSliver.py b/sfa/methods/DeleteSliver.py
new file mode 100644 (file)
index 0000000..9d02ed9
--- /dev/null
@@ -0,0 +1,38 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+
+class DeleteSliver(Method):
+    """
+    Delete sliver from a slice.   Callers can check on the status of
+    the resources using SliverStatus.
+
+    @param slice_urn (string) URN of slice to allocate to
+    @param credentials ([string]) of credentials
+    
+    """
+    interfaces = ['geni_am']
+    accepts = [
+        Parameter(str, "Slice URN"),
+        Parameter(type([str]), "List of credentials"),
+        ]
+    returns = Parameter(bool, "Success or Failure")
+
+    def call(self, slice_xrn, creds):
+        hrn, type = urn_to_hrn(slice_xrn)
+        self.api.logger.info("interface: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, hrn, self.name))
+
+        # Find the valid credentials
+        ValidCreds = self.api.auth.checkCredentials(creds, 'deletesliver', hrn)
+        
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.DeleteSliver(self.api, slice_xrn, ValidCreds)
+
+        return ''
+    
diff --git a/sfa/methods/GetVersion.py b/sfa/methods/GetVersion.py
new file mode 100644 (file)
index 0000000..e1ca8b3
--- /dev/null
@@ -0,0 +1,33 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+
+
+class GetVersion(Method):
+    """
+    Returns this GENI Aggregate Manager's Version Information
+    @return version
+    """
+    interfaces = ['geni_am','registry']
+    accepts = []
+    returns = Parameter(dict, "Version information")
+
+    def call(self):
+        self.api.logger.info("interface: %s\tmethod-name: %s" % (self.api.interface, self.name))
+
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.GetVersion()
+        if self.api.interface in ['registry']:
+            mgr_type = self.api.config.SFA_REGISTRY_TYPE
+            manager_module = manager_base + ".slice_manager_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.GetVersion()
+        
+        return {}
+    
diff --git a/sfa/methods/ListResources.py b/sfa/methods/ListResources.py
new file mode 100644 (file)
index 0000000..b017381
--- /dev/null
@@ -0,0 +1,59 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter, Mixed
+from sfa.trust.credential import Credential
+from sfatables.runtime import SFATablesRules
+import sys
+import zlib
+
+class ListResources(Method):
+    """
+    Returns information about available resources or resources allocated to this slice
+    @param credential list
+    @param options dictionary
+    @return string
+    """
+    interfaces = ['geni_am']
+    accepts = [
+        Parameter(type([str]), "List of credentials"),
+        Parameter(dict, "Options")
+        ]
+    returns = Parameter(str, "List of resources")
+
+    def call(self, creds, options):
+        self.api.logger.info("interface: %s\tmethod-name: %s" % (self.api.interface, self.name))
+            
+        # Find the valid credentials
+        hrn = None
+        if options.has_key('geni_slice_urn'):
+            xrn = options['geni_slice_urn']
+            hrn, _ = urn_to_hrn(xrn)        
+            
+        ValidCreds = self.api.auth.checkCredentials(creds, '', hrn)
+        origin_hrn = Credential(string=ValidCreds[0]).get_gid_caller().get_hrn()
+            
+                    
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            rspec = manager.ListResources(self.api, ValidCreds, options)
+            outgoing_rules = SFATablesRules('OUTGOING')
+            
+        
+        filtered_rspec = rspec
+        if outgoing_rules.sorted_rule_list:
+            context = {'sfa':{'user':{'hrn':origin_hrn}, 'slice':{'hrn':None}}}
+            outgoing_rules.set_context(context)
+            filtered_rspec = outgoing_rules.apply(rspec)      
+        if options.has_key('geni_compressed') and options['geni_compressed'] == True:
+            filtered_rspec = zlib.compress(rspec).encode('base64')
+    
+
+        return filtered_rspec  
+    
+    
diff --git a/sfa/methods/RenewSliver.py b/sfa/methods/RenewSliver.py
new file mode 100644 (file)
index 0000000..bce8a49
--- /dev/null
@@ -0,0 +1,46 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+from sfa.trust.credential import Credential
+from dateutil.parser import parse
+
+class RenewSliver(Method):
+    """
+    Renews the resources in a sliver, extending the lifetime of the slice.    
+    @param slice_urn (string) URN of slice to renew
+    @param credentials ([string]) of credentials
+    @param expiration_time (string) requested time of expiration
+    
+    """
+    interfaces = ['geni_am']
+    accepts = [
+        Parameter(str, "Slice URN"),
+        Parameter(type([str]), "List of credentials"),
+        Parameter(str, "Expiration time in RFC 3339 format")
+        ]
+    returns = Parameter(bool, "Success or Failure")
+
+    def call(self, slice_xrn, creds, expiration_time):
+        hrn, type = urn_to_hrn(slice_xrn)
+
+        self.api.logger.info("interface: %s\ttarget-hrn: %s\tcaller-creds: %s\tmethod-name: %s"%(self.api.interface, hrn, creds, self.name))
+
+        # Find the valid credentials
+        ValidCreds = self.api.auth.checkCredentials(creds, 'renewsliver', hrn)
+
+        # Validate that the time does not go beyond the credential's expiration time
+        requested_time = parse(expiration_time)
+        if requested_time > Credential(string=ValidCreds[0]).get_lifetime():
+            raise InsufficientRights('SliverStatus: Credential expires before requested expiration time')
+        
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.RenewSliver(self.api, slice_xrn, ValidCreds, expiration_time)
+
+        return ''
+    
diff --git a/sfa/methods/ResolveGENI.py b/sfa/methods/ResolveGENI.py
new file mode 100644 (file)
index 0000000..83b6c39
--- /dev/null
@@ -0,0 +1,30 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+from sfa.trust.credential import Credential
+
+class ResolveGENI(Method):
+    """
+    Lookup a URN and return information about the corresponding object.
+    @param urn
+    
+    """
+    interfaces = ['registry']
+    accepts = [
+        Parameter(str, "URN"),
+        Parameter(type([str]), "List of credentials"),
+        ]
+    returns = Parameter(bool, "Success or Failure")
+
+    def call(self, xrn):
+
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['registry']:
+            mgr_type = self.api.config.SFA_REGISTRY_TYPE
+            manager_module = manager_base + ".registry_manager_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.Resolve(self.api, xrn, '')
+               
+        return {}
\ No newline at end of file
diff --git a/sfa/methods/Shutdown.py b/sfa/methods/Shutdown.py
new file mode 100644 (file)
index 0000000..cd36532
--- /dev/null
@@ -0,0 +1,39 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+
+
+class Shutdown(Method):
+    """
+    Perform an emergency shut down of a sliver. This operation is intended for administrative use. 
+    The sliver is shut down but remains available for further forensics.
+
+    @param slice_urn (string) URN of slice to renew
+    @param credentials ([string]) of credentials    
+    """
+    interfaces = ['geni_am']
+    accepts = [
+        Parameter(str, "Slice URN"),
+        Parameter(type([str]), "List of credentials"),
+        ]
+    returns = Parameter(bool, "Success or Failure")
+
+    def call(self, slice_xrn, creds):
+        hrn, type = urn_to_hrn(slice_xrn)
+
+        self.api.logger.info("interface: %s\ttarget-hrn: %s\tcaller-creds: %s\tmethod-name: %s"%(self.api.interface, hrn, creds, self.name))
+
+        # Find the valid credentials
+        ValidCreds = self.api.auth.checkCredentials(creds, 'shutdown', hrn)
+
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.Shutdown(self.api, slice_xrn, ValidCreds)
+
+        return ''
+    
diff --git a/sfa/methods/SliverStatus.py b/sfa/methods/SliverStatus.py
new file mode 100644 (file)
index 0000000..24d6ff2
--- /dev/null
@@ -0,0 +1,35 @@
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter
+
+class SliverStatus(Method):
+    """
+    Get the status of a sliver
+    
+    @param slice_urn (string) URN of slice to allocate to
+    
+    """
+    interfaces = ['geni_am']
+    accepts = [
+        Parameter(str, "Slice URN"),
+        ]
+    returns = Parameter(bool, "Success or Failure")
+
+    def call(self, slice_xrn, creds):
+        hrn, type = urn_to_hrn(slice_xrn)
+        
+        ValidCreds = self.api.auth.checkCredentials(creds, 'sliverstatus', hrn)
+
+        self.api.logger.info("interface: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, hrn, self.name))
+
+        manager_base = 'sfa.managers'
+
+        if self.api.interface in ['geni_am']:
+            mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+            manager_module = manager_base + ".geni_am_%s" % mgr_type
+            manager = __import__(manager_module, fromlist=[manager_base])
+            return manager.SliverStatus(self.api, slice_xrn, ValidCreds)
+
+        return ''
+    
diff --git a/sfa/methods/get_geni_aggregates.py b/sfa/methods/get_geni_aggregates.py
new file mode 100644 (file)
index 0000000..d09505f
--- /dev/null
@@ -0,0 +1,60 @@
+### $Id: get_slices.py 14387 2009-07-08 18:19:11Z faiyaza $
+### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/methods/get_aggregates.py $
+from types import StringTypes
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter, Mixed
+from sfa.trust.auth import Auth
+from sfa.server.aggregate import Aggregates
+
+class get_geni_aggregates(Method):
+    """
+    Get a list of connection information for all known GENI aggregates.      
+
+    @param cred credential string specifying the rights of the caller
+    @param a Human readable name (hrn or urn), or list of hrns or None
+    @return list of dictionaries with aggregate information.  
+    """
+
+    interfaces = ['registry', 'aggregate', 'slicemgr']
+    
+    accepts = [
+        Parameter(str, "Credential string"),        
+        Mixed(Parameter(str, "Human readable name (hrn or urn)"),
+              Parameter(None, "hrn not specified"))
+        ]
+    
+
+    returns = [Parameter(dict, "Aggregate interface information")]
+    
+    def call(self, cred, xrn = None):
+        hrn, type = urn_to_hrn(xrn)
+        self.api.auth.check(cred, 'list')
+        
+        geni_aggs = Aggregates(self.api, '/etc/sfa/geni_aggregates.xml')        
+
+        geni_aggs = geni_aggs.interfaces.values()
+
+        hrn_list = [] 
+        if hrn:
+            if isinstance(hrn, StringTypes):
+                hrn_list = [hrn]
+            elif isinstance(hrn, list):
+                hrn_list = hrn
+        
+        if not hrn_list:
+            interfaces = geni_aggs
+        else:
+            interfaces = [interface for interface in geni_aggs if interface['hrn'] in hrn_list]
+
+
+        # Remove Aggregate's default sfa-aggregate 
+        interfaces = interfaces[:-1]
+
+        
+        # Add urns
+        for interface in interfaces:
+            interface['urn'] = hrn_to_urn(interface['hrn'], 'authority')
+
+        return interfaces
diff --git a/sfa/plc/api-dev.py b/sfa/plc/api-dev.py
new file mode 100644 (file)
index 0000000..29b0cc8
--- /dev/null
@@ -0,0 +1,562 @@
+#
+# SFA XML-RPC and SOAP interfaces
+#
+### $Id: api.py 17793 2010-04-26 21:40:57Z tmack $
+### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/plc/api.py $
+#
+
+import sys
+import os
+import traceback
+import string
+import xmlrpclib
+from sfa.trust.auth import Auth
+from sfa.util.config import *
+from sfa.util.faults import *
+from sfa.util.debug import *
+from sfa.trust.rights import *
+from sfa.trust.credential import *
+from sfa.trust.certificate import *
+from sfa.util.namespace import *
+from sfa.util.api import *
+from sfa.util.nodemanager import NodeManager
+from sfa.util.sfalogging import *
+from collections import defaultdict
+
+
+class RecordInfo():
+    
+    shell = None
+    auth = None
+    
+    # pl records
+    sites = {}
+    slices = {}
+    persons = {}
+    nodes = {}
+    keys = {}
+
+    # sfa records
+    sfa_authorities = {}
+    sfa_slices = {}
+    sfa_users = {}
+    sfa_nodes = {}
+     
+    records = []
+
+    def __init__(self, api, records):
+        self.api = api
+        self.shell = api.plshell
+        self.auth = api.plauth
+        
+        site_ids = []
+        slice_ids = []
+        person_ids = []
+        node_ids = []
+
+        # put records into groups based on types
+        for record in records:
+            pointer = record['pointer']
+            if record['type'] == 'authority':
+                self.sfa_authorities[pointer] = record
+                self.records.append(record)
+                site_ids.append(record['pointer'])
+            elif record['type'] == 'slice':
+                self.sfa_slices[pointer] = record
+                self.records.append(record)
+                slice_ids.append(record['pointer'])
+            elif record['type'] == 'user':
+                self.sfa_users[pointer] = record
+                self.records.append(record)
+                person_ids.append(record['pointer'])
+            elif record['type'] == 'node':
+                self.sfa_nodes[pointer] = record
+                self.records.append(record)
+                node_ids.append(record['pointer']) 
+
+        # get pl info for these records
+        self.update_pl_sites(site_ids)
+        self.update_pl_slices(slice_ids)
+        self.update_pl_persons(person_ids)
+        self.update_pl_nodes(node_ids)
+       
+        site_ids = []
+        slice_ids = []
+        person_ids = []
+        node_ids = []
+        # now get pl records for all ids associated with 
+        # these records
+        for record in records:
+            if 'site_id' in record:
+                site_ids.append(record['site_id'])
+            if 'site_ids' in records:
+                site_ids.extend(record['site_ids'])
+            if 'person_ids' in record:
+                person_ids.extend(record['person_ids'])
+            if 'slice_ids' in record:
+                slice_ids.extend(record['slice_ids'])
+            if 'node_ids' in record:
+                node_ids.extend(record['node_ids'])
+
+        # get pl info for these records
+        self.update_pl_sites(site_ids)
+        self.update_pl_slices(slice_ids)
+        self.update_pl_persons(person_ids)
+        self.update_pl_nodes(node_ids)
+
+        # convert pl ids to hrns  
+        self.update_hrns()
+
+        # update sfa info
+        self.update_sfa_info(person_ids)
+
+    def update_pl_sites(self, site_ids):
+        """
+        Update site records with PL info 
+        """
+        if not site_ids:
+            return
+        sites = self.shell.GetSites(self.auth, site_ids)
+        for site in sites:
+            site_id = site['site_id']
+            self.sites[site_id] = site
+            if site_id in self.sfa_authorities:
+                self.sfa_authorities[site_id].update(site)
+
+    def update_pl_slices(self, slice_ids):
+        """
+        Update slice records with PL info
+        """
+        if not slice_ids:
+            return
+        slices = self.shell.GetSlices(self.auth, slice_ids)
+        for slice in slices:
+            slice_id = slice['slice_id']
+            self.slices[slice_id] = slice
+            if slice_id in self.sfa_slices:
+                self.sfa_slices[slice_id].update(slice)
+
+    def update_pl_persons(self, person_ids):
+        """
+        Update person records with PL info
+        """
+        key_ids = []
+        if not person_ids:
+            return
+        persons = self.shell.GetPersons(self.auth, person_ids)
+        for person in persons:
+            person_id = person['person_id']
+            self.persons[person_id] = person 
+            key_ids.extend(person['key_ids'])
+            if person_id in self.sfa_users:
+                self.sfa_users[person_id].update(person)
+        self.update_pl_keys(key_ids)
+
+    def update_pl_keys(self, key_ids):
+        """
+        Update user records with PL public key info
+        """
+        if not key_ids:
+            return
+        keys = self.shell.GetKeys(self.auth, key_ids)
+        for key in keys:
+            person_id = key['person_id']
+            self.keys[key['key_id']] = key
+            if person_id in self.sfa_users:
+                person = self.sfa_users[person_id]    
+                if not 'keys' in person:
+                    person['keys'] = [key['key']]
+                else: 
+                    person['keys'].append(key['key'])
+
+    def update_pl_nodes(self, node_ids):
+        """
+        Update node records with PL info
+        """
+        if not node_ids:
+            return 
+        nodes = self.shell.GetNodes(self.auth, node_ids)
+        for node in nodes:
+            node_id = node['node_id']
+            self.nodes[node['node_id']] = node
+            if node_id in self.sfa_nodes:
+                self.sfa_nodes[node_id].update(node)
+    
+
+    def update_hrns(self):
+        """
+        Convert pl ids to hrns
+        """
+        for record in self.records:
+            # get all necessary data
+            type = record['type']
+            pointer = record['pointer']
+            auth_hrn = self.api.hrn
+            login_base = ''
+            if pointer == -1:
+                continue       
+
+            if 'site_id' in record:
+                site = self.sites[record['site_id']]
+                login_base = site['login_base']
+                record['site'] = ".".join([auth_hrn, login_base])
+            if 'person_ids' in record:
+                emails = [self.persons[person_id]['email'] for person_id in record['person_ids'] \
+                          if person_id in self.persons]
+                usernames = [email.split('@')[0] for email in emails]
+                person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
+                record['persons'] = person_hrns
+            if 'slice_ids' in record:
+                slicenames = [self.slices[slice_id]['name'] for slice_id in record['slice_ids'] \
+                              if slice_id in self.slices]
+                slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
+                record['slices'] = slice_hrns
+            if 'node_ids' in record:
+                hostnames = [self.nodes[node_id]['hostname'] for node_id in record['node_ids'] \
+                             if node_id in self.nodes]
+                node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
+                record['nodes'] = node_hrns
+            if 'site_ids' in record:
+                login_bases = [self.sites[site_id]['login_base'] for site_id in record['site_ids'] \
+                               if site_id in self.sites]
+                site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
+                record['sites'] = site_hrns 
+
+    def update_sfa_info(self, person_ids):
+        from sfa.util.table import SfaTable
+        table = SfaTable()
+        persons = table.find({'type': 'user', 'pointer': person_ids})
+        # create a hrns keyed on the sfa record's pointer.
+        # Its possible for  multiple records to have the same pointer so 
+        # the dict's value will be a list of hrns.
+        person_dict = defaultdict(list)
+        for person in persons:
+            person_dict[person['pointer']].append(person['hrn'])                       
+
+        def startswith(prefix, values):
+            return [value for value in values if value.startswith(prefix)]
+
+        for record in self.records:
+            authority = record['authority']
+            if record['pointer'] == -1:
+                continue
+            
+            if record['type'] == 'slice':
+                # all slice users are researchers
+                record['PI'] = []
+                record['researchers'] = []    
+                for person_id in record['person_ids']:
+                    record['researchers'].extend(person_dict[person_id])
+                # also add the pis at the slice's site
+                site = self.sites[record['site_id']]    
+                for person_id in site['person_ids']:
+                    person = self.persons[person_id]
+                    if 'pi' in person['roles']:
+                        # PLCAPI doesn't support per site roles 
+                        # (a pi has the pi role at every site he belongs to).
+                        # We shouldnt allow this in SFA         
+                        record['PI'].extend(startswith(authority, person_dict[person_id]))    
+
+            elif record['type'] == 'authority':
+                record['PI'] = []
+                record['operator'] = []
+                record['owner'] = []
+                for person_id in record['person_ids']:
+                    person = self.persons[person_id]
+                    if 'pi' in person['roles']:
+                        # only get PI's at this site
+                        record['PI'].extend(startswith(record['hrn'], person_dict[person_id]))
+                    if 'tech' in person['roles']:
+                        # only get PI's at this site
+                        record['operator'].extend(startswith(record['hrn'], person_dict[person_id]))
+                    if 'admin' in person['roles']:
+                        record['owner'].extend(startswith(record['hrn'], person_dict[person_id]))
+                            
+            elif record['type'] == 'node':
+                record['dns'] = record['hostname']
+
+            elif record['type'] == 'user':
+                record['email'] = record['email']                   
+                                   
+                  
+                 
+                
+                    
+    def get_records(self):
+        return self.records
+
+class SfaAPI(BaseAPI):
+
+    # flat list of method names
+    import sfa.methods
+    methods = sfa.methods.all
+    
+    def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", 
+                 methods='sfa.methods', peer_cert = None, interface = None, 
+                key_file = None, cert_file = None, cache = None):
+        BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, \
+                         peer_cert=peer_cert, interface=interface, key_file=key_file, \
+                         cert_file=cert_file, cache=cache)
+        self.encoding = encoding
+
+        from sfa.util.table import SfaTable
+        self.SfaTable = SfaTable
+        # Better just be documenting the API
+        if config is None:
+            return
+
+        # Load configuration
+        self.config = Config(config)
+        self.auth = Auth(peer_cert)
+        self.interface = interface
+        self.key_file = key_file
+        self.key = Keypair(filename=self.key_file)
+        self.cert_file = cert_file
+        self.cert = Certificate(filename=self.cert_file)
+        self.credential = None
+        # Initialize the PLC shell only if SFA wraps a myPLC
+        rspec_type = self.config.get_aggregate_type()
+        if (rspec_type == 'pl' or rspec_type == 'vini'):
+            self.plshell = self.getPLCShell()
+            self.plshell_version = "4.3"
+
+        self.hrn = self.config.SFA_INTERFACE_HRN
+        self.time_format = "%Y-%m-%d %H:%M:%S"
+        self.logger=get_sfa_logger()
+
+    def getPLCShell(self):
+        self.plauth = {'Username': self.config.SFA_PLC_USER,
+                       'AuthMethod': 'password',
+                       'AuthString': self.config.SFA_PLC_PASSWORD}
+
+        self.plshell_type = 'xmlrpc' 
+        # connect via xmlrpc
+        url = self.config.SFA_PLC_URL
+        shell = xmlrpclib.Server(url, verbose = 0, allow_none = True)
+        return shell
+
+    def getCredential(self):
+        if self.interface in ['registry']:
+            return self.getCredentialFromLocalRegistry()
+        else:
+            return self.getCredentialFromRegistry()
+    
+    def getCredentialFromRegistry(self):
+        """ 
+        Get our credential from a remote registry 
+        """
+        type = 'authority'
+        path = self.config.SFA_DATA_DIR
+        filename = ".".join([self.interface, self.hrn, type, "cred"])
+        cred_filename = path + os.sep + filename
+        try:
+            credential = Credential(filename = cred_filename)
+            return credential.save_to_string(save_parents=True)
+        except IOError:
+            from sfa.server.registry import Registries
+            registries = Registries(self)
+            registry = registries[self.hrn]
+            cert_string=self.cert.save_to_string(save_parents=True)
+            # get self credential
+            self_cred = registry.get_self_credential(cert_string, type, self.hrn)
+            # get credential
+            cred = registry.get_credential(self_cred, type, self.hrn)
+            
+            # save cred to file
+            Credential(string=cred).save_to_file(cred_filename, save_parents=True)
+            return cred
+
+    def getCredentialFromLocalRegistry(self):
+        """
+        Get our current credential directly from the local registry.
+        """
+
+        hrn = self.hrn
+        auth_hrn = self.auth.get_authority(hrn)
+    
+        # is this a root or sub authority
+        if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN:
+            auth_hrn = hrn
+        auth_info = self.auth.get_auth_info(auth_hrn)
+        table = self.SfaTable()
+        records = table.findObjects(hrn)
+        if not records:
+            raise RecordNotFound
+        record = records[0]
+        type = record['type']
+        object_gid = record.get_gid_object()
+        new_cred = Credential(subject = object_gid.get_subject())
+        new_cred.set_gid_caller(object_gid)
+        new_cred.set_gid_object(object_gid)
+        new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
+        new_cred.set_pubkey(object_gid.get_pubkey())
+        r1 = determine_rights(type, hrn)
+        new_cred.set_privileges(r1)
+
+        auth_kind = "authority,ma,sa"
+
+        new_cred.set_parent(self.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
+
+        new_cred.encode()
+        new_cred.sign()
+
+        return new_cred.save_to_string(save_parents=True)
+   
+
+    def loadCredential (self):
+        """
+        Attempt to load credential from file if it exists. If it doesnt get
+        credential from registry.
+        """
+
+        # see if this file exists
+        # XX This is really the aggregate's credential. Using this is easier than getting
+        # the registry's credential from iteslf (ssl errors).   
+        ma_cred_filename = self.config.SFA_DATA_DIR + os.sep + self.interface + self.hrn + ".ma.cred"
+        try:
+            self.credential = Credential(filename = ma_cred_filename)
+        except IOError:
+            self.credential = self.getCredentialFromRegistry()
+
+    ##
+    # Convert SFA fields to PLC fields for use when registering up updating
+    # registry record in the PLC database
+    #
+    # @param type type of record (user, slice, ...)
+    # @param hrn human readable name
+    # @param sfa_fields dictionary of SFA fields
+    # @param pl_fields dictionary of PLC fields (output)
+
+    def sfa_fields_to_pl_fields(self, type, hrn, record):
+
+        def convert_ints(tmpdict, int_fields):
+            for field in int_fields:
+                if field in tmpdict:
+                    tmpdict[field] = int(tmpdict[field])
+
+        pl_record = {}
+        #for field in record:
+        #    pl_record[field] = record[field]
+        if type == "slice":
+            if not "instantiation" in pl_record:
+                pl_record["instantiation"] = "plc-instantiated"
+            pl_record["name"] = hrn_to_pl_slicename(hrn)
+           if "url" in record:
+               pl_record["url"] = record["url"]
+           if "description" in record:
+               pl_record["description"] = record["description"]
+           if "expires" in record:
+               pl_record["expires"] = int(record["expires"])
+
+        elif type == "node":
+            if not "hostname" in pl_record:
+                if not "hostname" in record:
+                    raise MissingSfaInfo("hostname")
+                pl_record["hostname"] = record["hostname"]
+            if not "model" in pl_record:
+                pl_record["model"] = "geni"
+
+        elif type == "authority":
+            pl_record["login_base"] = hrn_to_pl_login_base(hrn)
+
+            if not "name" in pl_record:
+                pl_record["name"] = hrn
+
+            if not "abbreviated_name" in pl_record:
+                pl_record["abbreviated_name"] = hrn
+
+            if not "enabled" in pl_record:
+                pl_record["enabled"] = True
+
+            if not "is_public" in pl_record:
+                pl_record["is_public"] = True
+
+        return pl_record
+
+
+    def fill_record_info(self, records):
+        """
+        Given a SFA record, fill in the PLC specific and SFA specific
+        fields in the record. 
+        """
+        if not isinstance(records, list):
+            records = [records]
+
+        record_info = RecordInfo(self, records)
+        return record_info.get_records()
+
+    def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
+        # get a list of the HRNs tht are members of the old and new records
+        if oldRecord:
+            oldList = oldRecord.get(listName, [])
+        else:
+            oldList = []     
+        newList = record.get(listName, [])
+
+        # if the lists are the same, then we don't have to update anything
+        if (oldList == newList):
+            return
+
+        # build a list of the new person ids, by looking up each person to get
+        # their pointer
+        newIdList = []
+        table = self.SfaTable()
+        records = table.find({'type': 'user', 'hrn': newList})
+        for rec in records:
+            newIdList.append(rec['pointer'])
+
+        # build a list of the old person ids from the person_ids field 
+        if oldRecord:
+            oldIdList = oldRecord.get("person_ids", [])
+            containerId = oldRecord.get_pointer()
+        else:
+            # if oldRecord==None, then we are doing a Register, instead of an
+            # update.
+            oldIdList = []
+            containerId = record.get_pointer()
+
+    # add people who are in the new list, but not the oldList
+        for personId in newIdList:
+            if not (personId in oldIdList):
+                addFunc(self.plauth, personId, containerId)
+
+        # remove people who are in the old list, but not the new list
+        for personId in oldIdList:
+            if not (personId in newIdList):
+                delFunc(self.plauth, personId, containerId)
+
+    def update_membership(self, oldRecord, record):
+        if record.type == "slice":
+            self.update_membership_list(oldRecord, record, 'researcher',
+                                        self.plshell.AddPersonToSlice,
+                                        self.plshell.DeletePersonFromSlice)
+        elif record.type == "authority":
+            # xxx TODO
+            pass
+
+
+
+class ComponentAPI(BaseAPI):
+
+    def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", methods='sfa.methods',
+                 peer_cert = None, interface = None, key_file = None, cert_file = None):
+
+        BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, peer_cert=peer_cert,
+                         interface=interface, key_file=key_file, cert_file=cert_file)
+        self.encoding = encoding
+
+        # Better just be documenting the API
+        if config is None:
+            return
+
+        self.nodemanager = NodeManager(self.config)
+
+    def sliver_exists(self):
+        sliver_dict = self.nodemanager.GetXIDs()
+        if slicename in sliver_dict.keys():
+            return True
+        else:
+            return False
index cbe321b..0f5cb01 100644 (file)
@@ -76,6 +76,7 @@ class SfaAPI(BaseAPI):
                        'AuthMethod': 'password',
                        'AuthString': self.config.SFA_PLC_PASSWORD}
 
+
         self.plshell_type = 'xmlrpc' 
         # connect via xmlrpc
         url = self.config.SFA_PLC_URL
@@ -135,8 +136,8 @@ class SfaAPI(BaseAPI):
         new_cred = Credential(subject = object_gid.get_subject())
         new_cred.set_gid_caller(object_gid)
         new_cred.set_gid_object(object_gid)
-        new_cred.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
-        new_cred.set_pubkey(object_gid.get_pubkey())
+        new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
+        
         r1 = determine_rights(type, hrn)
         new_cred.set_privileges(r1)
 
index 0350bbb..39cb28c 100755 (executable)
@@ -82,8 +82,9 @@ def main():
     AuthHierarchy = sfaImporter.AuthHierarchy
     TrustedRoots = sfaImporter.TrustedRoots
     table = SfaTable()
+
     if not table.exists():
-        table.create()
+       table.create()
 
     # create root authority 
     sfaImporter.create_top_level_auth_records(root_auth)
@@ -148,10 +149,11 @@ def main():
     slices_dict = {}
     for slice in slices:
         slices_dict[slice['slice_id']] = slice
-
     # start importing 
     for site in sites:
         site_hrn = interface_hrn + "." + site['login_base']
+        print "Importing site: %s" % site_hrn
+
         # import if hrn is not in list of existing hrns or if the hrn exists
         # but its not a site record
         if site_hrn not in existing_hrns or \
index 9a5113e..b95bc5a 100644 (file)
@@ -200,6 +200,7 @@ class sfaImport:
         plc_auth = self.plc_auth
         sitename = site['login_base']
         sitename = cleanup_string(sitename)
+        print 'importing site %s' % sitename
         hrn = parent_hrn + "." + sitename
         urn = hrn_to_urn(hrn, 'authority')
         # Hardcode 'internet2' into the hrn for sites hosting
index 3c53504..3625bf0 100644 (file)
@@ -168,50 +168,62 @@ class Slices:
 
         return sfa_peer 
 
-    def verify_site(self, registry, credential, slice_hrn, peer, sfa_peer):
+    def verify_site(self, registry, credential, slice_hrn, peer, sfa_peer, reg_objects=None):
         authority = get_authority(slice_hrn)
         authority_urn = hrn_to_urn(authority, 'authority')
-        site_records = registry.resolve(credential, authority_urn)
+        
+        if reg_objects:
+            site = reg_objects['site']
+        else:
+            site_records = registry.resolve(credential, authority_urn)
+            site = {}            
+            for site_record in site_records:            
+                if site_record['type'] == 'authority':
+                    site = site_record
+            if not site:
+                raise RecordNotFound(authority)
             
-        site = {}
-        for site_record in site_records:
-            if site_record['type'] == 'authority':
-                site = site_record
-        if not site:
-            raise RecordNotFound(authority)
         remote_site_id = site.pop('site_id')    
                 
         login_base = get_leaf(authority)
         sites = self.api.plshell.GetSites(self.api.plauth, login_base)
+
         if not sites:
             site_id = self.api.plshell.AddSite(self.api.plauth, site)
             if peer:
                 self.api.plshell.BindObjectToPeer(self.api.plauth, 'site', site_id, peer, remote_site_id)   
             # mark this site as an sfa peer record
-            if sfa_peer:
+            if sfa_peer and not reg_objects:
                 peer_dict = {'type': 'authority', 'hrn': authority, 'peer_authority': sfa_peer, 'pointer': site_id}
                 registry.register_peer_object(credential, peer_dict)
         else:
             site_id = sites[0]['site_id']
             remote_site_id = sites[0]['peer_site_id']
+            
            old_site = sites[0]
-           #the site is alredy on the remote agg. Let us update(e.g. max_slices field) it with the latest info.
-           self.sync_site(old_site, site, peer)
+           #the site is already on the remote agg. Let us update(e.g. max_slices field) it with the latest info.
+            self.sync_site(old_site, site, peer)
 
 
         return (site_id, remote_site_id) 
 
-    def verify_slice(self, registry, credential, slice_hrn, site_id, remote_site_id, peer, sfa_peer):
+    def verify_slice(self, registry, credential, slice_hrn, site_id, remote_site_id, peer, sfa_peer, reg_objects=None):
         slice = {}
         slice_record = None
         authority = get_authority(slice_hrn)
-        slice_records = registry.resolve(credential, slice_hrn)
 
-        for record in slice_records:
-            if record['type'] in ['slice']:
-                slice_record = record
-        if not slice_record:
-            raise RecordNotFound(hrn)
+        if reg_objects:
+            slice_record = reg_objects['slice_record']
+        else:
+            slice_records = registry.resolve(credential, slice_hrn)
+    
+            for record in slice_records:
+                if record['type'] in ['slice']:
+                    slice_record = record
+            if not slice_record:
+                raise RecordNotFound(hrn)
+            
+        
         slicename = hrn_to_pl_slicename(slice_hrn)
         parts = slicename.split("_")
         login_base = parts[0]
@@ -245,33 +257,39 @@ class Slices:
            self.sync_slice(slice, slice_record, peer)
 
         slice['peer_slice_id'] = slice_record['pointer']
-        self.verify_persons(registry, credential, slice_record, site_id, remote_site_id, peer, sfa_peer)
+        self.verify_persons(registry, credential, slice_record, site_id, remote_site_id, peer, sfa_peer, reg_objects)
     
         return slice        
 
-    def verify_persons(self, registry, credential, slice_record, site_id, remote_site_id, peer, sfa_peer):
+    def verify_persons(self, registry, credential, slice_record, site_id, remote_site_id, peer, sfa_peer, reg_objects=None):
         # get the list of valid slice users from the registry and make 
         # sure they are added to the slice 
         slicename = hrn_to_pl_slicename(slice_record['hrn'])
-        researchers = slice_record.get('researcher', [])
+        if reg_objects:
+            researchers = reg_objects['users'].keys()
+        else:
+            researchers = slice_record.get('researcher', [])
         for researcher in researchers:
-            person_record = {}
-            person_records = registry.resolve(credential, researcher)
-            for record in person_records:
-                if record['type'] in ['user'] and record['enabled']:
-                    person_record = record
-            if not person_record:
-                return 1
-            person_dict = person_record
+            if reg_objects:
+                person_dict = reg_objects['users'][researcher]
+            else:
+                person_records = registry.resolve(credential, researcher)
+                for record in person_records:
+                    if record['type'] in ['user'] and record['enabled']:
+                        person_record = record
+                if not person_record:
+                    return 1
+                person_dict = person_record
+
             local_person=False
             if peer:
                 peer_id = self.api.plshell.GetPeers(self.api.plauth, {'shortname': peer}, ['peer_id'])[0]['peer_id']
                 persons = self.api.plshell.GetPersons(self.api.plauth, {'email': [person_dict['email']], 'peer_id': peer_id}, ['person_id', 'key_ids'])
-               if not persons:
-                   persons = self.api.plshell.GetPersons(self.api.plauth, [person_dict['email']], ['person_id', 'key_ids'])
-                   if persons:
-                       local_person=True
-
+                if not persons:
+                    persons = self.api.plshell.GetPersons(self.api.plauth, [person_dict['email']], ['person_id', 'key_ids'])
+                    if persons:
+                        local_person=True
+                        
             else:
                 persons = self.api.plshell.GetPersons(self.api.plauth, [person_dict['email']], ['person_id', 'key_ids'])   
         
index 4ae8e9f..65b8d83 100644 (file)
@@ -13,7 +13,7 @@ import traceback
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
 import sfa.util.soapprotocol as soapprotocol
 
+
 # GeniLight client support is optional
 try:
     from egeni.geniLight_client import *
@@ -100,6 +100,8 @@ class Interfaces(dict):
             return peer_gids
         trusted_certs_dir = self.api.config.get_trustedroots_dir()
         for new_hrn in new_hrns:
+            if not new_hrn:
+                continue
             # the gid for this interface should already be installed  
             if new_hrn == self.api.config.SFA_INTERFACE_HRN:
                 continue
@@ -184,9 +186,10 @@ class Interfaces(dict):
             # make sure the required fields are present and not null
             if not all([interface.get(key) for key in required_fields]):
                 continue
+            
             hrn, address, port = interface['hrn'], interface['addr'], interface['port']
             url = 'http://%(address)s:%(port)s' % locals()
+            
             # check which client we should use
             # sfa.util.xmlrpcprotocol is default
             client_type = 'xmlrpcprotocol'
index c909346..e86781f 100755 (executable)
@@ -18,6 +18,7 @@ class AuthenticatedApi(BaseApi):
         BaseApi.__init__(self, encoding)
         if trustedRootsDir:
             self.trusted_cert_list = TrustedRootList(trustedRootsDir).get_list()
+            self.trusted_cert_file_list = TrustedRootList(trustedRootsDir).get_file_list()
         else:
             self.trusted_cert_list = None
 
@@ -40,13 +41,7 @@ class AuthenticatedApi(BaseApi):
 
     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)
+            cred.verify(self.trusted_cert_file_list)
 
     def authenticateGid(self, gidStr, argList, requestHash):
         gid = GID(string = gidStr)
index 5e0905f..06393ce 100755 (executable)
@@ -34,7 +34,7 @@ registry_port=12345
 aggregate_port=12346
 slicemgr_port=12347
 component_port=12346
-
+geni_am_port=12348
 import os, os.path
 import sys
 from optparse import OptionParser
@@ -147,7 +147,14 @@ def init_server(options, config):
         try: manager = __import__(manager_module, fromlist=[manager_base])
         except: manager = None
         if manager and hasattr(manager, 'init_server'):
-            manager.init_server()    
+            manager.init_server()
+    if options.gam:
+        mgr_type = config.SFA_GENI_AGGREGATE_TYPE
+        manager_module = manager_base + ".geni_am_%s" % mgr_type
+        try: manager = __import__(manager_module, fromlist=[manager_base])
+        except: manager = None
+        if manager and hasattr(manager, 'init_server'):
+            manager.init_server()
             
 
 def sync_interfaces(server_key_file, server_cert_file):
@@ -179,6 +186,8 @@ def main():
          help="run aggregate manager", default=False)
     parser.add_option("-c", "--component", dest="cm", action="store_true",
          help="run component server", default=False)
+    parser.add_option("-g", "--geniam", dest="gam", action="store_true",
+         help="run GENI aggregate manager", default=False)
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true", 
          help="verbose mode", default=False)
     parser.add_option("-d", "--daemon", dest="daemon", action="store_true",
@@ -219,5 +228,11 @@ def main():
         c = Component("", component_port, server_key_file, server_cert_file)
         c.start()
 
+    # start GENI aggregate manager
+    if (options.gam):
+        from sfa.server.geni_aggregate import GENIAggregate
+        g = GENIAggregate("", geni_am_port, server_key_file, server_cert_file)
+        g.start()
+
 if __name__ == "__main__":
     main()
index 73fb592..470dc5e 100644 (file)
@@ -5,17 +5,17 @@
 ### $URL$
 #
 
-import time
 
 from sfa.trust.credential import Credential
 from sfa.trust.trustedroot import TrustedRootList
-from sfa.trust.rights import RightList
 from sfa.util.faults import *
 from sfa.trust.hierarchy import Hierarchy
 from sfa.util.config import *
 from sfa.util.namespace import *
-from sfa.trust.gid import GID
 from sfa.util.sfaticket import *
+from sfa.util.sfalogging import logger
+
+import sys
 
 class Auth:
     """
@@ -31,6 +31,25 @@ class Auth:
 
     def load_trusted_certs(self):
         self.trusted_cert_list = TrustedRootList(self.config.get_trustedroots_dir()).get_list()
+        self.trusted_cert_file_list = TrustedRootList(self.config.get_trustedroots_dir()).get_file_list()
+
+        
+        
+    def checkCredentials(self, creds, operation, hrn = None):
+        valid = []
+        for cred in creds:
+            try:
+                self.check(cred, operation, hrn)
+                valid.append(cred)
+            except:
+                error = sys.exc_info()[:2]
+                continue
+            
+        if not len(valid):
+            raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
+        
+        return valid
+        
         
     def check(self, cred, operation, hrn = None):
         """
@@ -58,14 +77,10 @@ class Auth:
                 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)
+            self.client_cred.verify(self.trusted_cert_file_list)
         else:
            raise MissingTrustedRoots(self.config.get_trustedroots_dir())
-
+       
         # Make sure the credential's target matches the specified hrn. 
         # This check does not apply to trusted peers 
         trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
@@ -107,13 +122,7 @@ class Auth:
 
     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)
+            cred.verify(self.trusted_cert_file_list)
 
     def authenticateGid(self, gidStr, argList, requestHash=None):
         gid = GID(string = gidStr)
@@ -229,6 +238,7 @@ class Auth:
         rl = RightList()
         type = record['type']
 
+
         if type=="slice":
             researchers = record.get("researcher", [])
             pis = record.get("PI", [])
@@ -243,11 +253,15 @@ class Auth:
             pis = record.get("PI", [])
             operators = record.get("operator", [])
             if (caller_hrn == self.config.SFA_INTERFACE_HRN):
-                rl.add("authority,sa,ma",)
+                rl.add("authority")
+                rl.add("sa")
+                rl.add("ma")
             if (caller_hrn in pis):
-                rl.add("authority,sa")
+                rl.add("authority")
+                rl.add("sa")
             if (caller_hrn in operators):
-                rl.add("authority,ma")
+                rl.add("authority")
+                rl.add("ma")
 
         elif type == "user":
             rl.add("refresh")
index 76b99db..9fd58ed 100644 (file)
@@ -22,8 +22,9 @@ import traceback
 from OpenSSL import crypto
 import M2Crypto
 from M2Crypto import X509
-from M2Crypto import EVP
-
+from tempfile import mkstemp
+from sfa.util.sfalogging import logger
+from sfa.util.namespace import urn_to_hrn
 from sfa.util.faults import *
 
 def convert_public_key(key):
@@ -31,10 +32,10 @@ def convert_public_key(key):
     if not os.path.isfile(keyconvert_path):
         raise IOError, "Could not find keyconvert in %s" % keyconvert_path
 
-    # we can only convert rsa keys 
+    # we can only convert rsa keys
     if "ssh-dss" in key:
         return None
-    
+
     (ssh_f, ssh_fn) = tempfile.mkstemp()
     ssl_fn = tempfile.mktemp()
     os.write(ssh_f, key)
@@ -42,13 +43,13 @@ def convert_public_key(key):
 
     cmd = keyconvert_path + " " + ssh_fn + " " + ssl_fn
     os.system(cmd)
-    
+
     # this check leaves the temporary file containing the public key so
     # that it can be expected to see why it failed.
     # TODO: for production, cleanup the temporary files
     if not os.path.exists(ssl_fn):
         return None
-    
+
     k = Keypair()
     try:
         k.load_pubkey_from_file(ssl_fn)
@@ -68,140 +69,141 @@ def convert_public_key(key):
 # may represent only a public key (this usage is consistent with OpenSSL).
 
 class Keypair:
-   key = None       # public/private keypair
-   m2key = None     # public key (m2crypto format)
-
-   ##
-   # Creates a Keypair object
-   # @param create If create==True, creates a new public/private key and
-   #     stores it in the object
-   # @param string If string!=None, load the keypair from the string (PEM)
-   # @param filename If filename!=None, load the keypair from the file
-
-   def __init__(self, create=False, string=None, filename=None):
-      if create:
-         self.create()
-      if string:
-         self.load_from_string(string)
-      if filename:
-         self.load_from_file(filename)
-
-   ##
-   # Create a RSA public/private key pair and store it inside the keypair object
-
-   def create(self):
-      self.key = crypto.PKey()
-      self.key.generate_key(crypto.TYPE_RSA, 1024)
-
-   ##
-   # Save the private key to a file
-   # @param filename name of file to store the keypair in
-
-   def save_to_file(self, filename):
-      open(filename, 'w').write(self.as_pem())
-
-   ##
-   # Load the private key from a file. Implicity the private key includes the public key.
-
-   def load_from_file(self, filename):
-      buffer = open(filename, 'r').read()
-      self.load_from_string(buffer)
-
-   ##
-   # Load the private key from a string. Implicitly the private key includes the public key.
-
-   def load_from_string(self, string):
-      self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string)
-      self.m2key = M2Crypto.EVP.load_key_string(string)
-
-   ##
-   #  Load the public key from a string. No private key is loaded. 
-
-   def load_pubkey_from_file(self, filename):
-      # load the m2 public key
-      m2rsakey = M2Crypto.RSA.load_pub_key(filename)
-      self.m2key = M2Crypto.EVP.PKey()
-      self.m2key.assign_rsa(m2rsakey)
-
-      # create an m2 x509 cert
-      m2name = M2Crypto.X509.X509_Name()
-      m2name.add_entry_by_txt(field="CN", type=0x1001, entry="junk", len=-1, loc=-1, set=0)
-      m2x509 = M2Crypto.X509.X509()
-      m2x509.set_pubkey(self.m2key)
-      m2x509.set_serial_number(0)
-      m2x509.set_issuer_name(m2name)
-      m2x509.set_subject_name(m2name)
-      ASN1 = M2Crypto.ASN1.ASN1_UTCTIME()
-      ASN1.set_time(500)
-      m2x509.set_not_before(ASN1)
-      m2x509.set_not_after(ASN1)
-      junk_key = Keypair(create=True)
-      m2x509.sign(pkey=junk_key.get_m2_pkey(), md="sha1")
-
-      # convert the m2 x509 cert to a pyopenssl x509
-      m2pem = m2x509.as_pem()
-      pyx509 = crypto.load_certificate(crypto.FILETYPE_PEM, m2pem)
-
-      # get the pyopenssl pkey from the pyopenssl x509
-      self.key = pyx509.get_pubkey()
-
-   ##
-   # Load the public key from a string. No private key is loaded.
-
-   def load_pubkey_from_string(self, string):
-      (f, fn) = tempfile.mkstemp()
-      os.write(f, string)
-      os.close(f)
-      self.load_pubkey_from_file(fn)
-      os.remove(fn)
-
-   ##
-   # Return the private key in PEM format.
-
-   def as_pem(self):
-      return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.key)
-
-   ##
-   # Return an M2Crypto key object
-
-   def get_m2_pkey(self):
-      if not self.m2key:
-         self.m2key = M2Crypto.EVP.load_key_string(self.as_pem())
-      return self.m2key
-
-   ##
-   # Returns a string containing the public key represented by this object.
-
-   def get_pubkey_string(self):
-      m2pkey = self.get_m2_pkey()
-      return base64.b64encode(m2pkey.as_der())
-
-   ##
-   # Return an OpenSSL pkey object
-
-   def get_openssl_pkey(self):
-      return self.key
-
-   ##
-   # Given another Keypair object, return TRUE if the two keys are the same.
+    key = None       # public/private keypair
+    m2key = None     # public key (m2crypto format)
+
+    ##
+    # Creates a Keypair object
+    # @param create If create==True, creates a new public/private key and
+    #     stores it in the object
+    # @param string If string!=None, load the keypair from the string (PEM)
+    # @param filename If filename!=None, load the keypair from the file
+
+    def __init__(self, create=False, string=None, filename=None):
+        if create:
+            self.create()
+        if string:
+            self.load_from_string(string)
+        if filename:
+            self.load_from_file(filename)
+
+    ##
+    # Create a RSA public/private key pair and store it inside the keypair object
+
+    def create(self):
+        self.key = crypto.PKey()
+        self.key.generate_key(crypto.TYPE_RSA, 1024)
+
+    ##
+    # Save the private key to a file
+    # @param filename name of file to store the keypair in
+
+    def save_to_file(self, filename):
+        open(filename, 'w').write(self.as_pem())
+
+    ##
+    # Load the private key from a file. Implicity the private key includes the public key.
+
+    def load_from_file(self, filename):
+        buffer = open(filename, 'r').read()
+        self.load_from_string(buffer)
 
-   def is_same(self, pkey):
-      return self.as_pem() == pkey.as_pem()
-
-   def sign_string(self, data):
-      k = self.get_m2_pkey()
-      k.sign_init()
-      k.sign_update(data)
-      return base64.b64encode(k.sign_final())
-
-   def verify_string(self, data, sig):
-      k = self.get_m2_pkey()
-      k.verify_init()
-      k.verify_update(data)
-      return M2Crypto.m2.verify_final(k.ctx, base64.b64decode(sig), k.pkey)
-
-   def compute_hash(self, value):
-      return self.sign_string(str(value))      
+    ##
+    # Load the private key from a string. Implicitly the private key includes the public key.
+
+    def load_from_string(self, string):
+        self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, string)
+        self.m2key = M2Crypto.EVP.load_key_string(string)
+
+    ##
+    #  Load the public key from a string. No private key is loaded.
+
+    def load_pubkey_from_file(self, filename):
+        # load the m2 public key
+        m2rsakey = M2Crypto.RSA.load_pub_key(filename)
+        self.m2key = M2Crypto.EVP.PKey()
+        self.m2key.assign_rsa(m2rsakey)
+
+        # create an m2 x509 cert
+        m2name = M2Crypto.X509.X509_Name()
+        m2name.add_entry_by_txt(field="CN", type=0x1001, entry="junk", len=-1, loc=-1, set=0)
+        m2x509 = M2Crypto.X509.X509()
+        m2x509.set_pubkey(self.m2key)
+        m2x509.set_serial_number(0)
+        m2x509.set_issuer_name(m2name)
+        m2x509.set_subject_name(m2name)
+        ASN1 = M2Crypto.ASN1.ASN1_UTCTIME()
+        ASN1.set_time(500)
+        m2x509.set_not_before(ASN1)
+        m2x509.set_not_after(ASN1)
+        junk_key = Keypair(create=True)
+        m2x509.sign(pkey=junk_key.get_m2_pkey(), md="sha1")
+
+        # convert the m2 x509 cert to a pyopenssl x509
+        m2pem = m2x509.as_pem()
+        pyx509 = crypto.load_certificate(crypto.FILETYPE_PEM, m2pem)
+
+        # get the pyopenssl pkey from the pyopenssl x509
+        self.key = pyx509.get_pubkey()
+
+    ##
+    # Load the public key from a string. No private key is loaded.
+
+    def load_pubkey_from_string(self, string):
+        (f, fn) = tempfile.mkstemp()
+        os.write(f, string)
+        os.close(f)
+        self.load_pubkey_from_file(fn)
+        os.remove(fn)
+
+    ##
+    # Return the private key in PEM format.
+
+    def as_pem(self):
+        return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.key)
+
+    ##
+    # Return an M2Crypto key object
+
+    def get_m2_pkey(self):
+        if not self.m2key:
+            self.m2key = M2Crypto.EVP.load_key_string(self.as_pem())
+        return self.m2key
+
+    ##
+    # Returns a string containing the public key represented by this object.
+
+    def get_pubkey_string(self):
+        m2pkey = self.get_m2_pkey()
+        return base64.b64encode(m2pkey.as_der())
+
+    ##
+    # Return an OpenSSL pkey object
+
+    def get_openssl_pkey(self):
+        return self.key
+
+
+    ##
+    # Given another Keypair object, return TRUE if the two keys are the same.
+
+    def is_same(self, pkey):
+        return self.as_pem() == pkey.as_pem()
+
+    def sign_string(self, data):
+        k = self.get_m2_pkey()
+        k.sign_init()
+        k.sign_update(data)
+        return base64.b64encode(k.sign_final())
+
+    def verify_string(self, data, sig):
+        k = self.get_m2_pkey()
+        k.verify_init()
+        k.verify_update(data)
+        return M2Crypto.m2.verify_final(k.ctx, base64.b64decode(sig), k.pkey)
+
+    def compute_hash(self, value):
+        return self.sign_string(str(value))
 
 ##
 # The certificate class implements a general purpose X509 certificate, making
@@ -216,301 +218,337 @@ class Keypair:
 # whether to save the parent certificates as well.
 
 class Certificate:
-   digest = "md5"
-
-   data = None
-   cert = None
-   issuerKey = None
-   issuerSubject = None
-   parent = None
-
-   separator="-----parent-----"
-
-   ##
-   # Create a certificate object.
-   #
-   # @param create If create==True, then also create a blank X509 certificate.
-   # @param subject If subject!=None, then create a blank certificate and set
-   #     it's subject name.
-   # @param string If string!=None, load the certficate from the string.
-   # @param filename If filename!=None, load the certficiate from the file.
-
-   def __init__(self, create=False, subject=None, string=None, filename=None):
-       if create or subject:
-           self.create()
-       if subject:
-           self.set_subject(subject)
-       if string:
-           self.load_from_string(string)
-       if filename:
-           self.load_from_file(filename)
-
-   ##
-   # Create a blank X509 certificate and store it in this object.
-
-   def create(self):
-       self.cert = crypto.X509()
-       self.cert.set_serial_number(1)
-       self.cert.gmtime_adj_notBefore(0)
-       self.cert.gmtime_adj_notAfter(60*60*24*365*5) # five years
-
-   ##
-   # Given a pyOpenSSL X509 object, store that object inside of this
-   # certificate object.
-
-   def load_from_pyopenssl_x509(self, x509):
-       self.cert = x509
-
-   ##
-   # Load the certificate from a string
-
-   def load_from_string(self, string):
-       # if it is a chain of multiple certs, then split off the first one and
-       # load it
-       parts = string.split(Certificate.separator, 1)
-       self.cert = crypto.load_certificate(crypto.FILETYPE_PEM, parts[0])
-
-       # if there are more certs, then create a parent and let the parent load
-       # itself from the remainder of the string
-       if len(parts) > 1:
-           self.parent = self.__class__()
-           self.parent.load_from_string(parts[1])
-
-   ##
-   # Load the certificate from a file
-
-   def load_from_file(self, filename):
-       file = open(filename)
-       string = file.read()
-       self.load_from_string(string)
-
-   ##
-   # Save the certificate to a string.
-   #
-   # @param save_parents If save_parents==True, then also save the parent certificates.
-
-   def save_to_string(self, save_parents=False):
-       string = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
-       if save_parents and self.parent:
-          string = string + Certificate.separator + self.parent.save_to_string(save_parents)
-       return string
-
-   ##
-   # Save the certificate to a file.
-   # @param save_parents If save_parents==True, then also save the parent certificates.
-
-   def save_to_file(self, filename, save_parents=False):
-       string = self.save_to_string(save_parents=save_parents)
-       open(filename, 'w').write(string)
-
-   ##
-   # Sets the issuer private key and name
-   # @param key Keypair object containing the private key of the issuer
-   # @param subject String containing the name of the issuer
-   # @param cert (optional) Certificate object containing the name of the issuer
-
-   def set_issuer(self, key, subject=None, cert=None):
-       self.issuerKey = key
-       if subject:
-          # it's a mistake to use subject and cert params at the same time
-          assert(not cert)
-          if isinstance(subject, dict) or isinstance(subject, str):
-             req = crypto.X509Req()
-             reqSubject = req.get_subject()
-             if (isinstance(subject, dict)):
-                for key in reqSubject.keys():
-                    setattr(reqSubject, key, name[key])
-             else:
-                setattr(reqSubject, "CN", subject)
-             subject = reqSubject
-             # subject is not valid once req is out of scope, so save req
-             self.issuerReq = req
-       if cert:
-          # if a cert was supplied, then get the subject from the cert
-          subject = cert.cert.get_subject()
-       assert(subject)
-       self.issuerSubject = subject
-
-   ##
-   # Get the issuer name
-
-   def get_issuer(self, which="CN"):
-       x = self.cert.get_issuer()
-       return getattr(x, which)
-
-   ##
-   # Set the subject name of the certificate
-
-   def set_subject(self, name):
-       req = crypto.X509Req()
-       subj = req.get_subject()
-       if (isinstance(name, dict)):
-           for key in name.keys():
-               setattr(subj, key, name[key])
-       else:
-           setattr(subj, "CN", name)
-       self.cert.set_subject(subj)
-   ##
-   # Get the subject name of the certificate
-
-   def get_subject(self, which="CN"):
-       x = self.cert.get_subject()
-       return getattr(x, which)
-
-   ##
-   # Get the public key of the certificate.
-   #
-   # @param key Keypair object containing the public key
-
-   def set_pubkey(self, key):
-       assert(isinstance(key, Keypair))
-       self.cert.set_pubkey(key.get_openssl_pkey())
-
-   ##
-   # Get the public key of the certificate.
-   # It is returned in the form of a Keypair object.
-
-   def get_pubkey(self):
-       m2x509 = X509.load_cert_string(self.save_to_string())
-       pkey = Keypair()
-       pkey.key = self.cert.get_pubkey()
-       pkey.m2key = m2x509.get_pubkey()
-       return pkey
-
-   ##
-   # Add an X509 extension to the certificate. Add_extension can only be called
-   # once for a particular extension name, due to limitations in the underlying
-   # library.
-   #
-   # @param name string containing name of extension
-   # @param value string containing value of the extension
-
-   def add_extension(self, name, critical, value):
-       ext = crypto.X509Extension (name, critical, value)
-       self.cert.add_extensions([ext])
-
-   ##
-   # Get an X509 extension from the certificate
-
-   def get_extension(self, name):
-       # pyOpenSSL does not have a way to get extensions
-       m2x509 = X509.load_cert_string(self.save_to_string())
-       value = m2x509.get_ext(name).get_value()
-       return value
-
-   ##
-   # Set_data is a wrapper around add_extension. It stores the parameter str in
-   # the X509 subject_alt_name extension. Set_data can only be called once, due
-   # to limitations in the underlying library.
-
-   def set_data(self, str):
-       # pyOpenSSL only allows us to add extensions, so if we try to set the
-       # same extension more than once, it will not work
-       if self.data != None:
-          raise "cannot set subjectAltName more than once"
-       self.data = str
-       self.add_extension("subjectAltName", 0, "URI:http://" + str)
-
-   ##
-   # Return the data string that was previously set with set_data
-
-   def get_data(self):
-       if self.data:
-           return self.data
-
-       try:
-           uri = self.get_extension("subjectAltName")
-       except LookupError:
-           self.data = None
-           return self.data
-
-       if not uri.startswith("URI:http://"):
-           raise "bad encoding in subjectAltName"
-       self.data = uri[11:]
-       return self.data
-
-   ##
-   # Sign the certificate using the issuer private key and issuer subject previous set with set_issuer().
-
-   def sign(self):
-       assert self.cert != None
-       assert self.issuerSubject != None
-       assert self.issuerKey != None
-       self.cert.set_issuer(self.issuerSubject)
-       self.cert.sign(self.issuerKey.get_openssl_pkey(), self.digest)
+    digest = "md5"
+
+    cert = None
+    issuerKey = None
+    issuerSubject = None
+    parent = None
+
+    separator="-----parent-----"
+
+    ##
+    # Create a certificate object.
+    #
+    # @param create If create==True, then also create a blank X509 certificate.
+    # @param subject If subject!=None, then create a blank certificate and set
+    #     it's subject name.
+    # @param string If string!=None, load the certficate from the string.
+    # @param filename If filename!=None, load the certficiate from the file.
+
+    def __init__(self, create=False, subject=None, string=None, filename=None, intermediate=None):
+        self.data = {}
+        if create or subject:
+            self.create()
+        if subject:
+            self.set_subject(subject)
+        if string:
+            self.load_from_string(string)
+        if filename:
+            self.load_from_file(filename)
+
+        if intermediate:
+            self.set_intermediate_ca(intermediate)
+
+    ##
+    # Create a blank X509 certificate and store it in this object.
+
+    def create(self):
+        self.cert = crypto.X509()
+        self.cert.set_serial_number(3)
+        self.cert.gmtime_adj_notBefore(0)
+        self.cert.gmtime_adj_notAfter(60*60*24*365*5) # five years
+
+    ##
+    # Given a pyOpenSSL X509 object, store that object inside of this
+    # certificate object.
+
+    def load_from_pyopenssl_x509(self, x509):
+        self.cert = x509
+
+    ##
+    # Load the certificate from a string
+
+    def load_from_string(self, string):
+        # if it is a chain of multiple certs, then split off the first one and
+        # load it (support for the ---parent--- tag as well as normal chained certs)
+
+        string = string.strip()
+
+
+        if not string.startswith('-----'):
+            string = '-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----' % string
+
+        parts = []
+
+        if string.count('-----BEGIN CERTIFICATE-----') > 1 and \
+               string.count(Certificate.separator) == 0:
+            parts = string.split('-----END CERTIFICATE-----',1)
+            parts[0] += '-----END CERTIFICATE-----'
+        else:
+            parts = string.split(Certificate.separator, 1)
+
+        self.cert = crypto.load_certificate(crypto.FILETYPE_PEM, parts[0])
+
+        # if there are more certs, then create a parent and let the parent load
+        # itself from the remainder of the string
+        if len(parts) > 1 and parts[1] != '':
+            self.parent = self.__class__()
+            self.parent.load_from_string(parts[1])
+
+    ##
+    # Load the certificate from a file
+
+    def load_from_file(self, filename):
+        file = open(filename)
+        string = file.read()
+        self.load_from_string(string)
+
+    ##
+    # Save the certificate to a string.
+    #
+    # @param save_parents If save_parents==True, then also save the parent certificates.
+
+    def save_to_string(self, save_parents=True):
+        string = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
+        if save_parents and self.parent:
+            string = string + self.parent.save_to_string(save_parents)
+        return string
+
+    ##
+    # Save the certificate to a file.
+    # @param save_parents If save_parents==True, then also save the parent certificates.
+
+    def save_to_file(self, filename, save_parents=True, filep=None):
+        string = self.save_to_string(save_parents=save_parents)
+        if filep:
+            f = filep
+        else:
+            f = open(filename, 'w')
+        f.write(string)
+        f.close()
+
+    ##
+    # Save the certificate to a random file in /tmp/
+    # @param save_parents If save_parents==True, then also save the parent certificates.
+    def save_to_random_tmp_file(self, save_parents=True):
+        fp, filename = mkstemp(suffix='cert', text=True)
+        fp = os.fdopen(fp, "w")
+        self.save_to_file(filename, save_parents=True, filep=fp)
+        return filename
+
+    ##
+    # Sets the issuer private key and name
+    # @param key Keypair object containing the private key of the issuer
+    # @param subject String containing the name of the issuer
+    # @param cert (optional) Certificate object containing the name of the issuer
+
+    def set_issuer(self, key, subject=None, cert=None):
+        self.issuerKey = key
+        if subject:
+            # it's a mistake to use subject and cert params at the same time
+            assert(not cert)
+            if isinstance(subject, dict) or isinstance(subject, str):
+                req = crypto.X509Req()
+                reqSubject = req.get_subject()
+                if (isinstance(subject, dict)):
+                    for key in reqSubject.keys():
+                        setattr(reqSubject, key, subject[key])
+                else:
+                    setattr(reqSubject, "CN", subject)
+                subject = reqSubject
+                # subject is not valid once req is out of scope, so save req
+                self.issuerReq = req
+        if cert:
+            # if a cert was supplied, then get the subject from the cert
+            subject = cert.cert.get_subject()
+        assert(subject)
+        self.issuerSubject = subject
+
+    ##
+    # Get the issuer name
+
+    def get_issuer(self, which="CN"):
+        x = self.cert.get_issuer()
+        return getattr(x, which)
+
+    ##
+    # Set the subject name of the certificate
+
+    def set_subject(self, name):
+        req = crypto.X509Req()
+        subj = req.get_subject()
+        if (isinstance(name, dict)):
+            for key in name.keys():
+                setattr(subj, key, name[key])
+        else:
+            setattr(subj, "CN", name)
+        self.cert.set_subject(subj)
+    ##
+    # Get the subject name of the certificate
+
+    def get_subject(self, which="CN"):
+        x = self.cert.get_subject()
+        return getattr(x, which)
+
+    ##
+    # Get the public key of the certificate.
+    #
+    # @param key Keypair object containing the public key
+
+    def set_pubkey(self, key):
+        assert(isinstance(key, Keypair))
+        self.cert.set_pubkey(key.get_openssl_pkey())
+
+    ##
+    # Get the public key of the certificate.
+    # It is returned in the form of a Keypair object.
+
+    def get_pubkey(self):
+        m2x509 = X509.load_cert_string(self.save_to_string())
+        pkey = Keypair()
+        pkey.key = self.cert.get_pubkey()
+        pkey.m2key = m2x509.get_pubkey()
+        return pkey
+
+    def set_intermediate_ca(self, val):
+        self.intermediate = val
+        if val:
+            self.add_extension('basicConstraints', 1, 'CA:TRUE')
+
+
+
+    ##
+    # Add an X509 extension to the certificate. Add_extension can only be called
+    # once for a particular extension name, due to limitations in the underlying
+    # library.
+    #
+    # @param name string containing name of extension
+    # @param value string containing value of the extension
+
+    def add_extension(self, name, critical, value):
+        ext = crypto.X509Extension (name, critical, value)
+        self.cert.add_extensions([ext])
+
+    ##
+    # Get an X509 extension from the certificate
+
+    def get_extension(self, name):
+        # pyOpenSSL does not have a way to get extensions
+        m2x509 = X509.load_cert_string(self.save_to_string())
+        value = m2x509.get_ext(name).get_value()
+        return value
+
+    ##
+    # Set_data is a wrapper around add_extension. It stores the parameter str in
+    # the X509 subject_alt_name extension. Set_data can only be called once, due
+    # to limitations in the underlying library.
+
+    def set_data(self, str, field='subjectAltName'):
+        # pyOpenSSL only allows us to add extensions, so if we try to set the
+        # same extension more than once, it will not work
+        if self.data.has_key(field):
+            raise "cannot set ", field, " more than once"
+        self.data[field] = str
+        self.add_extension(field, 0, str)
+
+    ##
+    # Return the data string that was previously set with set_data
+
+    def get_data(self, field='subjectAltName'):
+        if self.data.has_key(field):
+            return self.data[field]
+
+        try:
+            uri = self.get_extension(field)
+            self.data[field] = uri
+        except LookupError:
+            return None
+
+        return self.data[field]
+
+    ##
+    # Sign the certificate using the issuer private key and issuer subject previous set with set_issuer().
+
+    def sign(self):
+        assert self.cert != None
+        assert self.issuerSubject != None
+        assert self.issuerKey != None
+        self.cert.set_issuer(self.issuerSubject)
+        self.cert.sign(self.issuerKey.get_openssl_pkey(), self.digest)
 
     ##
     # Verify the authenticity of a certificate.
     # @param pkey is a Keypair object representing a public key. If Pkey
     #     did not sign the certificate, then an exception will be thrown.
 
-   def verify(self, pkey):
-       # pyOpenSSL does not have a way to verify signatures
-       m2x509 = X509.load_cert_string(self.save_to_string())
-       m2pkey = pkey.get_m2_pkey()
-       # verify it
-       return m2x509.verify(m2pkey)
-
-       # XXX alternatively, if openssl has been patched, do the much simpler:
-       # try:
-       #   self.cert.verify(pkey.get_openssl_key())
-       #   return 1
-       # except:
-       #   return 0
-
-   ##
-   # Return True if pkey is identical to the public key that is contained in the certificate.
-   # @param pkey Keypair object
-
-   def is_pubkey(self, pkey):
-       return self.get_pubkey().is_same(pkey)
-
-   ##
-   # Given a certificate cert, verify that this certificate was signed by the
-   # public key contained in cert. Throw an exception otherwise.
-   #
-   # @param cert certificate object
-
-   def is_signed_by_cert(self, cert):
-       k = cert.get_pubkey()
-       result = self.verify(k)
-       return result
-
-   ##
-   # Set the parent certficiate.
-   #
-   # @param p certificate object.
-
-   def set_parent(self, p):
+    def verify(self, pkey):
+        # pyOpenSSL does not have a way to verify signatures
+        m2x509 = X509.load_cert_string(self.save_to_string())
+        m2pkey = pkey.get_m2_pkey()
+        # verify it
+        return m2x509.verify(m2pkey)
+
+        # XXX alternatively, if openssl has been patched, do the much simpler:
+        # try:
+        #   self.cert.verify(pkey.get_openssl_key())
+        #   return 1
+        # except:
+        #   return 0
+
+    ##
+    # Return True if pkey is identical to the public key that is contained in the certificate.
+    # @param pkey Keypair object
+
+    def is_pubkey(self, pkey):
+        return self.get_pubkey().is_same(pkey)
+
+    ##
+    # Given a certificate cert, verify that this certificate was signed by the
+    # public key contained in cert. Throw an exception otherwise.
+    #
+    # @param cert certificate object
+
+    def is_signed_by_cert(self, cert):
+        k = cert.get_pubkey()
+        result = self.verify(k)
+        return result
+
+    ##
+    # Set the parent certficiate.
+    #
+    # @param p certificate object.
+
+    def set_parent(self, p):
         self.parent = p
 
-   ##
-   # Return the certificate object of the parent of this certificate.
+    ##
+    # Return the certificate object of the parent of this certificate.
 
-   def get_parent(self):
+    def get_parent(self):
         return self.parent
 
-   ##
-   # Verification examines a chain of certificates to ensure that each parent
-   # signs the child, and that some certificate in the chain is signed by a
-   # trusted certificate.
-   #
-   # Verification is a basic recursion: <pre>
-   #     if this_certificate was signed by trusted_certs:
-   #         return
-   #     else
-   #         return verify_chain(parent, trusted_certs)
-   # </pre>
-   #
-   # At each recursion, the parent is tested to ensure that it did sign the
-   # child. If a parent did not sign a child, then an exception is thrown. If
-   # the bottom of the recursion is reached and the certificate does not match
-   # a trusted root, then an exception is thrown.
-   #
-   # @param Trusted_certs is a list of certificates that are trusted.
-   #
-
-   def verify_chain(self, trusted_certs = None):
+    ##
+    # Verification examines a chain of certificates to ensure that each parent
+    # signs the child, and that some certificate in the chain is signed by a
+    # trusted certificate.
+    #
+    # Verification is a basic recursion: <pre>
+    #     if this_certificate was signed by trusted_certs:
+    #         return
+    #     else
+    #         return verify_chain(parent, trusted_certs)
+    # </pre>
+    #
+    # At each recursion, the parent is tested to ensure that it did sign the
+    # child. If a parent did not sign a child, then an exception is thrown. If
+    # the bottom of the recursion is reached and the certificate does not match
+    # a trusted root, then an exception is thrown.
+    #
+    # @param Trusted_certs is a list of certificates that are trusted.
+    #
+
+    def verify_chain(self, trusted_certs = None):
         # Verify a chain of certificates. Each certificate must be signed by
         # the public key contained in it's parent. The chain is recursed
         # until a certificate is found that is signed by a trusted root.
@@ -525,12 +563,7 @@ class Certificate:
             #print "TRUSTED CERT", trusted_cert.dump()
             #print "Client is signed by Trusted?", self.is_signed_by_cert(trusted_cert)
             if self.is_signed_by_cert(trusted_cert):
-                # make sure sure the trusted cert's hrn is a prefix of the
-                # signed cert's hrn
-                if not self.get_subject().startswith(trusted_cert.get_subject()):
-                    raise GidParentHrn(trusted_cert.get_subject()) 
-                #print self.get_subject(), "is signed by a root"
-                return
+                return trusted_cert
 
         # if there is no parent, then no way to verify the chain
         if not self.parent:
index d8451d7..f3fb180 100644 (file)
 ##
 # Implements SFA Credentials
 #
-# Credentials are layered on top of certificates, and are essentially a
-# certificate that stores a tuple of parameters.
+# Credentials are signed XML files that assign a subject gid privileges to an object gid
 ##
 
 ### $Id$
 ### $URL$
 
-import xmlrpclib
+import os
+import datetime
+from xml.dom.minidom import Document, parseString
+from tempfile import mkstemp
 
-from sfa.trust.certificate import Certificate
+from sfa.trust.credential_legacy import CredentialLegacy
 from sfa.trust.rights import *
 from sfa.trust.gid import *
 from sfa.util.faults import *
 
+from sfa.util.sfalogging import logger
+from dateutil.parser import parse
+
+
+
+# Two years, in seconds 
+DEFAULT_CREDENTIAL_LIFETIME = 60 * 60 * 24 * 365 * 2
+
+
+# TODO:
+# . make privs match between PG and PL
+# . Need to add support for other types of credentials, e.g. tickets
+
+
+
+signature_template = \
+'''
+<Signature xml:id="Sig_%s" xmlns="http://www.w3.org/2000/09/xmldsig#">
+    <SignedInfo>
+      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
+      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+      <Reference URI="#%s">
+      <Transforms>
+        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
+      </Transforms>
+      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+      <DigestValue></DigestValue>
+      </Reference>
+    </SignedInfo>
+    <SignatureValue />
+      <KeyInfo>
+        <X509Data>
+          <X509SubjectName/>
+          <X509IssuerSerial/>
+          <X509Certificate/>
+        </X509Data>
+      <KeyValue />
+      </KeyInfo>
+    </Signature>
+'''
+
+##
+# Convert a string into a bool
+
+def str2bool(str):
+    if str.lower() in ['yes','true','1']:
+        return True
+    return False
+
+
+
+##
+# Utility function to get the text of an XML element
+
+def getTextNode(element, subele):
+    sub = element.getElementsByTagName(subele)[0]
+    if len(sub.childNodes) > 0:            
+        return sub.childNodes[0].nodeValue
+    else:
+        return None
+        
+##
+# Utility function to set the text of an XML element
+# It creates the element, adds the text to it,
+# and then appends it to the parent.
+
+def append_sub(doc, parent, element, text):
+    ele = doc.createElement(element)
+    ele.appendChild(doc.createTextNode(text))
+    parent.appendChild(ele)
+
+##
+# Signature contains information about an xmlsec1 signature
+# for a signed-credential
+#
+
+class Signature(object):
+
+    
+    def __init__(self, string=None):
+        self.refid = None
+        self.issuer_gid = None
+        self.xml = None
+        if string:
+            self.xml = string
+            self.decode()
+
+
+
+    def get_refid(self):
+        if not self.refid:
+            self.decode()
+        return self.refid
+
+    def get_xml(self):
+        if not self.xml:
+            self.encode()
+        return self.xml
+
+    def set_refid(self, id):
+        self.refid = id
+
+    def get_issuer_gid(self):
+        if not self.gid:
+            self.decode()
+        return self.gid        
+
+    def set_issuer_gid(self, gid):
+        self.gid = gid
+
+    def decode(self):
+        doc = parseString(self.xml)
+        sig = doc.getElementsByTagName("Signature")[0]
+        self.set_refid(sig.getAttribute("xml:id").strip("Sig_"))
+        keyinfo = sig.getElementsByTagName("X509Data")[0]
+        szgid = getTextNode(keyinfo, "X509Certificate")
+        szgid = "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----" % szgid
+        self.set_issuer_gid(GID(string=szgid))        
+        
+    def encode(self):
+        self.xml = signature_template % (self.get_refid(), self.get_refid())
+
+
 ##
-# Credential is a tuple:
-#     (GIDCaller, GIDObject, LifeTime, Privileges, Delegate)
+# A credential provides a caller gid with privileges to an object gid.
+# A signed credential is signed by the object's authority.
+#
+# Credentials are encoded in one of two ways.  The legacy style places
+# it in the subjectAltName of an X509 certificate.  The new credentials
+# are placed in signed XML.
 #
-# These fields are encoded using xmlrpc into the subjectAltName field of the
-# x509 certificate. Note: Call encode() once the fields have been filled in
-# to perform this encoding.
+# WARNING:
+# In general, a signed credential obtained externally should
+# not be changed else the signature is no longer valid.  So, once
+# you have loaded an existing signed credential, do not call encode() or sign() on it.
+
+
+class Credential(object):
 
-class Credential(Certificate):
-    gidCaller = None
-    gidObject = None
-    lifeTime = None
-    privileges = None
-    delegate = False
 
     ##
     # Create a Credential object
@@ -39,7 +166,88 @@ class Credential(Certificate):
     # @param filename If filename!=None, load the credential from the file
 
     def __init__(self, create=False, subject=None, string=None, filename=None):
-        Certificate.__init__(self, create, subject, string, filename)
+        self.gidCaller = None
+        self.gidObject = None
+        self.expiration = None
+        self.privileges = None
+        self.issuer_privkey = None
+        self.issuer_gid = None
+        self.issuer_pubkey = None
+        self.parent = None
+        self.signature = None
+        self.xml = None
+        self.refid = None
+        self.legacy = None
+
+
+
+
+        # Check if this is a legacy credential, translate it if so
+        if string or filename:
+            if string:                
+                str = string
+            elif filename:
+                str = file(filename).read()
+                
+            if str.strip().startswith("-----"):
+                self.legacy = CredentialLegacy(False,string=str)
+                self.translate_legacy(str)
+            else:
+                self.xml = str
+                self.decode()
+
+        # Find an xmlsec1 path
+        self.xmlsec_path = ''
+        paths = ['/usr/bin','/usr/local/bin','/bin','/opt/bin','/opt/local/bin']
+        for path in paths:
+            if os.path.isfile(path + '/' + 'xmlsec1'):
+                self.xmlsec_path = path + '/' + 'xmlsec1'
+                break
+
+
+    def get_signature(self):
+        if not self.signature:
+            self.decode()
+        return self.signature
+
+    def set_signature(self, sig):
+        self.signature = sig
+
+        
+    ##
+    # Translate a legacy credential into a new one
+    #
+    # @param String of the legacy credential
+
+    def translate_legacy(self, str):
+        legacy = CredentialLegacy(False,string=str)
+        self.gidCaller = legacy.get_gid_caller()
+        self.gidObject = legacy.get_gid_object()
+        lifetime = legacy.get_lifetime()
+        if not lifetime:
+            # Default to two years
+            self.set_lifetime(DEFAULT_CREDENTIAL_LIFETIME)
+        else:
+            self.set_lifetime(int(lifetime))
+        self.lifeTime = legacy.get_lifetime()
+        self.set_privileges(legacy.get_privileges())
+        self.get_privileges().delegate_all_privileges(legacy.get_delegate())
+
+    ##
+    # Need the issuer's private key and name
+    # @param key Keypair object containing the private key of the issuer
+    # @param gid GID of the issuing authority
+
+    def set_issuer_keys(self, privkey, gid):
+        self.issuer_privkey = privkey
+        self.issuer_gid = gid
+
+
+    ##
+    # Set this credential's parent
+    def set_parent(self, cred):
+        self.parent = cred
+        self.updateRefID()
 
     ##
     # set the GID of the caller
@@ -79,34 +287,25 @@ class Credential(Certificate):
     # set the lifetime of this credential
     #
     # @param lifetime lifetime of credential
+    # . if lifeTime is a datetime object, it is used for the expiration time
+    # . if lifeTime is an integer value, it is considered the number of seconds
+    #   remaining before expiration
 
     def set_lifetime(self, lifeTime):
-        self.lifeTime = lifeTime
+        if isinstance(lifeTime, int):
+            self.expiration = datetime.timedelta(seconds=lifeTime) + datetime.datetime.utcnow()
+        else:
+            self.expiration = lifeTime
 
     ##
-    # get the lifetime of the credential
+    # get the lifetime of the credential (in datetime format)
 
     def get_lifetime(self):
-        if not self.lifeTime:
-            self.decode()
-        return self.lifeTime
-
-    ##
-    # set the delegate bit
-    #
-    # @param delegate boolean (True or False)
-
-    def set_delegate(self, delegate):
-        self.delegate = delegate
-
-    ##
-    # get the delegate bit
-
-    def get_delegate(self):
-        if not self.delegate:
+        if not self.expiration:
             self.decode()
-        return self.delegate
+        return self.expiration
 
     ##
     # set the privileges
     #
@@ -117,6 +316,7 @@ class Credential(Certificate):
             self.privileges = RightList(string = privs)
         else:
             self.privileges = privs
+        
 
     ##
     # return the privileges as a RightList object
@@ -134,88 +334,405 @@ class Credential(Certificate):
 
     def can_perform(self, op_name):
         rights = self.get_privileges()
+        
         if not rights:
             return False
+
         return rights.can_perform(op_name)
 
+
     ##
-    # Encode the attributes of the credential into a string and store that
-    # string in the alt-subject-name field of the X509 object. This should be
-    # done immediately before signing the credential.
+    # Encode the attributes of the credential into an XML string    
+    # This should be done immediately before signing the credential.    
+    # WARNING:
+    # In general, a signed credential obtained externally should
+    # not be changed else the signature is no longer valid.  So, once
+    # you have loaded an existing signed credential, do not call encode() or sign() on it.
 
     def encode(self):
-        dict = {"gidCaller": None,
-                "gidObject": None,
-                "lifeTime": self.lifeTime,
-                "privileges": None,
-                "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)
+        # Create the XML document
+        doc = Document()
+        signed_cred = doc.createElement("signed-credential")
+        doc.appendChild(signed_cred)  
+        
+        # Fill in the <credential> bit        
+        cred = doc.createElement("credential")
+        cred.setAttribute("xml:id", self.get_refid())
+        signed_cred.appendChild(cred)
+        append_sub(doc, cred, "type", "privilege")
+        append_sub(doc, cred, "serial", "8")
+        append_sub(doc, cred, "owner_gid", self.gidCaller.save_to_string())
+        append_sub(doc, cred, "owner_urn", self.gidCaller.get_urn())
+        append_sub(doc, cred, "target_gid", self.gidObject.save_to_string())
+        append_sub(doc, cred, "target_urn", self.gidObject.get_urn())
+        append_sub(doc, cred, "uuid", "")
+        if  not self.expiration:
+            self.set_lifetime(DEFAULT_CREDENTIAL_LIFETIME)
+        self.expiration = self.expiration.replace(microsecond=0)
+        append_sub(doc, cred, "expires", self.expiration.isoformat())
+        privileges = doc.createElement("privileges")
+        cred.appendChild(privileges)
+
         if self.privileges:
-            dict["privileges"] = self.privileges.save_to_string()
-        str = xmlrpclib.dumps((dict,), allow_none=True)
-        self.set_data(str)
+            rights = self.get_privileges()
+            for right in rights.rights:
+                priv = doc.createElement("privilege")
+                append_sub(doc, priv, "name", right.kind)
+                append_sub(doc, priv, "can_delegate", str(right.delegate).lower())
+                privileges.appendChild(priv)
+
+        # Add the parent credential if it exists
+        if self.parent:
+            sdoc = parseString(self.parent.get_xml())
+            p_cred = doc.importNode(sdoc.getElementsByTagName("credential")[0], True)
+            p = doc.createElement("parent")
+            p.appendChild(p_cred)
+            cred.appendChild(p)
 
-    ##
-    # Retrieve the attributes of the credential from the alt-subject-name field
-    # of the X509 certificate. This is automatically done by the various
-    # get_* methods of this class and should not need to be called explicitly.
 
-    def decode(self):
-        data = self.get_data()
-        if data:
-            dict = xmlrpclib.loads(self.get_data())[0][0]
+        # Create the <signatures> tag
+        signatures = doc.createElement("signatures")
+        signed_cred.appendChild(signatures)
+
+        # Add any parent signatures
+        if self.parent:
+            for cur_cred in self.get_credential_list()[1:]:
+                sdoc = parseString(cur_cred.get_signature().get_xml())
+                ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
+                signatures.appendChild(ele)
+                
+        # Get the finished product
+        self.xml = doc.toxml()
+
+
+    def save_to_random_tmp_file(self):       
+        fp, filename = mkstemp(suffix='cred', text=True)
+        fp = os.fdopen(fp, "w")
+        self.save_to_file(filename, save_parents=True, filep=fp)
+        return filename
+    
+    def save_to_file(self, filename, save_parents=True, filep=None):
+        if not self.xml:
+            self.encode()
+        if filep:
+            f = filep 
         else:
-            dict = {}
+            f = open(filename, "w")
+        f.write(self.xml)
+        f.close()
 
-        self.lifeTime = dict.get("lifeTime", None)
-        self.delegate = dict.get("delegate", None)
+    def save_to_string(self, save_parents=True):
+        if not self.xml:
+            self.encode()
+        return self.xml
 
-        privStr = dict.get("privileges", None)
-        if privStr:
-            self.privileges = RightList(string = privStr)
-        else:
-            self.privileges = None
+    def get_refid(self):
+        if not self.refid:
+            self.refid = 'ref0'
+        return self.refid
 
-        gidCallerStr = dict.get("gidCaller", None)
-        if gidCallerStr:
-            self.gidCaller = GID(string=gidCallerStr)
-        else:
-            self.gidCaller = None
+    def set_refid(self, rid):
+        self.refid = rid
 
-        gidObjectStr = dict.get("gidObject", None)
-        if gidObjectStr:
-            self.gidObject = GID(string=gidObjectStr)
-        else:
-            self.gidObject = None
+    ##
+    # Figure out what refids exist, and update this credential's id
+    # so that it doesn't clobber the others.  Returns the refids of
+    # the parents.
+    
+    def updateRefID(self):
+        if not self.parent:
+            self.set_refid('ref0')
+            return []
+        
+        refs = []
+
+        next_cred = self.parent
+        while next_cred:
+            refs.append(next_cred.get_refid())
+            if next_cred.parent:
+                next_cred = next_cred.parent
+            else:
+                next_cred = None
+
+        
+        # Find a unique refid for this credential
+        rid = self.get_refid()
+        while rid in refs:
+            val = int(rid[3:])
+            rid = "ref%d" % (val + 1)
+
+        # Set the new refid
+        self.set_refid(rid)
+
+        # Return the set of parent credential ref ids
+        return refs
+
+    def get_xml(self):
+        if not self.xml:
+            self.encode()
+        return self.xml
 
     ##
-    # Verify that a chain of credentials is valid (see cert.py:verify). In
-    # addition to the checks for ordinary certificates, verification also
-    # ensures that the delegate bit was set by each parent in the chain. If
-    # a delegate bit was not set, then an exception is thrown.
+    # Sign the XML file created by encode()
     #
-    # Each credential must be a subset of the rights of the parent.
+    # WARNING:
+    # In general, a signed credential obtained externally should
+    # not be changed else the signature is no longer valid.  So, once
+    # you have loaded an existing signed credential, do not call encode() or sign() on it.
+
+    def sign(self):
+        if not self.issuer_privkey or not self.issuer_gid:
+            return
+        doc = parseString(self.get_xml())
+        sigs = doc.getElementsByTagName("signatures")[0]
+
+        # Create the signature template to be signed
+        signature = Signature()
+        signature.set_refid(self.get_refid())
+        sdoc = parseString(signature.get_xml())        
+        sig_ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
+        sigs.appendChild(sig_ele)
+
+        self.xml = doc.toxml()
+
+
+        # Split the issuer GID into multiple certificates if it's a chain
+        chain = GID(filename=self.issuer_gid)
+        gid_files = []
+        while chain:
+            gid_files.append(chain.save_to_random_tmp_file(False))
+            if chain.get_parent():
+                chain = chain.get_parent()
+            else:
+                chain = None
+
+
+        # Call out to xmlsec1 to sign it
+        ref = 'Sig_%s' % self.get_refid()
+        filename = self.save_to_random_tmp_file()
+        signed = os.popen('%s --sign --node-id "%s" --privkey-pem %s,%s %s' \
+                 % (self.xmlsec_path, ref, self.issuer_privkey, ",".join(gid_files), filename)).read()
+        os.remove(filename)
+
+        for gid_file in gid_files:
+            os.remove(gid_file)
+
+        self.xml = signed
+
+        # This is no longer a legacy credential
+        if self.legacy:
+            self.legacy = None
+
+        # Update signatures
+        self.decode()
 
-    def verify_chain(self, trusted_certs = None):
-        # do the normal certificate verification stuff
-        Certificate.verify_chain(self, trusted_certs)
+        
 
+        
+    ##
+    # Retrieve the attributes of the credential from the XML.
+    # This is automatically called by the various get_* methods of
+    # this class and should not need to be called explicitly.
+
+    def decode(self):
+        if not self.xml:
+            return
+        doc = parseString(self.xml)
+        sigs = []
+        signed_cred = doc.getElementsByTagName("signed-credential")
+
+        # Is this a signed-cred or just a cred?
+        if len(signed_cred) > 0:
+            cred = signed_cred[0].getElementsByTagName("credential")[0]
+            signatures = signed_cred[0].getElementsByTagName("signatures")
+            if len(signatures) > 0:
+                sigs = signatures[0].getElementsByTagName("Signature")
+        else:
+            cred = doc.getElementsByTagName("credential")[0]
+        
+
+
+        self.set_refid(cred.getAttribute("xml:id"))
+        self.set_lifetime(parse(getTextNode(cred, "expires")))
+        self.gidCaller = GID(string=getTextNode(cred, "owner_gid"))
+        self.gidObject = GID(string=getTextNode(cred, "target_gid"))   
+
+
+        # Process privileges
+        privs = cred.getElementsByTagName("privileges")[0]
+        rlist = RightList()
+        for priv in privs.getElementsByTagName("privilege"):
+            kind = getTextNode(priv, "name")
+            deleg = str2bool(getTextNode(priv, "can_delegate"))
+            if kind == '*':
+                # Convert * into the default privileges for the credential's type                
+                _ , type = urn_to_hrn(self.gidObject.get_urn())
+                rl = rlist.determine_rights(type, self.gidObject.get_urn())
+                for r in rl.rights:
+                    rlist.add(r)
+            else:
+                rlist.add(Right(kind.strip(), deleg))
+        self.set_privileges(rlist)
+
+
+        # Is there a parent?
+        parent = cred.getElementsByTagName("parent")
+        if len(parent) > 0:
+            parent_doc = parent[0].getElementsByTagName("credential")[0]
+            parent_xml = parent_doc.toxml()
+            self.parent = Credential(string=parent_xml)
+            self.updateRefID()
+
+        # Assign the signatures to the credentials
+        for sig in sigs:
+            Sig = Signature(string=sig.toxml())
+
+            for cur_cred in self.get_credential_list():
+                if cur_cred.get_refid() == Sig.get_refid():
+                    cur_cred.set_signature(Sig)
+                                    
+            
+    ##
+    # Verify
+    #   trusted_certs: A list of trusted GID filenames (not GID objects!) 
+    #                  Chaining is not supported within the GIDs by xmlsec1.
+    #    
+    # Verify that:
+    # . All of the signatures are valid and that the issuers trace back
+    #   to trusted roots (performed by xmlsec1)
+    # . The XML matches the credential schema
+    # . That the issuer of the credential is the authority in the target's urn
+    #    . In the case of a delegated credential, this must be true of the root
+    # . That all of the gids presented in the credential are valid
+    # . The credential is not expired
+    #
+    # -- For Delegates (credentials with parents)
+    # . The privileges must be a subset of the parent credentials
+    # . The privileges must have "can_delegate" set for each delegated privilege
+    # . The target gid must be the same between child and parents
+    # . The expiry time on the child must be no later than the parent
+    # . The signer of the child must be the owner of the parent
+    #
+    # -- Verify does *NOT*
+    # . ensure that an xmlrpc client's gid matches a credential gid, that
+    #   must be done elsewhere
+    #
+    # @param trusted_certs: The certificates of trusted CA certificates
+    
+    def verify(self, trusted_certs):
+        if not self.xml:
+            self.decode()        
+        trusted_cert_objects = [GID(filename=f) for f in trusted_certs]
+
+        # Use legacy verification if this is a legacy credential
+        if self.legacy:
+            self.legacy.verify_chain(trusted_cert_objects)
+            if self.legacy.client_gid:
+                self.legacy.client_gid.verify_chain(trusted_cert_objects)
+            if self.legacy.object_gid:
+                self.legacy.object_gid.verify_chain(trusted_cert_objects)
+            return True
+        
+        # make sure it is not expired
+        if self.get_lifetime() < datetime.datetime.utcnow():
+            raise CredentialNotVerifiable("credential is expired")
+
+        # Verify the signatures
+        filename = self.save_to_random_tmp_file()
+        cert_args = " ".join(['--trusted-pem %s' % x for x in trusted_certs])
+
+        # Verify the gids of this cred and of its parents
+
+
+
+        for cur_cred in self.get_credential_list():
+            cur_cred.get_gid_object().verify_chain(trusted_cert_objects)
+            cur_cred.get_gid_caller().verify_chain(trusted_cert_objects)            
+
+
+        refs = []
+        refs.append("Sig_%s" % self.get_refid())
+
+        parentRefs = self.updateRefID()
+        for ref in parentRefs:
+            refs.append("Sig_%s" % ref)
+
+        for ref in refs:
+            verified = os.popen('%s --verify --node-id "%s" %s %s 2>&1' \
+                            % (self.xmlsec_path, ref, cert_args, filename)).read()
+            if not verified.strip().startswith("OK"):
+                raise CredentialNotVerifiable("xmlsec1 error: " + verified)
+        os.remove(filename)
+
+        # Verify the parents (delegation)
         if self.parent:
-            # make sure the parent delegated rights to the child
-            if not self.parent.get_delegate():
-                raise MissingDelegateBit(self.parent.get_subject())
+            self.verify_parent(self.parent)
+
+        # Make sure the issuer is the target's authority
+        self.verify_issuer()
+        return True
 
-            # make sure the rights given to the child are a subset of the
-            # parents rights
-            if not self.parent.get_privileges().is_superset(self.get_privileges()):
-                raise ChildRightsNotSubsetOfParent(self.get_subject() 
-                                                   + " " + self.parent.get_privileges().save_to_string()
-                                                   + " " + self.get_privileges().save_to_string())
+    ##
+    # Creates a list of the credential and its parents, with the root 
+    # (original delegated credential) as the last item in the list
+    def get_credential_list(self):    
+        cur_cred = self
+        list = []
+        while cur_cred:
+            list.append(cur_cred)
+            if cur_cred.parent:
+                cur_cred = cur_cred.parent
+            else:
+                cur_cred = None
+        return list
+    
+    ##
+    # Make sure the credential's target gid was signed by (or is the same) as the entity that signed
+    # the original credential.  
+    def verify_issuer(self):                
+        root_cred = self.get_credential_list()[-1]
+        root_target_gid = root_cred.get_gid_object()
+        root_cred_signer = root_cred.get_signature().get_issuer_gid()
+        
+        if root_target_gid.is_signed_by_cert(root_cred_signer) or \
+            root_target_gid.save_to_string() == root_cred_signer.save_to_string():
+            pass
+        else:            
+            raise CredentialNotVerifiable("Could not verify credential signer")
+        
 
-        return
+    ##
+    # -- For Delegates (credentials with parents) verify that:
+    # . The privileges must be a subset of the parent credentials
+    # . The privileges must have "can_delegate" set for each delegated privilege
+    # . The target gid must be the same between child and parents
+    # . The expiry time on the child must be no later than the parent
+    # . The signer of the child must be the owner of the parent
+        
+    def verify_parent(self, parent_cred):
+        # make sure the rights given to the child are a subset of the
+        # parents rights (and check delegate bits)
+        if not parent_cred.get_privileges().is_superset(self.get_privileges()):
+            raise ChildRightsNotSubsetOfParent(
+                self.parent.get_privileges().save_to_string() + " " +
+                self.get_privileges().save_to_string())
+
+        # make sure my target gid is the same as the parent's
+        if not parent_cred.get_gid_object().save_to_string() == \
+           self.get_gid_object().save_to_string():
+            raise CredentialNotVerifiable("target gid not equal between parent and child")
+
+        # make sure my expiry time is <= my parent's
+        if not parent_cred.get_lifetime() >= self.get_lifetime():
+            raise CredentialNotVerifiable("delegated credential expires after parent")
+
+        # make sure my signer is the parent's caller
+        if not parent_cred.get_gid_caller().save_to_string(False) == \
+           self.get_signature().get_issuer_gid().save_to_string(False):
+            raise CredentialNotVerifiable("delegated credential not signed by parent caller")
+                
+        if parent_cred.parent:
+            parent_cred.verify_parent(parent_cred.parent)
 
     ##
     # Dump the contents of a credential to stdout in human-readable format
@@ -237,9 +754,8 @@ class Credential(Certificate):
         if gidObject:
             gidObject.dump(8, dump_parents)
 
-        print "   delegate:", self.get_delegate()
 
         if self.parent and dump_parents:
-           print "PARENT",
-           self.parent.dump(dump_parents)
+            print "PARENT",
+            self.parent.dump_parents()
 
diff --git a/sfa/trust/credential_legacy.py b/sfa/trust/credential_legacy.py
new file mode 100644 (file)
index 0000000..c33ed6f
--- /dev/null
@@ -0,0 +1,247 @@
+##
+# Implements SFA Credentials
+#
+# Credentials are layered on top of certificates, and are essentially a
+# certificate that stores a tuple of parameters.
+##
+
+### $Id: credential.py 17477 2010-03-25 16:49:34Z jkarlin $
+### $URL: svn+ssh://svn.planet-lab.org/svn/sfa/branches/geni-api/sfa/trust/credential.py $
+
+import xmlrpclib
+
+from sfa.trust.certificate import Certificate
+from sfa.trust.rights import *
+from sfa.trust.gid import *
+from sfa.util.faults import *
+from sfa.util.sfalogging import *
+
+##
+# Credential is a tuple:
+#     (GIDCaller, GIDObject, LifeTime, Privileges, Delegate)
+#
+# These fields are encoded using xmlrpc into the subjectAltName field of the
+# x509 certificate. Note: Call encode() once the fields have been filled in
+# to perform this encoding.
+
+class CredentialLegacy(Certificate):
+    gidCaller = None
+    gidObject = None
+    lifeTime = None
+    privileges = None
+    delegate = False
+
+    ##
+    # Create a Credential object
+    #
+    # @param create If true, create a blank x509 certificate
+    # @param subject If subject!=None, create an x509 cert with the subject name
+    # @param string If string!=None, load the credential from the string
+    # @param filename If filename!=None, load the credential from the file
+
+    def __init__(self, create=False, subject=None, string=None, filename=None):
+        Certificate.__init__(self, create, subject, string, filename)
+
+    ##
+    # set the GID of the caller
+    #
+    # @param gid GID object of the caller
+
+    def set_gid_caller(self, gid):
+        self.gidCaller = gid
+        # gid origin caller is the caller's gid by default
+        self.gidOriginCaller = gid
+
+    ##
+    # get the GID of the object
+
+    def get_gid_caller(self):
+        if not self.gidCaller:
+            self.decode()
+        return self.gidCaller
+
+    ##
+    # set the GID of the object
+    #
+    # @param gid GID object of the object
+
+    def set_gid_object(self, gid):
+        self.gidObject = gid
+
+    ##
+    # get the GID of the object
+
+    def get_gid_object(self):
+        if not self.gidObject:
+            self.decode()
+        return self.gidObject
+
+    ##
+    # set the lifetime of this credential
+    #
+    # @param lifetime lifetime of credential
+
+    def set_lifetime(self, lifeTime):
+        self.lifeTime = lifeTime
+
+    ##
+    # get the lifetime of the credential
+
+    def get_lifetime(self):
+        if not self.lifeTime:
+            self.decode()
+        return self.lifeTime
+
+    ##
+    # set the delegate bit
+    #
+    # @param delegate boolean (True or False)
+
+    def set_delegate(self, delegate):
+        self.delegate = delegate
+
+    ##
+    # get the delegate bit
+
+    def get_delegate(self):
+        if not self.delegate:
+            self.decode()
+        return self.delegate
+
+    ##
+    # set the privileges
+    #
+    # @param privs either a comma-separated list of privileges of a RightList object
+
+    def set_privileges(self, privs):
+        if isinstance(privs, str):
+            self.privileges = RightList(string = privs)
+        else:
+            self.privileges = privs
+
+    ##
+    # return the privileges as a RightList object
+
+    def get_privileges(self):
+        if not self.privileges:
+            self.decode()
+        return self.privileges
+
+    ##
+    # determine whether the credential allows a particular operation to be
+    # performed
+    #
+    # @param op_name string specifying name of operation ("lookup", "update", etc)
+
+    def can_perform(self, op_name):
+        rights = self.get_privileges()
+        if not rights:
+            return False
+        return rights.can_perform(op_name)
+
+    ##
+    # Encode the attributes of the credential into a string and store that
+    # string in the alt-subject-name field of the X509 object. This should be
+    # done immediately before signing the credential.
+
+    def encode(self):
+        dict = {"gidCaller": None,
+                "gidObject": None,
+                "lifeTime": self.lifeTime,
+                "privileges": None,
+                "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)
+        if self.privileges:
+            dict["privileges"] = self.privileges.save_to_string()
+        str = xmlrpclib.dumps((dict,), allow_none=True)
+        self.set_data('URI:http://' + str)
+
+    ##
+    # Retrieve the attributes of the credential from the alt-subject-name field
+    # of the X509 certificate. This is automatically done by the various
+    # get_* methods of this class and should not need to be called explicitly.
+
+    def decode(self):
+        data = self.get_data().lstrip('URI:http://')
+        
+        if data:
+            dict = xmlrpclib.loads(data)[0][0]
+        else:
+            dict = {}
+
+        self.lifeTime = dict.get("lifeTime", None)
+        self.delegate = dict.get("delegate", None)
+
+        privStr = dict.get("privileges", None)
+        if privStr:
+            self.privileges = RightList(string = privStr)
+        else:
+            self.privileges = None
+
+        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
+
+    ##
+    # Verify that a chain of credentials is valid (see cert.py:verify). In
+    # addition to the checks for ordinary certificates, verification also
+    # ensures that the delegate bit was set by each parent in the chain. If
+    # a delegate bit was not set, then an exception is thrown.
+    #
+    # Each credential must be a subset of the rights of the parent.
+
+    def verify_chain(self, trusted_certs = None):
+        # do the normal certificate verification stuff
+        Certificate.verify_chain(self, trusted_certs)
+
+        if self.parent:
+            # make sure the parent delegated rights to the child
+            if not self.parent.get_delegate():
+                raise MissingDelegateBit(self.parent.get_subject())
+
+            # make sure the rights given to the child are a subset of the
+            # parents rights
+            if not self.parent.get_privileges().is_superset(self.get_privileges()):
+                raise ChildRightsNotSubsetOfParent(self.get_subject() 
+                                                   + " " + self.parent.get_privileges().save_to_string()
+                                                   + " " + self.get_privileges().save_to_string())
+
+        return
+
+    ##
+    # Dump the contents of a credential to stdout in human-readable format
+    #
+    # @param dump_parents If true, also dump the parent certificates
+
+    def dump(self, dump_parents=False):
+        print "CREDENTIAL", self.get_subject()
+
+        print "      privs:", self.get_privileges().save_to_string()
+
+        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 "   delegate:", self.get_delegate()
+
+        if self.parent and dump_parents:
+           print "PARENT",
+           self.parent.dump(dump_parents)
+
index b1f2c82..99fa031 100644 (file)
@@ -8,9 +8,10 @@
 
 import xmlrpclib
 import uuid
-
 from sfa.trust.certificate import Certificate
 from sfa.util.namespace import *
+from sfa.util.sfalogging import logger
+
 ##
 # Create a new uuid. Returns the UUID as a string.
 
@@ -18,8 +19,8 @@ def create_uuid():
     return str(uuid.uuid4().int)
 
 ##
-# GID is a tuplie:
-#    (uuid, hrn, public_key)
+# GID is a tuple:
+#    (uuid, urn, public_key)
 #
 # UUID is a unique identifier and is created by the python uuid module
 #    (or the utility function create_uuid() in gid.py).
@@ -58,16 +59,22 @@ class GID(Certificate):
     def __init__(self, create=False, subject=None, string=None, filename=None, uuid=None, hrn=None, urn=None):
         
         Certificate.__init__(self, create, subject, string, filename)
+        if subject:
+            logger.info("subject: %s" % subject)
         if uuid:
-            self.uuid = uuid
+            self.uuid = int(uuid)
         if hrn:
             self.hrn = hrn
+            self.urn = hrn_to_urn(hrn, 'unknown')
         if urn:
             self.urn = urn
             self.hrn, type = urn_to_hrn(urn)
 
     def set_uuid(self, uuid):
-        self.uuid = uuid
+        if isinstance(uuid, str):
+            self.uuid = int(uuid)
+        else:
+            self.uuid = uuid
 
     def get_uuid(self):
         if not self.uuid:
@@ -91,6 +98,12 @@ class GID(Certificate):
             self.decode()
         return self.urn            
 
+    def get_type(self):
+        if not self.urn:
+            self.decode()
+        _, t = urn_to_hrn(self.urn)
+        return t
+    
     ##
     # Encode the GID fields and package them into the subject-alt-name field
     # of the X509 certificate. This must be called prior to signing the
@@ -101,11 +114,16 @@ class GID(Certificate):
             urn = self.urn
         else:
             urn = hrn_to_urn(self.hrn, None)
-        dict = {"uuid": self.uuid,
-                "urn": self.urn}
-        str = xmlrpclib.dumps((dict,))
-        self.set_data(str)
+            
+        str = "URI:" + urn
+
+        if self.uuid:
+            str += ", " + "URI:" + uuid.UUID(int=self.uuid).urn
+        
+        self.set_data(str, 'subjectAltName')
+
+        
+
 
     ##
     # Decode the subject-alt-name field of the X509 certificate into the
@@ -113,12 +131,19 @@ class GID(Certificate):
     # functions in this class.
 
     def decode(self):
-        data = self.get_data()
+        data = self.get_data('subjectAltName')
+        dict = {}
         if data:
-            dict = xmlrpclib.loads(self.get_data())[0][0]
-        else:
-            dict = {}
-
+            if data.lower().startswith('uri:http://<params>'):
+                dict = xmlrpclib.loads(data[11:])[0][0]
+            else:
+                spl = data.split(', ')
+                for val in spl:
+                    if val.lower().startswith('uri:urn:uuid:'):
+                        dict['uuid'] = uuid.UUID(val[4:]).int
+                    elif val.lower().startswith('uri:urn:publicid:idn+'):
+                        dict['urn'] = val[4:]
+                    
         self.uuid = dict.get("uuid", None)
         self.urn = dict.get("urn", None)
         self.hrn = dict.get("hrn", None)    
@@ -152,12 +177,19 @@ class GID(Certificate):
 
     def verify_chain(self, trusted_certs = None):
         # do the normal certificate verification stuff
-        Certificate.verify_chain(self, trusted_certs)
-
+        trusted_root = Certificate.verify_chain(self, trusted_certs)        
+       
         if self.parent:
             # make sure the parent's hrn is a prefix of the child's hrn
             if not self.get_hrn().startswith(self.parent.get_hrn()):
                 raise GidParentHrn(self.parent.get_subject())
+        else:
+            # make sure that the trusted root's hrn is a prefix of the child's
+            trusted_gid = GID(string=trusted_root.save_to_string())
+            trusted_hrn = trusted_gid.get_hrn()
+            cur_hrn = self.get_hrn()
+            if not self.get_hrn().startswith(trusted_hrn):
+                raise GidParentHrn(trusted_hrn + " " + self.get_hrn())
 
         return
 
index b3e730f..e277ec5 100644 (file)
@@ -60,6 +60,12 @@ class AuthInfo:
         self.gid_filename = fn
         self.gid_object = None
 
+    def get_privkey_filename(self):
+        return self.privkey_filename
+
+    def get_gid_filename(self):
+        return self.gid_filename
+
     ##
     # Get the GID in the form of a GID object
 
@@ -159,8 +165,9 @@ class Hierarchy:
 
         # create the parent authority if necessary
         parent_hrn = get_authority(hrn)
-        if (parent_hrn) and (not self.auth_exists(parent_hrn)) and (create_parents):
-            self.create_auth(parent_hrn, create_parents)
+        parent_urn = hrn_to_urn(parent_hrn, 'authority')
+        if (parent_hrn) and (not self.auth_exists(parent_urn)) and (create_parents):
+            self.create_auth(parent_urn, create_parents)
 
         (directory, gid_filename, privkey_filename, dbinfo_filename) = \
             self.get_auth_filenames(hrn)
@@ -296,18 +303,20 @@ class Hierarchy:
         cred.set_gid_caller(gid)
         cred.set_gid_object(gid)
         cred.set_privileges(kind)
-        cred.set_delegate(True)
-        cred.set_pubkey(auth_info.get_gid_object().get_pubkey())
+        cred.get_privileges().delegate_all_privileges(True)
+        #cred.set_pubkey(auth_info.get_gid_object().get_pubkey())
 
         parent_hrn = get_authority(hrn)
         if not parent_hrn or hrn == self.config.SFA_INTERFACE_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)
+            cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
         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_issuer_keys(parent_auth_info.get_privkey_filename(), parent_auth_info.get_gid_filename())
+
+            
             cred.set_parent(self.get_auth_cred(parent_hrn, kind))
 
         cred.encode()
index 6d7fc85..59324e8 100644 (file)
 # allows "listslices", "listcomponentresources", etc.
 ##
 
+
+
 ##
 # privilege_table is a list of priviliges and what operations are allowed
 # per privilege.
+# Note that "*" is a privilege granted by ProtoGENI slice authorities, and we
+# give it access to the GENI AM calls
 
 privilege_table = {"authority": ["register", "remove", "update", "resolve", "list", "getcredential", "*"],
                    "refresh": ["remove", "update"],
                    "resolve": ["resolve", "list", "getcredential"],
-                   "sa": ["getticket", "redeemslice", "redeemticket", "createslice", "deleteslice", "updateslice", 
-                          "getsliceresources", "getticket", "loanresources", "stopslice", "startslice", 
-                          "deleteslice", "resetslice", "listslices", "listnodes", "getpolicy"],
-                   "embed": ["getticket", "redeemslice", "redeemticket", "createslice", "deleteslice", "updateslice", "getsliceresources"],
+                   "sa": ["getticket", "redeemslice", "redeemticket", "createslice", "createsliver", "deleteslice", "deletesliver", "updateslice",
+                          "getsliceresources", "getticket", "loanresources", "stopslice", "startslice", "renewsliver",
+                          "deleteslice", "deletesliver", "resetslice", "listslices", "listnodes", "getpolicy", "sliverstatus"],
+                   "embed": ["getticket", "redeemslice", "redeemticket", "createslice", "createsliver", "renewsliver", "deleteslice", "deletesliver", "updateslice", "sliverstatus", "getsliceresources", "shutdown"],
                    "bind": ["getticket", "loanresources", "redeemticket"],
-                   "control": ["updateslice", "createslice", "stopslice", "startslice", "deleteslice", "resetslice", "getsliceresources", "getgids"],
+                   "control": ["updateslice", "createslice", "createsliver", "renewsliver", "sliverstatus", "stopslice", "startslice", "deleteslice", "deletesliver", "resetslice", "getsliceresources", "getgids"],
                    "info": ["listslices", "listnodes", "getpolicy"],
                    "ma": ["setbootstate", "getbootstate", "reboot", "getgids", "gettrustedcerts"],
-                   "operator": ["gettrustedcerts", "getgids"]}
+                   "operator": ["gettrustedcerts", "getgids"],                   
+                   "*": ["createsliver", "deletesliver", "sliverstatus", "renewsliver", "shutdown"]} 
+
 
 
 ##
@@ -50,11 +56,15 @@ def determine_rights(type, name):
         rl.add("resolve")
         rl.add("info")
     elif type == "sa":
-        rl.add("authority,sa")
+        rl.add("authority")
+        rl.add("sa")
     elif type == "ma":
-        rl.add("authority,ma")
+        rl.add("authority")
+        rl.add("ma")
     elif type == "authority":
-        rl.add("authority,sa,ma")
+        rl.add("authority")
+        rl.add("sa")
+        rl.add("ma")
     elif type == "slice":
         rl.add("refresh")
         rl.add("embed")
@@ -72,50 +82,54 @@ def determine_rights(type, name):
 
 
 class Right:
-   ##
-   # Create a new right.
-   #
-   # @param kind is a string naming the right. For example "control"
+    ##
+    # Create a new right.
+    #
+    # @param kind is a string naming the right. For example "control"
+
+    def __init__(self, kind, delegate=False):
+        self.kind = kind
+        self.delegate = delegate
 
-   def __init__(self, kind):
-      self.kind = kind
+    ##
+    # Test to see if this right object is allowed to perform an operation.
+    # Returns True if the operation is allowed, False otherwise.
+    #
+    # @param op_name is a string naming the operation. For example "listslices".
 
-   ##
-   # Test to see if this right object is allowed to perform an operation.
-   # Returns True if the operation is allowed, False otherwise.
-   #
-   # @param op_name is a string naming the operation. For example "listslices".
+    def can_perform(self, op_name):
+        allowed_ops = privilege_table.get(self.kind.lower(), None)
+        if not allowed_ops:
+            return False
 
-   def can_perform(self, op_name):
-      allowed_ops = privilege_table.get(self.kind.lower(), None)
-      if not allowed_ops:
-         return False
+        # if "*" is specified, then all ops are permitted
+        if "*" in allowed_ops:
+            return True
 
-      # if "*" is specified, then all ops are permitted
-      if "*" in allowed_ops:
-         return True
+        return (op_name.lower() in allowed_ops)
 
-      return (op_name.lower() in allowed_ops)
+    ##
+    # Test to see if this right is a superset of a child right. A right is a
+    # superset if every operating that is allowed by the child is also allowed
+    # by this object.
+    #
+    # @param child is a Right object describing the child right
 
-   ##
-   # Test to see if this right is a superset of a child right. A right is a
-   # superset if every operating that is allowed by the child is also allowed
-   # by this object.
-   #
-   # @param child is a Right object describing the child right
+    def is_superset(self, child):
+        my_allowed_ops = privilege_table.get(self.kind.lower(), None)
+        child_allowed_ops = privilege_table.get(child.kind.lower(), None)
 
-   def is_superset(self, child):
-      my_allowed_ops = privilege_table.get(self.kind.lower(), None)
-      child_allowed_ops = privilege_table.get(child.kind.lower(), None)
+        if not self.delegate:
+            return False
 
-      if "*" in my_allowed_ops:
-          return True
+        if "*" in my_allowed_ops:
+            return True
 
-      for right in child_allowed_ops:
-          if not right in my_allowed_ops:
-              return False
+        for right in child_allowed_ops:
+            if not right in my_allowed_ops:
+                return False
 
-      return True
+        return True
 
 ##
 # A RightList object represents a list of privileges.
@@ -139,9 +153,9 @@ class RightList:
     #
     # @param right is either a Right object or a string describing the right
 
-    def add(self, right):
+    def add(self, right, delegate=False):
         if isinstance(right, str):
-            right = Right(kind = right)
+            right = Right(right, delegate)
         self.rights.append(right)
 
     ##
@@ -156,7 +170,14 @@ class RightList:
 
         parts = string.split(",")
         for part in parts:
-            self.rights.append(Right(part))
+            if ':' in part:
+                spl = part.split(':')
+                kind = spl[0].strip()
+                delegate = bool(int(spl[1]))
+            else:
+                kind = part.strip()
+                delegate = 0
+            self.rights.append(Right(kind, bool(delegate)))
 
     ##
     # Save the rightlist object to a string. It is saved in the format of a
@@ -165,7 +186,7 @@ class RightList:
     def save_to_string(self):
         right_names = []
         for right in self.rights:
-            right_names.append(right.kind)
+            right_names.append('%s:%d' % (right.kind.strip(), right.delegate))
 
         return ",".join(right_names)
 
@@ -202,7 +223,29 @@ class RightList:
 
 
     ##
-    # Determine tje rights that an object should have. The rights are entirely
+    # set the delegate bit to 'delegate' on
+    # all privileges
+    #
+    # @param delegate boolean (True or False)
+
+    def delegate_all_privileges(self, delegate):
+        for right in self.rights:
+            right.delegate = delegate
+
+    ##
+    # true if all privileges have delegate bit set true
+    # false otherwise
+
+    def get_all_delegate(self):
+        for right in self.rights:
+            if not right.delegate:
+                return False
+        return True
+
+
+
+    ##
+    # Determine the rights that an object should have. The rights are entirely
     # dependent on the type of the object. For example, users automatically
     # get "refresh", "resolve", and "info".
     #
@@ -224,11 +267,15 @@ class RightList:
             rl.add("resolve")
             rl.add("info")
         elif type == "sa":
-            rl.add("authority,sa")
+            rl.add("authority")
+            rl.add("sa")
         elif type == "ma":
-            rl.add("authority,ma")
+            rl.add("authority")
+            rl.add("ma")
         elif type == "authority":
-            rl.add("authority,sa,ma")
+            rl.add("authority")
+            rl.add("sa")
+            rl.add("ma")
         elif type == "slice":
             rl.add("refresh")
             rl.add("embed")
index 90dfe46..2d4b89e 100644 (file)
@@ -34,3 +34,13 @@ class TrustedRootList:
 
         return gid_list
 
+    def get_file_list(self):
+        gid_file_list = []
+        
+        file_list = os.listdir(self.basedir)
+        for gid_file in file_list:
+            fn = os.path.join(self.basedir, gid_file)
+            if os.path.isfile(fn):
+                gid_file_list.append(fn)
+
+        return gid_file_list
index 0c2b78a..a7a07b6 100644 (file)
@@ -142,6 +142,7 @@ class BaseAPI:
         Return a new instance of the specified method.
         """
         # Look up method
+        print self.methods
         if method not in self.methods:
             raise SfaInvalidAPIMethod, method
         
@@ -164,7 +165,8 @@ class BaseAPI:
         self.source = source
         return function(*args)
 
-    def handle(self, source, data):
+    
+    def handle(self, source, data, method_map):
         """
         Handle an XML-RPC or SOAP request from the specified source.
         """
@@ -172,7 +174,10 @@ class BaseAPI:
         try:
             interface = xmlrpclib
             (args, method) = xmlrpclib.loads(data)
+            if method_map.has_key(method):
+                method = method_map[method]
             methodresponse = True
+            
         except Exception, e:
             if SOAPpy is not None:
                 interface = SOAPpy
index 8e5ece1..5880fd9 100644 (file)
@@ -95,7 +95,8 @@ class ExistingRecord(SfaFault):
         SfaFault.__init__(self, 111, faultString, extra)
     def __str__(self):
         return repr(self.value)
-        
+
+    
 class NonexistingCredType(SfaFault):
     def __init__(self, value, extra = None):
         self.value = value
@@ -105,7 +106,7 @@ class NonexistingCredType(SfaFault):
         return repr(self.value)
 
 class NonexistingFile(SfaFault):
-    def __init__(self, value):
+    def __init__(self, value, extra = None):
         self.value = value
         faultString = "Non existing file: %(value)s, " % locals()
         SfaFault.__init__(self, 111, faultString, extra)
@@ -113,7 +114,7 @@ class NonexistingFile(SfaFault):
         return repr(self.value)
 
 class InvalidRPCParams(SfaFault):
-    def __init__(self, value):
+    def __init__(self, value, extra = None):
         self.value = value
         faultString = "Invalid RPC Params: %(value)s, " % locals()
         SfaFault.__init__(self, 102, faultString, extra)
@@ -217,7 +218,15 @@ class CertNotSignedByParent(SfaFault):
         SfaFault.__init__(self, 103, faultString, extra)
     def __str__(self):
         return repr(self.value)
-
+    
+class GidParentHrn(SfaFault):
+    def __init__(self, value, extra = None):
+        self.value = value
+        faultString = "Cert URN is not an extension of its parent: %(value)s" % locals()
+        SfaFault.__init__(self, 103, faultString, extra)
+    def __str__(self):
+        return repr(self.value)
+        
 class GidInvalidParentHrn(SfaFault):
     def __init__(self, value, extra = None):
         self.value = value
@@ -235,7 +244,7 @@ class SliverDoesNotExist(SfaFault):
         return repr(self.value)
 
 class BadRequestHash(xmlrpclib.Fault):
-   def __init__(self, hash = None):
+    def __init__(self, hash = None, extra = None):
         faultString = "bad request hash: " + str(hash)
         xmlrpclib.Fault.__init__(self, 902, faultString)
 
@@ -270,3 +279,10 @@ class AccountNotEnabled(SfaFault):
     def __str__(self):
         return repr(self.value)
 
+class CredentialNotVerifiable(SfaFault):
+    def __init__(self, value, extra = None):
+        self.value = value
+        faultString = "Unable to verify credential: %(value)s, " %locals()
+        SfaFault.__init__(self, 115, faultString, extra)
+    def __str__(self):
+        return repr(self.value)
index e3a7536..45ce06a 100644 (file)
@@ -9,7 +9,11 @@ def get_leaf(hrn):
     parts = hrn.split(".")
     return ".".join(parts[-1:])
 
-def get_authority(hrn):
+def get_authority(xrn):
+    hrn, type = urn_to_hrn(xrn)
+    if type and type == 'authority':
+        return hrn
+    
     parts = hrn.split(".")
     return ".".join(parts[:-1])
 
@@ -85,7 +89,14 @@ def hrn_to_urn(hrn, type=None):
 
     authority = get_authority(hrn)
     name = get_leaf(hrn)
-    urn = "+".join([unicode(part).replace('.', ':') \
-                    for part in ['',authority,type,name]])
-
+    
+    if authority.startswith("plc"):
+        if type == None:
+            urn = "+".join(['',authority.replace('.',':'),name])
+        else:
+            urn = "+".join(['',authority.replace('.',':'),type,name])
+
+    else:
+        urn = "+".join(['',authority,type,name])
+        
     return URN_PREFIX + urn
index 2ee4378..e6d3f3b 100644 (file)
@@ -105,8 +105,8 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
             # get arguments
             request = self.rfile.read(int(self.headers["content-length"]))
             remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
-            self.api.remote_addr = remote_addr
-            response = self.api.handle(remote_addr, request)
+            self.api.remote_addr = remote_addr            
+            response = self.api.handle(remote_addr, request, self.server.method_map)
 
         
         except Exception, fault:
@@ -139,6 +139,7 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR
         self.interface = None
         self.key_file = key_file
         self.cert_file = cert_file
+        self.method_map = {}
         # add cache to the request handler
         HandlerClass.cache = Cache()
         #for compatibility with python 2.4 (centos53)
@@ -255,7 +256,7 @@ class SfaServer(threading.Thread):
 
     ##
     # Register functions that will be served by the XMLRPC server. This
-    # function should be overrided by each descendant class.
+    # function should be overridden by each descendant class.
 
     def register_functions(self):
         self.server.register_function(self.noop)
index ff08c08..0e17ccb 100644 (file)
@@ -1,17 +1,20 @@
 import logging
+import os
 
 #SFA access log initialization
-
+TMPDIR = os.getenv("TMPDIR", "/tmp")
+SFA_HTTPD_ACCESS_LOGFILE = TMPDIR + "/" + 'sfa_httpd_access.log'
 SFA_ACCESS_LOGFILE='/var/log/sfa_access.log'
-SFA_HTTPD_ACCESS_LOGFILE='/tmp/sfa_httpd_access.log'
 logger=logging.getLogger()
 logger.setLevel(logging.INFO)
+
 try:
-     logfile=logging.FileHandler(SFA_ACCESS_LOGFILE)
+    logfile=logging.FileHandler(SFA_ACCESS_LOGFILE)
 except IOError:
-     # This is usually a permissions error becaue the file is
-     # owned by root, but httpd is trying to access it.
-     logfile=logging.FileHandler(SFA_HTTPD_ACCESS_LOGFILE)
+    # This is usually a permissions error becaue the file is
+    # owned by root, but httpd is trying to access it.
+    logfile=logging.FileHandler(SFA_HTTPD_ACCESS_LOGFILE)
+    
 formatter = logging.Formatter("%(asctime)s - %(message)s")
 logfile.setFormatter(formatter)
 logger.addHandler(logfile)
index 8364627..6c68776 100644 (file)
@@ -28,7 +28,7 @@ class SfaTable(list):
 
         if record_filter:
             records = self.find(record_filter)
-            for record in reocrds:
+            for record in records:
                 self.append(record)             
 
     def exists(self):