source: fedd/fedd_to_abac.py @ a2ca699

Last change on this file since a2ca699 was 67fa1cf, checked in by Ted Faber <faber@…>, 11 years ago

MOve over to ABAC 0.1.4

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