From 455f3a3cfa775a386a66db8ca0a040f7c5b33d29 Mon Sep 17 00:00:00 2001 From: Scott Baker Date: Fri, 3 Oct 2008 00:33:50 +0000 Subject: [PATCH] check in latest updates to documentation --- util/cert.py | 13 +- util/config.py | 35 +- util/credential.py | 4 +- util/db.py | 81 ----- util/excep.pyc | Bin 3020 -> 12102 bytes util/geniclient.py | 205 ++++++++++- util/geniserver.py | 56 ++- util/gid.py | 84 ++++- util/hierarchy.py | 125 ++++++- util/misc.py | 7 +- util/pl_to_geni.py | 174 --------- util/record.py | 109 +++++- util/rights.py | 74 +++- util/sec/certgen.py | 111 ------ util/sec/certgen.pyc | Bin 4119 -> 0 bytes util/sec/certgen2.py | 368 ------------------- util/sec/certgen_m2crypto.py | 20 - util/sec/certs/den.py | 43 --- util/sec/certs/gen.py | 57 --- util/sec/certs/jp.cert | 11 - util/sec/certs/jp.pkey | 15 - util/sec/certs/planetlab.cert | 11 - util/sec/certs/planetlab.pkey | 15 - util/sec/certs/usersoner.cert | 11 - util/sec/sec.py | 663 ---------------------------------- util/sec/sec.pyc | Bin 18124 -> 0 bytes 26 files changed, 635 insertions(+), 1657 deletions(-) delete mode 100644 util/db.py delete mode 100644 util/pl_to_geni.py delete mode 100755 util/sec/certgen.py delete mode 100644 util/sec/certgen.pyc delete mode 100755 util/sec/certgen2.py delete mode 100755 util/sec/certgen_m2crypto.py delete mode 100644 util/sec/certs/den.py delete mode 100644 util/sec/certs/gen.py delete mode 100644 util/sec/certs/jp.cert delete mode 100644 util/sec/certs/jp.pkey delete mode 100644 util/sec/certs/planetlab.cert delete mode 100644 util/sec/certs/planetlab.pkey delete mode 100644 util/sec/certs/usersoner.cert delete mode 100644 util/sec/sec.py delete mode 100644 util/sec/sec.pyc diff --git a/util/cert.py b/util/cert.py index 1994af5e..5d4796e0 100644 --- a/util/cert.py +++ b/util/cert.py @@ -192,13 +192,6 @@ class Certificate: def load_from_pyopenssl_x509(self, x509): self.cert = x509 - ## - # Return another instance of the same class. - # XXX: probably will be deleted, can use cls() function instead - - def create_similar(self): - return Certificate() - ## # Load the certificate from a string @@ -211,7 +204,7 @@ class Certificate: # 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.create_similar() + self.parent = self.__class__() self.parent.load_from_string(parts[1]) ## @@ -429,7 +422,9 @@ class Certificate: return self.parent ## - # Verify a chain of certificates. + # 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:
    #     if this_certificate was signed by trusted_certs:
diff --git a/util/config.py b/util/config.py
index 08548098..562aee17 100644
--- a/util/config.py
+++ b/util/config.py
@@ -1,10 +1,18 @@
-# config.py
+##
+# Geniwrapper Configuration Info
 #
-# geniwrapper configuration info
+# This module holds configuration parameters for geniwrapper. There are two
+# main pieces of information that are used: the database connection and
+# the PLCAPI connection
+##
+
+##
+# Geniwrapper uses a MYSQL database to store records. This database may be
+# co-located with the PLC database, or it may be a separate database. The
+# following parameters define the connection to the database.
 #
-# this module holds configuration parameters for geniwrapper. The wrapper
-# needs an account to connect to PLC with and a database to store geni
-# tables
+# Note that Geniwrapper does not access any of the PLC databases directly via
+# a mysql connection; All PLC databases are accessed via PLCAPI.
 
 def get_default_dbinfo():
     dbinfo={}
@@ -16,4 +24,21 @@ def get_default_dbinfo():
 
     return dbinfo
 
+##
+# Geniwrapper uses a PLCAPI connection to perform operations on the registry,
+# such as creating and deleting slices. This connection requires an account
+# on the PLC server with full administrator access.
+#
+# The Url parameter controls whether the connection uses PLCAPI directly (i.e.
+# Geniwrapper is located on the same machine as PLC), or uses a XMLRPC connection
+# to the PLC machine. If you wish to use the API directly, then remove the Url
+# field from the dictionary. 
+
+def get_pl_auth():
+    pl_auth = {'Username': 'root@198.0.0.132',
+    'AuthMethod': 'password',
+    'AuthString':  'root',
+    "Url": "https://localhost:443/PLCAPI/"
+    }
 
+    return pl_auth
diff --git a/util/credential.py b/util/credential.py
index 0daaf422..697adf05 100644
--- a/util/credential.py
+++ b/util/credential.py
@@ -1,9 +1,9 @@
 ##
-#
 # Implements Geni Credentials
 #
 # Credentials are layered on top of certificates, and are essentially a
 # certificate that stores a tuple of parameters.
+##
 
 from cert import *
 from rights import *
@@ -189,6 +189,8 @@ class Credential(Certificate):
     # 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
diff --git a/util/db.py b/util/db.py
deleted file mode 100644
index 590a0020..00000000
--- a/util/db.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import os
-from pg import DB
-from excep import *
-from tree import *
-from util import *
-
-#planetlab authentication structure
-pl_auth = {'Username': 'ssevinc@princeton.edu',        # User account
-'AuthMethod': 'password',        # Type of auth this is. Can be password, session ...
-'AuthString':  'Ss3928Ee' # the password for this account
-} 
-
-def get_plDB_conn():
-    dbname = 'plDB'
-    address = 'localhost'
-    port = 5433
-    user = 'postgres'
-    password = '111'
-    cnx = DB(dbname, address, port=port, user=user, passwd=password)
-    return cnx
-    
-#copy the pl db info to requester
-def get_plDB_info(dst):
-    dst.db_name = 'plDB'
-    dst.address = 'localhost'
-    dst.port = 5433
-    dst.user = 'postgres'
-    dst.password = '111'
-
-
-#determines the database info of a given hrn
-#if the hrn does not exist in the tree hierarchy None is returned
-def determine_dbinfo(hrn, tree):
-    info = tree.tree_lookup(hrn)
-    if info == None:
-        return None
-    else:
-        db_info = info.node_data['db_info']
-        cnx = DB(db_info.db_name, db_info.address, port = db_info.port, user = db_info.user, passwd = db_info.password)
-        tablename = db_info.table_name
-        return [cnx, tablename]
-
-#convert the parameter list to query string suitable for supplying to database queries
-#input: query type, table name and field-value pairs
-def generate_querystr(type, table, dict):
-    querystr = ""
-    if type == 'INSERT':
-        keys = dict.keys()
-        str1 = keys[0]
-        for i in range(1, len(keys)):
-            str1 = str1 + ','+ keys[i]
-        str2 = ""
-        for i in range(len(keys)-1):
-            if isinstance(dict[keys[i]],str):
-                str2 = str2 + "'" + dict[keys[i]] + "', " 
-            else:
-                str2 = str2 + str(dict[keys[i]]) + ", "
-        if isinstance(dict[keys[len(keys)-1]],str):
-            str2 = str2 + "'" + dict[keys[len(keys)-1]] + "'" 
-        else:
-            str2 = str2 + str(dict[keys[len(keys)-1]]) 
-        querystr = "INSERT INTO "+table+ "(" + str1 + ") VALUES(" + str2 + ")"
-    elif type == 'UPDATE':
-        str1 = ""
-        keys = dict.keys()
-        for i in range(len(keys)-1):
-            if keys[i] != 'hrn':
-                if isinstance(dict[keys[i]],str):
-                    str1 = str1 + keys[i] + " = '" + dict[keys[i]] + "', " 
-                else:
-                    str1 = str1 + keys[i] + " = " + dict[keys[i]] + ", " 
-        if keys[len(keys)-1] != 'hrn':
-            if isinstance(dict[keys[len(keys)-1]],str):
-                str1 = str1 + keys[len(keys)-1] + " = '" + dict[keys[len(keys)-1]] + "'" 
-            else:
-                str1 = str1 + keys[len(keys)-1] + " = '" + dict[keys[len(keys)-1]]
-        querystr = "UPDATE "+table+ " SET " + str1 + " WHERE hrn = '"+get_leaf(dict["hrn"])+"'"
-    elif type == 'DELETE':
-        querystr = "DELETE FROM "+table+" WHERE hrn = '"+get_leaf(dict["hrn"])+"'"
-    return querystr
-            
diff --git a/util/excep.pyc b/util/excep.pyc
index 3aa377e54ed3851881a5d7450ad1120da0234575..edb7e87d25158f251adddb80e54f919932fb4627 100644
GIT binary patch
literal 12102
zcmds-*=`#*6o!9!k@tPc-X=|&C@m17K#Kz1(0Df*JBE}1NeXl@7AcwWh@)~SI|5wv
zB7LLY^f`Lj$LJFjhtiN5^r}J4PRE7>`-evQ=kU`xL^$_RVS=E1~!?hQ&t@Zn@-gktCqoL
zQ+3X&6JYbHx?t5wu*Fnevg#Dra;mOabsB6nRoARK1Gb*3SFAbO9!ZRJ~=@1+e#0^?j=@g56HlJ62r+`yf^CT6G!hUaEd*)fKS&srtaGt6-H>eQ4D+
zu#Zr$d<;OZ0XPVwW~|zr?J2qarolT}#L{9=07qFR7bRQ(fq|%Aq4zs9&w3odSr~P>xmb_U1w3lj=p_}R&^#qu0IDZ(n^%+e
zad^sAwWH$KyrGk7CkiFkQFvVKM2%{TOL4BkPKT?iR$X~o?P?KKIo;EvPESt)2m&F6
z4g#V(A*(1g==117|611?yV*~2Rr^v#*ia6$iV7a0mFtM61@d?t%4&ZPfjK^1E?1I0=Y
z0Q>SRj0CG4Z61XxY$pXv77tv+^NYnpXVO0wwCcE@!Qv_{;$kUzgO;)1@!szK)`3Xc
zp>CWM43Dade17rx#F_Mu2Te2|W$>V^aUhaJ$kt{UMO^LfZxsxS&s@awi^b>8q<<`E
zLh)$^3j(FTJUxu{PTZA6Tc&>L;+n^+{{t5L&ZPfju?%1*lPqXqe`K~J
zo`lE!QnD5Ee02ll}>zcGi;&AyiI|xN6hvGnU&*#j0Rb)Lp>y
zOT}|%(mxf{@}$i(S57FC?8~IvY>I~9QrAT5L>H}*@zTXSzhHdtO!^0db_`zp4UEA`
zb&E&56>7dAidIJg7xVmr5jvCp!B_|ILk5hS)Zsc25$mUl!&uk5#|hU@n@6GIvS^5K
z7ybMq(sCyKBSIT_EQ1KGS~j`TgZxoQE>2y@^UFosne@*EwZdO#xS%RSTu{ysEy>wN
zuaIo~=)#^~HWFvjKO5HobTVwvf@N2*!IoMdi#_GzAfV6VI_ECv`6Z+0O!_B-_TJAj
zWY7|&9*Hxq3f$57$;CXsVEpV%`UitLTo=P&99C#@s_A@}J@si$hMJ>*=G>P#=VOj^
zm>qnxUu$;d%pQ#CxtkWNX{ec=Nq>nZGLy0loyN&!9c@kM2;*1%qqN_c
UBd+vG>*b-}$)Uf8_40c8FA~q4q5uE@

delta 371
zcmX>WcSc-h^Cw=eN6UY@Co@0+GXn#I0|Ns?@jQl!GV_^(HTWlgVqC!{z`($ur#JZu
zQ#p*;%A5dW%Cl^NF&p`XCo8Z=0 and alg_count>=0:
-        if alg_count == 0:
-            login_base = login_base+hrn_arr[j]
-        j = j-1
-        alg_count = alg_count-1
-    if len(login_base) > 20:
-        return login_base[0:20]
-    else:
-        return login_base
-        
-    
-#given an email or person_id (which are ids of a person in Planetlab), determined the last portion of hierarchical name of it in GENI
-def person_to_user(email, algorithm = 0):
-    hrn_suffix = ''
-    if algorithm == 0:
-        hrn_suffix = email.split('@')[0].replace('.','-')
-    elif algorithm == 1:
-        hrn_suffix = email.split('@')[0].replace('.','-')+'-'+email.split('@')[1].split('.')[0]
-    elif algorithm == 2:
-        hrn_suffix = email.replace('@','-')
-        hrn_suffix = hrn_suffix.replace('.','-')
-    return hrn_suffix
-    
-def plslice_to_slice(slice_name):
-    i = 0
-    while slice_name[i]!='_':
-        i = i+1
-    return slice_name[i+1:len(slice_name)]
-    
-def plnode_to_node(hostname, algorithm = 0):
-    hrn_suffix = ''
-    if algorithm == 0:
-        hrn_suffix = hostname.split('.')[0]
-    elif algorithm == 1:
-        hrn_suffix = hostname.split('.')[0]+'-'+hostname.split('.')[1]
-    elif algorithm == 2:
-        hrn_suffix = hrn_suffix.replace('.','-')
-    return hrn_suffix
-    
-def check_exists_pl(cnx, pointer, type):
-    exists = True
-    if type == 'SA' or type == 'MA':
-        res = cnx.query("SELECT deleted FROM sites WHERE site_id = "+str(pointer)).dictresult()
-        if len(res)==0 or res[0]['deleted'] == 't':
-            exists = False
-    elif type == 'slice':
-        res = cnx.query("SELECT is_deleted FROM slices WHERE slice_id = "+str(pointer)).dictresult()
-        if len(res)==0 or res[0]['is_deleted'] == 't':
-            exists = False
-    elif type == 'user':
-        res = cnx.query("SELECT deleted FROM persons WHERE person_id = "+str(pointer)).dictresult()
-        if len(res)==0 or res[0]['deleted'] == 't':
-            exists = False
-    elif type == 'node':
-        res = cnx.query("SELECT deleted FROM nodes WHERE node_id = "+str(pointer)).dictresult()
-        if len(res)==0 or res[0]['deleted'] == 't':
-            exists = False
-    return exists
-    
-def check_exists_geni(record, dbinfo):
-    cnx = dbinfo[0]
-    table = dbinfo[1] 
-    try:
-        #lookup in GENI tables
-        geni_res = cnx.query("SELECT * FROM "+table+" WHERE hrn = '"+get_leaf(record['g_params']["hrn"])+"' ").dictresult()
-        if geni_res:
-            return geni_res[0]
-        else:
-            return None
-    except:
-        return None
-        
-#fill the geni table with the records in PL database
-#login_base: indicates the site in PL
-#tablename: the GENI table name
-#type: 'slice' or 'component' indicating the registry type of the GENI table
-def populate_pl_data(login_base, tablename, type):
-    cnx = get_plDB_conn()
-    site_id = cnx.query("SELECT site_id FROM sites WHERE login_base='"+login_base+"';").dictresult()[0]['site_id']
-    
-    if type == 'slice': #slice registry
-        #populate user records
-        querystr = "SELECT p.person_id, p.email FROM persons as p, person_site as ps WHERE p.person_id = ps.person_id AND  ps.site_id = "+str(site_id)
-        users = cnx.query(querystr).dictresult()
-        for user in users:
-            new_hrn = person_to_user(user['email'])
-            existing = cnx.query("SELECT * FROM "+tablename+" WHERE hrn = '"+new_hrn+"'; ").dictresult()
-            if len(existing) > 0:
-                new_hrn = person_to_user(user['email'], 1)
-                existing = cnx.query("SELECT * FROM "+tablename+" WHERE hrn = '"+new_hrn+"'; ").dictresult()
-                if len(existing) > 0:
-                    new_hrn = person_to_user(user['email'], 2)
-            cnx.query("INSERT INTO "+tablename+"(hrn,type,wrapperurl,pointer) VALUES('"+new_hrn+"','user','local',"+str(user['person_id'])+")")
-        #populate slice records
-        querystr = "SELECT slice_id, name FROM slices WHERE site_id = "+str(site_id)
-        slices = cnx.query(querystr).dictresult()
-        for slice in slices:
-            slcname = slice['name'].split('_')
-            if slcname[len(slcname)-1] != 'deleted':
-                new_hrn = plslice_to_slice(slice['name'])
-                existing = cnx.query("SELECT * FROM "+tablename+" WHERE hrn = '"+new_hrn+"'; ").dictresult()
-                if len(existing) > 0:
-                    new_hrn = new_hrn+'-'+str(slice['slice_id'])
-                cnx.query("INSERT INTO "+tablename+"(hrn,type,wrapperurl,pointer) VALUES('"+new_hrn+"','slice','local',"+str(slice['slice_id'])+")")
-                
-    if type == 'component': #component registry
-        #populate node records
-        querystr = "SELECT node_id, hostname FROM nodes WHERE site_id = "+str(site_id)
-        nodes = cnx.query(querystr).dictresult()
-        for node in nodes:
-            new_hrn = plnode_to_node(node['hostname'], 0)
-            existing = cnx.query("SELECT * FROM "+tablename+" WHERE hrn = '"+new_hrn+"'; ").dictresult()
-            if len(existing) > 0:
-                new_hrn = plnode_to_node(node['hostname'], 1)
-                existing = cnx.query("SELECT * FROM "+tablename+" WHERE hrn = '"+new_hrn+"'; ").dictresult()
-                if len(existing) > 0:
-                    new_hrn = plnode_to_node(node['hostname'], 2)
-            cnx.query("INSERT INTO "+tablename+"(hrn,type,wrapperurl,pointer) VALUES('"+new_hrn+"','node','local',"+str(node['node_id'])+")")
-        
diff --git a/util/record.py b/util/record.py
index dfa8d1b8..2d6d7acf 100644
--- a/util/record.py
+++ b/util/record.py
@@ -8,13 +8,37 @@ import report
 from gid import *
 
 ##
-# GeniRecord is a tuple (Name, GID, Type, Info)
-#    info is comprised of the following sub-fields
+# The GeniRecord class implements a Geni Record. A GeniRecord is a tuple
+# (Name, GID, Type, Info).
+#
+# Name specifies the HRN of the object
+# GID is the GID of the object
+# Type is user | sa | ma | slice | component
+#
+# Info is comprised of the following sub-fields
 #        pointer = a pointer to the record in the PL database
 #        pl_info = planetlab-specific info (when talking to client)
 #        geni_info = geni-specific info (when talking to client)
+#
+# The pointer is interpreted depending on the type of the record. For example,
+# if the type=="user", then pointer is assumed to be a person_id that indexes
+# into the persons table.
+#
+# A given HRN may have more than one record, provided that the records are
+# of different types. For example, planetlab.us.arizona may have both an SA
+# and a MA record, but cannot have two SA records.
 
 class GeniRecord():
+
+    ##
+    # Create a Geni Record
+    #
+    # @param name if !=None, assign the name of the record
+    # @param gid if !=None, assign the gid of the record
+    # @param type one of user | sa | ma | slice | component
+    # @param pointer is a pointer to a PLC record
+    # @param dict if !=None, then fill in this record from the dictionary
+
     def __init__(self, name=None, gid=None, type=None, pointer=None, dict=None):
         self.dirty = True
         self.pl_info = None
@@ -37,10 +61,20 @@ class GeniRecord():
             if "geni_info" in dict:
                self.set_geni_info(dict["geni_info"])
 
+    ##
+    # Set the name of the record
+    #
+    # @param name is a string containing the HRN
+
     def set_name(self, name):
         self.name = name
         self.dirty = True
 
+    ##
+    # Set the GID of the record
+    #
+    # @param gid is a GID object or the string representation of a GID object
+
     def set_gid(self, gid):
         if isinstance(gid, str):
             self.gid = gid
@@ -48,53 +82,109 @@ class GeniRecord():
             self.gid = gid.save_to_string(save_parents=True)
         self.dirty = True
 
+    ##
+    # Set the type of the record
+    #
+    # @param type is a string: user | sa | ma | slice | component
+
     def set_type(self, type):
         self.type = type
         self.dirty = True
 
+    ##
+    # Set the pointer of the record
+    #
+    # @param pointer is an integer containing the ID of a PLC record
+
     def set_pointer(self, pointer):
         self.pointer = pointer
         self.dirty = True
 
+    ##
+    # Set the PLC info of the record
+    #
+    # @param pl_info is a dictionary containing planetlab info
+
     def set_pl_info(self, pl_info):
         self.pl_info = pl_info
         self.dirty = True
 
+    ##
+    # Set the geni info the record
+    #
+    # @param geni_info is a dictionary containing geni info
+
     def set_geni_info(self, geni_info):
         self.geni_info = geni_info
         self.dirty = True
 
+    ##
+    # Return the pl_info of the record, or an empty dictionary if none exists
+
     def get_pl_info(self):
         if self.pl_info:
             return self.pl_info
         else:
             return {}
 
+    ##
+    # Return the geni_info of the record, or an empty dictionary if none exists
+
     def get_geni_info(self):
         if self.geni_info:
             return self.geni_info
         else:
             return {}
 
+    ##
+    # Return the name (HRN) of the record
+
     def get_name(self):
         return self.name
 
+    ##
+    # Return the type of the record
+
     def get_type(self):
         return self.type
 
+    ##
+    # Return the pointer of the record. The pointer is an integer that may be
+    # used to look up the record in the PLC database. The evaluation of pointer
+    # depends on the type of the record
+
     def get_pointer(self):
         return self.pointer
 
-    # TODO: not the best name for the function, because we have things called gidObjects in the Cred
+    ##
+    # Return the GID of the record, in the form of a GID object
+    # TODO: not the best name for the function, because we have things called
+    # gidObjects in the Cred
+
     def get_gid_object(self):
         return GID(string=self.gid)
 
+    ##
+    # Return a key that uniquely identifies this record among all records in
+    # Geni. This key is used to uniquely identify the record in the Geni
+    # database.
+
     def get_key(self):
         return self.name + "#" + self.type
 
+    ##
+    # Returns a list of field names in this record. pl_info, geni_info are not
+    # included because they are not part of the record that is stored in the
+    # database, but are rather computed values from other entities
+
     def get_field_names(self):
         return ["name", "gid", "type", "pointer"]
 
+    ##
+    # Given a field name ("name", "gid", ...) return the value of that field.
+    #
+    # @param name is the name of field to be returned
+
     def get_field_value_string(self, fieldname):
         if fieldname == "key":
             val = self.get_key()
@@ -105,12 +195,20 @@ class GeniRecord():
         else:
             return str(val)
 
+    ##
+    # Given a list of field names, return a list of values for those fields.
+    #
+    # @param fieldnames is a list of field names
+
     def get_field_value_strings(self, fieldnames):
         strs = []
         for fieldname in fieldnames:
             strs.append(self.get_field_value_string(fieldname))
         return strs
 
+    ##
+    # Return the record in the form of a dictionary
+
     def as_dict(self):
         dict = {}
         names = self.get_field_names()
@@ -125,6 +223,11 @@ class GeniRecord():
 
         return dict
 
+    ##
+    # Dump the record to stdout
+    #
+    # @param dump_parents if true, then the parents of the GID will be dumped
+
     def dump(self, dump_parents=False):
         print "RECORD", self.name
         print "        hrn:", self.name
diff --git a/util/rights.py b/util/rights.py
index ac5e0fc4..92ab583b 100644
--- a/util/rights.py
+++ b/util/rights.py
@@ -1,10 +1,18 @@
-# rights.py
+##
+# This Module implements rights and lists of rights for the Geni wrapper. Rights
+# are implemented by two classes:
 #
-# support for privileges according to GENI specification
-
-# privilege_table:
+# Right - represents a single right
+#
+# RightList - represents a list of rights
 #
-# a list of priviliges and what operations are allowed per privilege
+# A right may allow several different operations. For example, the "info" right
+# allows "listslices", "listcomponentresources", etc.
+##
+
+##
+# privilege_table is a list of priviliges and what operations are allowed
+# per privilege.
 
 privilege_table = {"authority": ["*"],
                    "refresh": ["remove", "update"],
@@ -16,12 +24,24 @@ privilege_table = {"authority": ["*"],
                    "info": ["listslices", "listcomponentresources", "getsliceresources"],
                    "ma": ["*"]}
 
-# a "Right" is a single privilege.
+##
+# The Right class represents a single privilege.
 
 class Right:
+   ##
+   # Create a new right.
+   #
+   # @param kind is a string naming the right. For example "control"
+
    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".
+
    def can_perform(self, op_name):
       allowed_ops = privilege_table.get(self.kind.lower(), None)
       if not allowed_ops:
@@ -33,6 +53,13 @@ class Right:
 
       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
+
    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)
@@ -46,19 +73,33 @@ class Right:
 
       return True
 
-# a "RightList" is a list of privileges
+##
+# A RightList object represents a list of privileges.
 
 class RightList:
+    ##
+    # Create a new rightlist object, containing no rights.
+    #
+    # @param string if string!=None, load the rightlist from the string
+
     def __init__(self, string=None):
         self.rights = []
         if string:
             self.load_from_string(string)
 
+    ##
+    # Add a right to this list
+    #
+    # @param right is either a Right object or a string describing the right
+
     def add(self, right):
         if isinstance(right, str):
             right = Right(kind = right)
         self.rights.append(right)
 
+    ##
+    # Load the rightlist object from a string
+
     def load_from_string(self, string):
         self.rights = []
 
@@ -70,6 +111,10 @@ class RightList:
         for part in parts:
             self.rights.append(Right(part))
 
+    ##
+    # Save the rightlist object to a string. It is saved in the format of a
+    # comma-separated list.
+
     def save_to_string(self):
         right_names = []
         for right in self.rights:
@@ -77,12 +122,27 @@ class RightList:
 
         return ",".join(right_names)
 
+    ##
+    # Check to see if some right in this list allows an operation. This is
+    # done by evaluating the can_perform function of each operation in the
+    # list.
+    #
+    # @param op_name is an operation to check, for example "listslices"
+
     def can_perform(self, op_name):
         for right in self.rights:
             if right.can_perform(op_name):
                 return True
         return False
 
+    ##
+    # Check to see if all of the rights in this rightlist are a superset
+    # of all the rights in a child rightlist. A rightlist is a superset
+    # if there is no operation in the child rightlist that cannot be
+    # performed in the parent rightlist.
+    #
+    # @param child is a rightlist object describing the child
+
     def is_superset(self, child):
         for child_right in child.rights:
             allowed = False
diff --git a/util/sec/certgen.py b/util/sec/certgen.py
deleted file mode 100755
index d969f27f..00000000
--- a/util/sec/certgen.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#
-# certgen.py
-#
-# Copyright (C) Martin Sjogren and AB Strakt 2001, All rights reserved
-#
-# $Id: certgen.py,v 1.2 2004/07/22 12:01:25 martin Exp $
-#
-"""
-Certificate generation and validation module.
-"""
-
-from OpenSSL import crypto
-import time, calendar, datetime
-
-TYPE_RSA = crypto.TYPE_RSA
-TYPE_DSA = crypto.TYPE_DSA
-
-def createKeyPair(type, bits):
-    """
-    Create a public/private key pair.
-
-    Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA
-               bits - Number of bits to use in the key
-    Returns:   The public/private key pair in a PKey object
-    """
-    pkey = crypto.PKey()
-    pkey.generate_key(type, bits)
-    return pkey
-
-def createCertRequest(pkey, name, digest="md5"):
-    """
-    Create a certificate request.
-
-    Arguments: pkey   - The key to associate with the request
-               digest - Digestion method to use for signing, default is md5
-               **name - The name of the subject of the request, possible
-                        arguments are:
-                          C     - Country name
-                          ST    - State or province name
-                          L     - Locality name
-                          O     - Organization name
-                          OU    - Organizational unit name
-                          CN    - Common name
-                          emailAddress - E-mail address
-    Returns:   The certificate request in an X509Req object
-    """
-    req = crypto.X509Req()
-    subj = req.get_subject()
-    for (key,value) in name.items():
-        setattr(subj, key, value)
-    req.set_pubkey(pkey)
-    req.sign(pkey, digest)
-    return req
-
-def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter), extensions=[], digest="md5"):
-    """
-    Generate a certificate given a certificate request.
-
-    Arguments: req        - Certificate reqeust to use
-               issuerCert - The certificate of the issuer
-               issuerKey  - The private key of the issuer
-               serial     - Serial number for the certificate
-               notBefore  - Timestamp (relative to now) when the certificate
-                            starts being valid
-               notAfter   - Timestamp (relative to now) when the certificate
-                            stops being valid
-               digest     - Digest method to use for signing, default is md5
-    Returns:   The signed certificate in an X509 object
-    """
-    cert = crypto.X509()
-    cert.set_serial_number(serial)
-    cert.gmtime_adj_notBefore(notBefore)
-    cert.gmtime_adj_notAfter(notAfter)
-    cert.set_issuer(issuerCert.get_subject())
-    cert.set_subject(req.get_subject())
-    cert.set_pubkey(req.get_pubkey())
-    if extensions:
-        extList = []
-        for name, critical, value in extensions:
-            ext = crypto.X509Extension (name, critical, value)
-            extList.append(ext)
-        cert.add_extensions(extList)
-    cert.sign(issuerKey, digest)
-    return cert
-
-
-#checks if a certificate is valid in terms of validity periods    
-def check_valid(usercert):
-    """
-    Method that ensures the issuer cert has
-    valid, not_before and not_after fields
-    """
-    valid = True
-    before_time = usercert.get_not_before()
-    after_time = usercert.get_not_after()
-    before_tuple = time.strptime(str(before_time), "%b %d %H:%M:%S %Y %Z")
-    after_tuple = time.strptime(str(after_time), "%b %d %H:%M:%S %Y %Z")
-    starts =  datetime.timedelta(seconds=calendar.timegm(before_tuple))
-    expires = datetime.timedelta(seconds=calendar.timegm(after_tuple))
-    now = datetime.timedelta(seconds=time.time())
-    time_delta = expires - now
-    
-    #cert has expired
-    if time_delta.days < 0:
-        valid = False
-    #cert is not yet valid
-    time_delta = now - starts
-    if time_delta.days < 0:   
-        valid = False
-
-    return valid
diff --git a/util/sec/certgen.pyc b/util/sec/certgen.pyc
deleted file mode 100644
index 4bfb1aea183a6538282c3c99f153c7f0dbabe17a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 4119
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