source: fedd/fedd_to_abac.py @ e65150a

axis_examplecompt_changesinfo-ops
Last change on this file since e65150a was e65150a, checked in by Ted Faber <faber@…>, 14 years ago

Make common ops use fewer options. Refactor.

  • Property mode set to 100755
File size: 6.0 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4import re
5
6import os
7import os.path
8import subprocess
9from tempfile import mkdtemp
10
11from string import join
12
13from federation.authorizer import abac_authorizer
14from federation.util import abac_pem_type, abac_split_cert, file_expanding_opts
15
16class Parser(file_expanding_opts):
17    def __init__(self):
18        file_expanding_opts.__init__(self)
19        self.add_option('--cert', dest='cert', default=None,
20                action='callback', callback=self.expand_file, type='str',
21                help='my fedid as an X.509 certificate')
22        self.add_option('--key', dest='key', default=None,
23                action='callback', callback=self.expand_file, type='str',
24                help='key for the certificate')
25        self.add_option('--dir', dest='dir', default=None,
26                action='callback', callback=self.expand_file, type='str',
27                help='Output directory for credentials')
28        self.add_option('--make-dir', action='store_true', dest='make_dir',
29                default=False, help='Create the --dir directory')
30        self.add_option('--debug', action='store_true', dest='debug',
31                default=False, help='Just print the creddy commands')
32        self.add_option('--policy_only', action='store_const', const=False,
33                dest='make_authorizer', default=True, 
34                help='Only create the directory of certs, " + \
35                        "do not create an authorizer')
36        self.add_option('--update', action='store_const', const=True,
37                dest='update_authorizer', default=False, 
38                help='Add the generated policy to an existing authorizer')
39
40class identity:
41    def __init__(self, name, roles=None):
42        self.name = name
43        if roles: self.roles = set(roles)
44
45    def add_roles(self, roles):
46        self.roles |= set(roles)
47
48    def __str__(self):
49        return "%s: %s" % (self.name, join(self.roles, ', '))
50
51def clear_dir(dir):
52    for path, dirs, files in os.walk(dir, topdown=False):
53        for f in files: os.unlink(os.path.join(path, f))
54        for d in dirs: os.rmdir(os.path.join(path, d))
55
56def parse_configs(files):
57    """
58    Step through each file pulling the roles out of the database lines, if any,
59    and creating (or appending to) identity objects, one identity in a dict for
60    each  fedid.  Return that dict.  May raise an exception when file
61    difficulties occur.
62    """
63    comment_re = re.compile('^\s*#|^$')
64    fedid_str = 'fedid:([0-9a-fA-F]{40})'
65    id_str = '[a-zA-Z][\w_-]*'
66    single_re = re.compile('\s*%s\s*->\s*(%s)' % (fedid_str, id_str))
67    double_re = re.compile('\s*%s\s*->\s*\((%s)\s*,\s*(%s)\)' % \
68            (fedid_str, id_str, id_str))
69    bad_role = re.compile('[^a-zA-Z0-9_]+')
70
71    roles = { }
72
73    for fn in files:
74        f = open(fn, "r")
75        for l in f:
76            id = None
77            for r in (comment_re, single_re, double_re):
78                m = r.match(l)
79                if m: 
80                    # NB, the comment_re has no groups
81                    if m.groups():
82                        g = m.groups()
83                        id = g[0]
84                        r = [ bad_role.sub('_', x) for x in g[1:] ]
85                    break
86            else:
87                print 'Unmatched line: %s' % l
88
89            if id:
90                # New and create are implicit.  >sigh<
91                r.extend(('new', 'create'))
92                if id in roles: roles[id].add_roles(r)
93                else: roles[id] = identity(r[0], r)
94
95    return roles
96
97def make_credentials(roles, cert, key, creds_dir, debug):
98    """
99    From the dict of identities, indexed by fedid, call creddy to create the
100    ABAC certificates.  Return a list of the created files.  If debug is true,
101    just print the creddy commands.
102    """
103    credfiles = []
104    for k, id in roles.items():
105        for i, r in enumerate(id.roles):
106            cf = '%s/%s%03d_attr.der' % \
107                    (creds_dir or 'new_cert_dir', id.name, i)
108            cmd = ['creddy', '--attribute', 
109                    '--issuer=%s' % (cert or 'cert_file'),
110                    '--key=%s' % (key or 'key_file'), '--role=%s' % r,
111                    '--subject-id=%s' % k, '--out=%s' % cf ]
112            if debug:
113                print join(cmd)
114            else:
115                rv =  subprocess.call(cmd)
116                if rv != 0:
117                    raise RuntimeError('%s failed: %d' % (join(cmd), rv))
118                else:
119                    credfiles.append(cf)
120    return credfiles
121
122parser = Parser()
123opts, args = parser.parse_args()
124cert, key = None, None
125delete_certs = False
126
127if not opts.make_authorizer and opts.update_authorizer:
128    sys.exit('--policy_only and --update are in conflict.  Pick one.')
129
130if opts.key:
131    if os.access(opts.key, os.R_OK): key = opts.key
132    else: sys.exit('Cannot read %s (key file)' % opts.key)
133
134if opts.dir:
135    if opts.make_dir:
136        if not os.path.isdir(opts.dir) and not debug:
137            try:
138                os.mkdir(opts.dir, 0755)
139            except EnvironmentError, e:
140                sys.exit('Could not make %s: %s' % (opts.dir, e))
141    else:
142        if not os.path.isdir(opts.dir):
143            sys.exit('%s is not a directory' % opts.dir)
144        elif not os.access(opts.dir, os.W_OK):
145            sys.exit('%s is not writable' % opts.dir)
146
147if opts.make_authorizer:
148    creds_dir = mkdtemp()
149    delete_creds = True
150else:
151    creds_dir = opts.dir
152    delete_creds = False
153
154if opts.cert:
155    if os.access(opts.cert, os.R_OK):
156        if not key:
157            if abac_pem_type(opts.cert) == 'both':
158                key, cert = abac_split_cert(opts.cert)
159                delete_certs = True
160        else:
161            cert = opts.cert
162    else:
163        sys.exit('Cannot read %s (certificate file)' % opts.cert)
164
165if any([ x is None for x in (cert, opts.dir, key)]):
166    print >>sys.stderr, "Need output dir, certificate and key to make creds"
167    print >>sys.stderr, "Reverting to debug mode"
168    debug = True
169else:
170    debug = opts.debug
171
172try:
173    roles = parse_configs(args)
174    if not roles:
175        print >>sys.stderr, "No roles found.  Did you specify a configuration?"
176
177    try:
178        credfiles = make_credentials(roles, cert, key, creds_dir, debug)
179
180        if opts.make_authorizer:
181            if debug:
182                print >>sys.stderr, 'Debug mode, no authorizer created'
183            elif opts.update_authorizer:
184                operation = 'updat'
185                a = abac_authorizer(load=opts.dir)
186                a.import_credentials(file_list=credfiles)
187                a.save()
188            else:
189                operation = 'creat'
190                a = abac_authorizer(key=opts.key, me=opts.cert, 
191                        certs=creds_dir, save=opts.dir)
192                a.save()
193    except EnvironmentError, e:
194        sys.exit("Can't create or write %s: %s" % (e.filename, 
195            e.strerror))
196    except abac_authorizer.bad_cert_error, e:
197        sys.exit("Error %sing authorizer: %s" % (op, e))
198    except RuntimeError, e:
199        sys.exit('%s' % e)
200
201finally:
202    if delete_certs:
203        if cert: os.unlink(cert)
204        if key: os.unlink(key)
205    if delete_creds:
206        clear_dir(creds_dir)
207        os.rmdir(creds_dir)
Note: See TracBrowser for help on using the repository browser.