#!/usr/local/bin/python import sys, os import re from federation.fedid import fedid from optparse import OptionParser, OptionValueError class attribute: def __init__(self, p, a): self.principal = p self.attr = a def __str__(self): return "%s.%s" % (self.principal, self.attr) class credential: 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.req = req def __str__(self): if isinstance(self.req, (tuple, list, set)): return "%s.%s <- %s" % (self.principal, self.attr, " & ".join(["%s" % r for r in self.req])) else: return "%s.%s <- %s" % (self.principal, self.attr, self.req) class parse_error(RuntimeError): pass def parse_emulab(l, creds, me, to_id, p, gp, gu): right_side_str = '\s*,\s*\(\s*%s\s*,\s*%s\s*,\s*%s\s*\)' % \ (id_same_str, id_same_str,id_same_str) m = re.match(right_side_str, l) if m: project, user = m.group(1,2) if project == '': if gp is not None: project = gp else: raise parse_error("Project cannot be decisively mapped: %s" % l) return False if user == '': if gu is not None: project = gu else: raise parse_error("User cannot be decisively mapped: %s" % l) return False if project and user: a = 'project_%s_user_%s' % (project, user) elif project: a = 'project_%s' % project elif user: a = 'user_%s' % user else: raise parse_error("No mapping for %s/%s!?" % (gp, gu)) return False c = credential(me, a, [attribute(p, x) for x in (gp, gu) if x is not None]) creds.add(c) if a in to_id: to_id[a].append(c) else: to_id[a] = [ c ] return True else: raise parse_error("Badly formatted local mapping: %s" % l) return False def parse_dragon(l, creds, me, to_id, p, gp, gu): right_side_str = '\s*,\s*\(\s*(%s)\s*\)' % \ (id_str) m = re.match(right_side_str, l) if m: repo= m.group(1) c = credential(me, 'repo_%s' % repo, [attribute(p, x) for x in (gp, gu) if x is not None]) creds.add(c) if repo in to_id: to_id[repo].append(c) else: to_id[repo] = [ c ] return True else: raise parse_error("Badly formatted local mapping: %s" % l) return False class access_opts(OptionParser): mappers = { 'emulab': parse_emulab, 'dragon': parse_dragon } @staticmethod def parse_mapper(opt, s, val, parser, dest): if val in access_opts.mappers: setattr(parser.values, dest, access_opts.mappers[val]) else: raise OptionValueError('%s must be one of %s' % \ (s, ", ".join(access_opts.mappers.keys()))) def __init__(self): OptionParser.__init__(self, usage='%prog [opts] file [...]') self.add_option('--cert', dest='cert', default=None, help='my fedid as an X.509 certificate') self.add_option('--key', dest='key', default=None, help='key for the certificate') self.add_option('--type', action='callback', nargs=1, type='str', callback=access_opts.parse_mapper, callback_kwargs = { 'dest': 'mapper'}, help='Type of access file to parse. One of %s. ' %\ ", ".join(access_opts.mappers.keys()) + \ 'Omit for generic parsing.') self.set_defaults(mapper=None) comment_re = re.compile('^\s*#|^$') fedid_str = 'fedid:([0-9a-fA-F]{40})' id_str = '[a-zA-Z][\w-]*' id_any_str = '(%s|)' % id_str id_same_str = '(%s|)' % id_str left_side_str = '\(\s*%s\s*,\s*%s\s*,\s*%s\s*\)' % \ (fedid_str, id_any_str, id_any_str) right_side_str = '(%s)(\s*,\s*\(.*\))?' % (id_str) line_re = re.compile('%s\s*->\s*%s' % (left_side_str, right_side_str)) p = access_opts() opts, args = p.parse_args() if len(args) < 1: sys.exit('No filenames given to parse') if opts.cert: try: me = fedid(file=opts.cert) except EnvironmentError, e: sys.exit('Bad --cert: %s (%s)' % (e.strerror, e.filename or '?!')) else: print >>sys.stderr, 'No --cert, using dummy fedid' me = fedid(hexstr='0123456789012345678901234567890123456789') mapper = opts.mapper for fn in args: creds = set() to_id = { } try: f = open(fn, "r") for i, l in enumerate(f): try: if comment_re.match(l): continue else: m = line_re.match(l) if m: p, da = m.group(1, 4) gp, gu = m.group(2, 3) if gp == '': gp = None if gu == '': gu = None creds.add(credential(me, da, [attribute(p, x) 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) else: raise parse_error('Syntax error') except parse_error, e: raise parse_error('Error on line %d of %s: %s' % \ (i, fn, e.message)) f.close() except parse_error, e: print >> sys.stderr, "%s" % e continue except EnvironmentError, e: print >>sys.stderr, "File error %s: %s" % \ (e.filename or '!?', e.strerror) continue for c in creds: print "%s" % c for k, c in to_id.items(): print "%s: %s" % ( k , ", ".join(set(["%s.%s" % (x.principal, x.attr) \ for x in c])))