Index: fedd/access_to_abac.py
===================================================================
--- fedd/access_to_abac.py	(revision af25848db7d28b68368af99043ebf63f7121c43e)
+++ fedd/access_to_abac.py	(revision 87807f428463fbb11fe1c89939a8c9edf39dd0aa)
@@ -3,18 +3,26 @@
 import sys, os
 import re
-
+import subprocess
+
+from string import join
 from federation.fedid import fedid
 from optparse import OptionParser, OptionValueError
 
+
 class attribute:
     '''
-    Encapculate a principal/attribute pair.
-    '''
-    def __init__(self, p, a):
+    Encapculate a principal/attribute/link tuple.
+    '''
+    bad_attr = re.compile('[^a-zA-Z0-9_]+')
+    def __init__(self, p, a, l=None):
 	self.principal = p
-	self.attr = a
+	self.attr = attribute.bad_attr.sub('_', a)
+	if l: self.link = attribute.bad_attr.sub('_', l)
+	else: self.link = None
 
     def __str__(self):
-	if self.attr:
+	if self.link:
+	    return "%s.%s.%s" % (self.principal, self.attr, self.link)
+	elif self.attr:
 	    return "%s.%s" % (self.principal, self.attr)
 	else:
@@ -27,10 +35,11 @@
     an intersection/conjunction.
     '''
+    bad_attr = re.compile('[^a-zA-Z0-9_]+')
     def __init__(self, p, a, req):
 	self.principal = p
 	if isinstance(a, (tuple, list, set)) and len(a) == 1:
-	    self.attr = a[0]
-	else:
-	    self.attr = a
+	    self.attr = credential.bad_attr.sub('_', a[0])
+	else:
+	    self.attr = credential.bad_attr.sub('_', a)
 	self.req = req
 
@@ -38,5 +47,5 @@
 	if isinstance(self.req, (tuple, list, set)):
 	    return "%s.%s <- %s" % (self.principal, self.attr, 
-		    " & ".join(["%s" % r for r in self.req]))
+		    join(["%s" % r for r in self.req], ' & '))
 	else:
 	    return "%s.%s <- %s" % (self.principal, self.attr, self.req)
@@ -45,4 +54,7 @@
 # a parsing problem.
 class parse_error(RuntimeError): pass
+
+# Error creating a credential
+class credential_error(RuntimeError): pass
 
 # Functions to parse the individual access maps as well as an overall function
@@ -56,5 +68,5 @@
 #  to ABAC credentials that are required to exercise those local rights and the
 #  three-name (p, gp, gu) that is being mapped.
-def parse_emulab(l, creds, me, to_id, p, gp, gu):
+def parse_emulab(l, creds, me, to_id, p, gp, gu, lr):
     '''
     Parse the emulab (project, allocation_user, access_user) format.  Access
@@ -93,5 +105,5 @@
 	# Store the creds and map entries
 	c = credential(me, a, 
-		[attribute(p, x) for x in (gp, gu) if x is not None])
+		[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)
@@ -101,5 +113,5 @@
 
 
-def parse_protogeni(l, creds, me, to_id, p, gp, gu):
+def parse_protogeni(l, creds, me, to_id, p, gp, gu, lr):
     '''
     Parse the protoGENI (cert, user, user_key, cert_pw) format.
@@ -119,5 +131,5 @@
 	# Store em
 	c = credential(me, a, 
-		[attribute(p, x) for x in (gp, gu) if x is not None])
+		[attribute(p, x, lr) for x in (gp, gu) if x is not None])
 	creds.add(c)
 	if (cert, user, key, pw) in to_id: 
@@ -128,5 +140,5 @@
 	raise parse_error("Badly formatted local mapping: %s" % l)
 
-def parse_dragon(l, creds, me, to_id, p, gp, gu):
+def parse_dragon(l, creds, me, to_id, p, gp, gu, lr):
     '''
     Parse the dragon (repository_name) version.
@@ -139,5 +151,5 @@
 	repo= m.group(1)
 	c = credential(me, 'repo_%s' % repo, 
-		[attribute(p, x) for x in (gp, gu) if x is not None])
+		[attribute(p, x, lr) for x in (gp, gu) if x is not None])
 	creds.add(c)
 	if repo in to_id: to_id[repo].append(c)
@@ -146,5 +158,5 @@
 	raise parse_error("Badly formatted local mapping: %s" % l)
 
-def parse_skel(l, creds, me, to_id, p, gp, gu):
+def parse_skel(l, creds, me, to_id, p, gp, gu, lr):
     '''
     Parse the skeleton (local_attr) version.
@@ -157,5 +169,5 @@
 	lattr = m.group(1)
 	c = credential(me, 'lattr_%s' % lattr, 
-		[attribute(p, x) for x in (gp, gu) if x is not None])
+		[attribute(p, x, lr) for x in (gp, gu) if x is not None])
 	creds.add(c)
 	if lattr in to_id: to_id[lattr].append(c)
@@ -165,5 +177,5 @@
 
 # internal plug-ins have no local attributes.
-def parse_internal(l, creds, me, to_id, p, gp, gu): pass
+def parse_internal(l, creds, me, to_id, p, gp, gu, lr): pass
 
 
@@ -188,5 +200,5 @@
 	else:
 	    raise OptionValueError('%s must be one of %s' % \
-		    (s, ", ".join(access_opts.mappers.keys())))
+		    (s, join(access_opts.mappers.keys(), ', ')))
 
     def __init__(self):
@@ -202,5 +214,5 @@
 		callback_kwargs = { 'dest': 'mapper'}, 
 		help='Type of access file to parse.  One of %s. ' %\
-			", ".join(access_opts.mappers.keys()) + \
+			join(access_opts.mappers.keys(), ', ') + \
 			'Omit for generic parsing.')
 	self.add_option('--quiet', dest='quiet', action='store_true', 
@@ -211,7 +223,19 @@
 		help='create credentials for rules.  Requires ' + \
 			'--cert, --key, and --dir to be given.')
+	self.add_option('--file', dest='file', default=None,
+		help='Access DB to parse.  If this is present, ' + \
+			'omit the positional filename')
+	self.add_option('--mapfile', dest='map', default=None,
+		help='File for the attribute to local authorization data')
+	self.add_option('--no-delegate', action='store_false', dest='delegate',
+		default=True,
+		help='do not accept delegated attributes with the ' +\
+			'acting_for linking role')
+	self.add_option('--debug', action='store_true', dest='debug', 
+		default=False, help='Just print actions')
 	self.set_defaults(mapper=None)
 
-def create_creds(creds, cert, key, dir, creddy='/usr/local/bin/creddy'):
+def create_creds(creds, cert, key, dir, debug=False, 
+	creddy='/usr/local/bin/creddy'):
     '''
     Make the creddy calls to create the attributes from the list of credential
@@ -223,5 +247,9 @@
 	parameters
 	'''
-	if r.principal and r.attr:
+	if r.principal and r.link and r.attr:
+	    return ['--subject-id=%s' % r.principal, 
+		    '--subject-role=%s.%s' % (r.attr, r.link),
+		    ]
+	elif r.principal and r.attr:
 	    return ['--subject-id=%s' % r.principal, 
 		    '--subject-role=%s' %r.attr]
@@ -237,5 +265,10 @@
 	for r in c.req:
 	    cmd.extend(attrs(r))
-	print " ".join(cmd)
+	if debug:
+	    print join(cmd)
+	else:
+	    rv = subprocess.call(cmd)
+	    if rv != 0:
+		raise credential_error("%s: %d" % (join(cmd), rv))
 
 # Regular expressions and parts thereof for parsing
@@ -254,4 +287,7 @@
 opts, args = p.parse_args()
 
+if opts.file:
+    args.append(opts.file)
+
 # Validate arguments
 if len(args) < 1:
@@ -275,4 +311,7 @@
     elif not os.access(opts.dir, os.W_OK):
 	sys.exit('%s is not writable' % opts.dir)
+
+if opts.delegate: delegation_link = 'acting_for'
+else: delegation_link = None
 
 mapper = opts.mapper
@@ -297,8 +336,10 @@
 
 			creds.add(credential(me, da, 
-				[attribute(p, x) for x in (gp, gu) \
-				    if x is not None]))
+				[attribute(p, x, delegation_link) \
+					for x in (gp, gu) \
+					    if x is not None]))
 			if m.group(5) and mapper:
-			    mapper(m.group(5), creds, me, to_id, p, gp, gu)
+			    mapper(m.group(5), creds, me, to_id, p, gp, gu,
+				    delegation_link)
 		    else:
 			raise parse_error('Syntax error')
@@ -321,12 +362,22 @@
     if opts.create_creds:
 	if all([opts.cert, opts.key, opts.dir]):
-	    create_creds([c for c in creds if c.principal == me],
-		    opts.cert, opts.key, opts.dir)
+	    try:
+		create_creds([c for c in creds if c.principal == me],
+			opts.cert, opts.key, opts.dir, opts.debug)
+	    except credential_error, e:
+		sys.exit('Credential creation failed: %s' % e)
 	else:
 	    print >>sys.stderr, 'Cannot create credentials.  Missing parameter'
 
     # Local map output
-    if not opts.quiet:
-	for k, c in to_id.items():
-	    for a in set(["%s.%s" % (x.principal, x.attr) for x in c]):
-		print "%s -> (%s)" % ( a, ", ".join(k))
+    if opts.map or opts.debug:
+	try:
+	    if opts.map and opts.map != '-' and not opts.debug:
+		f = open(opts.map, 'w')
+	    else:
+		f = sys.stdout
+	    for k, c in to_id.items():
+		for a in set(["%s.%s" % (x.principal, x.attr) for x in c]):
+		    print >>f, "%s -> (%s)" % ( a, join(k, ', '))
+	except EnvironmentError, e:
+	    sys.exit("Cannot open %s: %s" % (e.filename or '!?', e.strerror))
