#!/usr/local/bin/python import sys import re import os import os.path import subprocess from string import join from optparse import OptionParser from federation.util import abac_pem_type, abac_split_cert class Parser(OptionParser): def __init__(self): OptionParser.__init__(self) 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('--dir', dest='dir', default=None, help='Output directory for credentials') self.add_option('--make-dir', action='store_true', dest='make_dir', default=False, help='Create the --dir directory') self.add_option('--debug', action='store_true', dest='debug', default=False, help='Just print the creddy commands') class identity: def __init__(self, name, roles=None): self.name = name if roles: self.roles = set(roles) def add_roles(self, roles): self.roles |= set(roles) def __str__(self): return "%s: %s" % (self.name, join(self.roles, ', ')) comment_re = re.compile('^\s*#|^$') fedid_str = 'fedid:([0-9a-fA-F]{40})' id_str = '[a-zA-Z][\w_-]*' single_re = re.compile('\s*%s\s*->\s*(%s)' % (fedid_str, id_str)) double_re = re.compile('\s*%s\s*->\s*\((%s)\s*,\s*(%s)\)' % \ (fedid_str, id_str, id_str)) bad_role = re.compile('[^a-zA-Z0-9_]+') parser = Parser() opts, args = parser.parse_args() cert, key = None, None delete_certs = False if opts.key: if os.access(opts.key, os.R_OK): key = opts.key else: sys.exit('Cannot read %s (key file)' % opts.key) if opts.dir: if opts.make_dir: if not os.path.isdir(opts.dir) and not debug: try: os.mkdir(opts.dir, 0755) except EnvironmentError, e: sys.exit('Could not make %s: %s' % (opts.dir, e)) else: if not os.path.isdir(opts.dir): sys.exit('%s is not a directory' % opts.dir) elif not os.access(opts.dir, os.W_OK): sys.exit('%s is not writable' % opts.dir) if opts.cert: if os.access(opts.cert, os.R_OK): if not key: if abac_pem_type(opts.cert) == 'both': key, cert = abac_split_cert(opts.cert) delete_certs = True else: cert = opts.cert else: sys.exit('Cannot read %s (certificate file)' % opts.cert) if any([ x is None for x in (cert, opts.dir, key)]): print >>sys.stderr, "Need output dir, certificate and key to make creds" print >>sys.stderr, "Reverting to debug mode" debug = True else: debug = opts.debug roles = { } try: for fn in args: try: f = open(fn, "r") for l in f: id = None for r in (comment_re, single_re, double_re): m = r.match(l) if m: if m.groups(): g = m.groups() id = g[0] r = [ bad_role.sub('_', x) for x in g[1:] ] break else: print 'Unmatched line: %s' % l if id: # New and create are implicit. >sigh< r.extend(('new', 'create')) if id in roles: roles[id].add_roles(r) else: roles[id] = identity(r[0], r) except EnvironmentError, e: print >>sys.stderr, 'Cannot open file (%s): %s' % \ (e.filename, e.strerror) if not roles: print >>sys.stderr, "No roles found. Did you specify a configuration?" for k, id in roles.items(): for i, r in enumerate(id.roles): cmd = ['creddy', '--attribute', '--issuer=%s' % (cert or 'cert_file'), '--key=%s' % (key or 'key_file'), '--role=%s' % r, '--subject-id=%s' % k, '--out=%s/%s%03d_attr.der' % \ (opts.dir or 'new_cert_dir', id.name, i)] if debug: print join(cmd) else: rv = subprocess.call(cmd) if rv != 0: sys.exit('%s failed: %d' % (join(cmd), rv)) finally: if delete_certs: if cert: os.unlink(cert) if key: os.unlink(key)