Index: fedd/access_to_abac.py
===================================================================
--- fedd/access_to_abac.py	(revision ee950c2260d0e0d90dc0a6a4d78740b92459415b)
+++ fedd/access_to_abac.py	(revision 3ce6b208613eee2da17b5fb77a2537249e74f2f4)
@@ -71,5 +71,5 @@
 #  All the local parsing functions get the unparsed remainder of the line
 #  (after the three-name and the attribute it maps to), the credential list to
-#  add the new ABAC credential(s) that will be mapped into the loacl
+#  add the new ABAC credential(s) that will be mapped into the local
 #  credentials, the fedid of this entity, a dict mapping the local credentials
 #  to ABAC credentials that are required to exercise those local rights and the
@@ -77,14 +77,12 @@
 def parse_emulab(l, creds, me, to_id, p, gp, gu, lr):
     '''
-    Parse the emulab (project, allocation_user, access_user) format.  Access
-    users are deprecates and allocation users used for both.  This fuction
-    collapses them.
-    '''
-    right_side_str = '\s*,\s*\(\s*%s\s*,\s*%s\s*,\s*%s\s*\)' % \
-	    (proj_same_str, id_same_str,id_same_str)
+    Parse the emulab (project, allocation_user, cert_file) format. 
+    '''
+    right_side_str = '\s*,\s*\(\s*%s\s*,\s*%s\s*,\s*(%s)\s*\)' % \
+	    (proj_same_str, id_same_str,path_str)
 
     m = re.match(right_side_str, l)
     if m:
-	project, user = m.group(1,2)
+	project, user, cert = m.group(1,2,3)
 	# Resolve "<same>"s in project and user
 	if project == '<same>':
@@ -114,6 +112,6 @@
 		[attribute(p, x, lr) for x in (gp, gu) if x is not None])
 	creds.add(c)
-	if (project, user) in to_id: to_id[(project,user)].append(c)
-	else: to_id[(project,user)] = [ c ]
+	if (project, user,cert) in to_id: to_id[(project,user,cert)].append(c)
+	else: to_id[(project,user,cert)] = [ c ]
     else:
 	raise parse_error("Badly formatted local mapping: %s" % l)
@@ -320,5 +318,5 @@
 id_str = '[a-zA-Z][\w_-]*'
 proj_str = '[a-zA-Z][\w_/-]*'
-path_str = '[a-zA-Z_/\.-]+'
+path_str = '[a-zA-Z0-9_/\.-]+'
 id_any_str = '(%s|<any>)' % id_str
 proj_any_str = '(%s|<any>)' % proj_str
Index: fedd/db_to_certs.py
===================================================================
--- fedd/db_to_certs.py	(revision 3ce6b208613eee2da17b5fb77a2537249e74f2f4)
+++ fedd/db_to_certs.py	(revision 3ce6b208613eee2da17b5fb77a2537249e74f2f4)
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+import os, sys
+import MySQLdb
+import tempfile
+import stat
+from optparse import OptionParser
+
+from federation import fedid
+
+class opt_parser(OptionParser):
+    def __init__(self):
+	OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
+		version="0.1")
+	self.add_option('-u', '--user', dest='users', action='append',
+		default=[], help="Users to extract from DB")
+	self.add_option('-p', '--project', dest='projects', action='append',
+		default=[], help="Projects to extract from DB")
+	self.add_option('-d', '--cert-dir', dest='cert_dir', 
+		default=None, help='Directory to store copies of certificates')
+
+
+def cert_to_fid(cstr):
+    fd, path = tempfile.mkstemp('.pem')
+    try:
+	try:
+	    f = os.fdopen(fd, "w")
+	    print >>f, cstr
+	    f.close()
+	except IOError, e:
+	    print >>sys.stderr, "Error creating user %s" % u
+	return fedid(file=path)
+    finally:
+	os.remove(path)
+
+
+def add_list(l, field):
+    str = ""
+    for x in l:
+	if str: str += " OR "
+	else: str = " AND ("
+
+	str += "%s='%s'" % (field, x)
+    if str: str += ")"
+    return str
+
+
+fids = { }
+q_start = """
+SELECT
+    g.uid, 
+    CASE g.gid
+	WHEN g.pid THEN g.pid
+	ELSE CONCAT(g.pid, '/', g.gid)
+    END,
+    CONCAT('-----BEGIN RSA PRIVATE KEY-----\\n',
+    s.privkey,
+    '-----END RSA PRIVATE KEY-----\\n',
+    '-----BEGIN CERTIFICATE-----\\n',
+	s.cert,
+	'-----END CERTIFICATE-----\\n'),
+    encrypted
+FROM group_membership g INNER JOIN user_sslcerts s
+    ON g.uid = s.uid
+WHERE revoked is NULL AND encrypted = 0
+"""
+q_end ="""
+ORDER BY s.uid
+"""
+
+opts, args = opt_parser().parse_args()
+
+if opts.cert_dir:
+    if not os.path.isdir(opts.cert_dir):
+	sys.exit('Not a directory: %s' % opts.cert_dir)
+    elif not os.access(opts.cert_dir, os.W_OK):
+	sys.exit('Cannot write %s' % opts.cert_dir)
+    else:
+	os.chmod(opts.cert_dir, stat.S_IRWXU)
+
+
+
+user_clause= add_list(opts.users, 'g.uid')
+project_clause= add_list(opts.projects, 'g.pid')
+
+query = q_start + user_clause + project_clause + q_end
+
+db = MySQLdb.connect(db='tbdb')
+c = db.cursor()
+c.execute(query)
+
+for u, p, c, e in c.fetchall():
+    fid = fids.get(c, None)
+
+    if not fid:
+	fid = cert_to_fid(c)
+	fids[c] = fid
+	if opts.cert_dir:
+	    cf =os.path.join(opts.cert_dir, '%s.pem' % u)
+	    if not os.access(cf, os.F_OK) or os.access(cf, os.W_OK):
+		try:
+		    f = open(cf, 'w')
+		    f.write(c)
+		    f.close()
+		    os.chmod(cf, stat.S_IREAD)
+		except EnvironmentError, e:
+		    print >>sys.stderr, 'Cannot write %s: %s' % \
+			    (e.filename, e.strerror)
Index: fedd/deter_exp_access_db.py
===================================================================
--- fedd/deter_exp_access_db.py	(revision 3ce6b208613eee2da17b5fb77a2537249e74f2f4)
+++ fedd/deter_exp_access_db.py	(revision 3ce6b208613eee2da17b5fb77a2537249e74f2f4)
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+import os, sys
+import MySQLdb
+import tempfile
+from optparse import OptionParser
+
+from federation import fedid
+
+class opt_parser(OptionParser):
+    def __init__(self):
+	OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
+		version="0.1")
+	self.add_option('-u', '--user', dest='users', action='append',
+		default=[], help="Users to extract from DB")
+	self.add_option('-p', '--project', dest='projects', action='append',
+		default=[], help="Projects to extract from DB")
+	self.add_option('-d', '--cert-dir', dest='cert_dir', 
+		default=None, help='Directory to store copies of certificates')
+	self.add_option('-t','--testbed', dest='tb', default=None,
+		help='testbed from which attrs come (cert file)')
+
+def cert_to_fid(cstr):
+    fd, path = tempfile.mkstemp('.pem')
+    try:
+	try:
+	    f = os.fdopen(fd, "w")
+	    print >>f, cstr
+	    f.close()
+	except IOError, e:
+	    print >>sys.stderr, "Error creating user %s" % u
+	return fedid(file=path)
+    finally:
+	os.remove(path)
+
+
+def add_list(l, field, prefix=''):
+    str = ""
+    for x in l:
+	if str: str += " OR "
+	else: str = " %s (" % prefix
+
+	str += "%s='%s'" % (field, x)
+    if str: str += ")"
+    return str
+
+
+fids = { }
+q_start = """
+SELECT
+    g.uid, 
+    CASE g.gid
+	WHEN g.pid THEN g.pid
+	ELSE CONCAT(g.pid, '/', g.gid)
+    END
+FROM group_membership g 
+"""
+q_end ="""
+ORDER BY g.uid
+"""
+
+opts, args = opt_parser().parse_args()
+
+if opts.users or opts.projects: q_start += '\nWHERE '
+user_clause= add_list(opts.users, 'g.uid')
+if user_clause: prefix = 'AND'
+else: prefix = ''
+project_clause= add_list(opts.projects, 'g.pid', prefix)
+
+if opts.tb:
+    try:
+	tbc=fedid(file=opts.tb)
+    except EnvironmentError, e:
+	sys.exit('Cannot get testbed cert from %s: %s' % \
+		(e.filename, e.strerror))
+else:
+    sys.exit('need a testbed (--testbed)')
+
+query = q_start + user_clause + project_clause + q_end
+
+db = MySQLdb.connect(db='tbdb')
+c = db.cursor()
+c.execute(query)
+
+for u, p, in c.fetchall():
+    print "(fedid:%s,%s,%s)-> access, (%s,%s,%s)" % \
+	    (tbc, p, u, p, u, os.path.join(opts.cert_dir, "%s.pem" % u))
Index: fedd/federation/emulab_access.py
===================================================================
--- fedd/federation/emulab_access.py	(revision ee950c2260d0e0d90dc0a6a4d78740b92459415b)
+++ fedd/federation/emulab_access.py	(revision 3ce6b208613eee2da17b5fb77a2537249e74f2f4)
@@ -176,21 +176,21 @@
     def access_tuple(str):
 	"""
-	Convert a string of the form (id, id) into an access_project.  This is
-	called by read_access to convert to local attributes.  It returns
-	a tuple of the form (project, user).
+	Convert a string of the form (id, id, id) into an access_project.  This
+	is called by read_access to convert to local attributes.  It returns a
+	tuple of the form (project, user, certificate_file).
 	"""
 
 	str = str.strip()
-	if str.startswith('(') and str.endswith(')') and str.count(',') == 1:
+	if str.startswith('(') and str.endswith(')') and str.count(',') == 2:
 	    # The slice takes the parens off the string.
-	    proj, user = str[1:-1].split(',')
-	    return (proj.strip(), user.strip())
+	    proj, user, cert = str[1:-1].split(',')
+	    return (proj.strip(), user.strip(), cert.strip())
 	else:
 	    raise self.parse_error(
-		    'Bad mapping (unbalanced parens or more than 1 comma)')
+		    'Bad mapping (unbalanced parens or more than 2 commas)')
 
     # RequestAccess support routines
 
-    def save_project_state(self, aid, pname, uname, owners):
+    def save_project_state(self, aid, pname, uname, certf, owners):
 	"""
 	Save the project, user, and owners associated with this allocation.
@@ -201,4 +201,5 @@
 	self.allocation[aid]['project'] = pname
 	self.allocation[aid]['user'] = uname
+	self.allocation[aid]['cert'] = certf
 	self.allocation[aid]['owners'] = owners
 	self.write_state()
@@ -272,5 +273,6 @@
 	aid = unicode(allocID)
 
-	pname, uname = self.save_project_state(aid, found[0], found[1], owners)
+	pname, uname = self.save_project_state(aid, found[0], found[1], 
+		found[2], owners)
 
 	services, svc_state = self.export_services(req.get('service',[]),
@@ -690,4 +692,5 @@
 	if aid in self.allocation:
 	    user = self.allocation[aid].get('user', None)
+	    cert = self.allocation[aid].get('cert', None)
 	    self.allocation[aid]['experiment'] = ename
 	    self.allocation[aid]['nonce'] = nonce_experiment
@@ -711,5 +714,5 @@
 		    "Can't find creation user for %s" %aid)
 
-	return (ename, proj, user, pubkey_base, secretkey_base, alloc_log)
+	return (ename, proj, user, cert, pubkey_base, secretkey_base, alloc_log)
 
     def decorate_topology(self, info, t):
@@ -853,7 +856,7 @@
 	try:
 	    self.retrieve_software(topo, certfile, softdir)
-	    ename, proj, user, pubkey_base, secretkey_base, alloc_log = \
-		    self.initialize_experiment_info(attrs, aid, 
-			    certfile, tmpdir)
+	    ename, proj, user, xmlrpc_cert, pubkey_base, secretkey_base, \
+		alloc_log =  self.initialize_experiment_info(attrs, aid, 
+			certfile, tmpdir)
 
 	    if '/' in proj: proj, gid = proj.split('/')
@@ -877,5 +880,5 @@
 	    starter = self.start_segment(keyfile=self.ssh_privkey_file, 
 		    debug=self.create_debug, log=alloc_log, boss=self.boss,
-		    ops=self.ops, cert=self.xmlrpc_cert)
+		    ops=self.ops, cert=xmlrpc_cert)
 	    rv = starter(self, ename, proj, user, expfile, tmpdir, gid=gid)
 	except service_error, e:
@@ -918,4 +921,5 @@
 	    proj = self.allocation[aid].get('project', None)
 	    user = self.allocation[aid].get('user', None)
+	    xmlrpc_cert = self.allocation[aid].get('cert', None)
 	    ename = self.allocation[aid].get('experiment', None)
 	    nonce = self.allocation[aid].get('nonce', False)
@@ -925,4 +929,5 @@
 	    ename = None
 	    nonce = False
+	    xmlrpc_cert = None
 	self.state_lock.release()
 
@@ -942,5 +947,5 @@
 	stopper = self.stop_segment(keyfile=self.ssh_privkey_file,
 		debug=self.create_debug, boss=self.boss, ops=self.ops,
-		cert=self.xmlrpc_cert)
+		cert=xmlrpc_cert)
 	stopper(self, user, proj, ename, gid, nonce)
 	return { 'allocID': req['allocID'], 'proof': proof.to_dict() }
@@ -966,4 +971,5 @@
 	    proj = self.allocation[aid].get('project', None)
 	    user = self.allocation[aid].get('user', None)
+	    xmlrpc_cert = self.allocation[aid].get('cert', None)
 	    ename = self.allocation[aid].get('experiment', None)
 	else:
@@ -972,4 +978,5 @@
 	    ename = None
 	    topo = None
+	    xmlrpc_cert = None
 	self.state_lock.release()
 
@@ -989,5 +996,5 @@
 	info = self.info_segment(keyfile=self.ssh_privkey_file,
 		debug=self.create_debug, boss=self.boss, ops=self.ops,
-		cert=self.xmlrpc_cert)
+		cert=xmlrpc_cert)
 	info(self, user, proj, ename)
 	self.decorate_topology(info, topo)
@@ -1038,9 +1045,13 @@
 	    raise service_error(service_error.req, "no targets")
 
+	self.state_lock.acquire()
 	if aid in self.allocation:
 	    topo = self.allocation[aid].get('topo', None)
 	    if topo: topo = topo.clone()
+	    xmlrpc_cert = self.allocation[aid].get('cert', None)
 	else:
 	    topo = None
+	    xmlrpc_cert = None
+	self.state_lock.release()
 
 	targets = copy.copy(targets)
@@ -1058,5 +1069,5 @@
 	ops = self.operation_segment(keyfile=self.ssh_privkey_file,
 		debug=self.create_debug, boss=self.boss, ops=self.ops,
-		cert=self.xmlrpc_cert)
+		cert=xmlrpc_cert)
 	ops(self, op, ptargets, params, topo)
 	
