source: fedd/fedd_to_user_certs.py @ b06744b

Last change on this file since b06744b 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: 5.6 KB
Line 
1#!/usr/bin/env python
2
3import sys
4import re
5
6import os
7import os.path
8import zipfile
9
10from tempfile import mkdtemp
11
12import ABAC
13
14from federation.authorizer import abac_authorizer
15from federation.util import abac_pem_type, abac_split_cert, file_expanding_opts
16
17class Parser(file_expanding_opts):
18    def __init__(self):
19        file_expanding_opts.__init__(self)
20        self.add_option('--cert', dest='cert', default=None,
21                action='callback', callback=self.expand_file, type='str',
22                help='my fedid as an X.509 certificate')
23        self.add_option('--key', dest='key', default=None,
24                action='callback', callback=self.expand_file, type='str',
25                help='key for the certificate')
26        self.add_option('--dir', dest='dir', default='.',
27                action='callback', callback=self.expand_file, type='str',
28                help='Output directory for credentials')
29        self.add_option('--make_dir', action='store_true', dest='make_dir',
30                default=False, help='Create the --dir directory')
31        self.add_option('--debug', action='store_true', dest='debug',
32                default=False, help='Just print the libcreddy parameters')
33        self.add_option('--user', action='append', dest='users',
34                help='Output credentials for this user.  ' + \
35                        'May be specified more than once or omitted entirely')
36
37class identity:
38    def __init__(self, name, roles=None):
39        self.name = name
40        if roles: self.roles = set(roles)
41
42    def add_roles(self, roles):
43        self.roles |= set(roles)
44
45    def __str__(self):
46        return "%s: %s" % (self.name, ', '.join(self.roles))
47
48def clear_dir(dir):
49    '''
50    Empty the given directory (and all its subdirectories).
51    '''
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                r.append('feduser')
91                if id in roles: roles[id].add_roles(r)
92                else: roles[id] = identity(r[0], r)
93
94    return roles
95
96def make_credentials(roles, cert, key, creds_dir, users, debug):
97    """
98    From the dict of identities, indexed by fedid, call libcreddy to create the
99    ABAC certificates.  Return a list of the created files.  If debug is true,
100    just print the creddy attribute creation parameters.
101    """
102    credfiles = []
103    cwd = os.getcwd()
104    for k, id in roles.items():
105        if users is not None and id.name not in users:
106            continue
107        if debug:
108            print 'cert %s key %s role %s principal %s' % \
109                    (cert, key, ','.join(id.roles), k)
110            continue
111
112        # This user is requested and we're not debugging
113
114        zname = os.path.join(creds_dir, '%s.zip' % id.name)
115        zf = zipfile.ZipFile(zname,'w', zipfile.ZIP_DEFLATED)
116        credfiles.append(zname)
117        td = mkdtemp()
118        try:
119            os.chdir(td)
120            cid = ABAC.ID(cert)
121            cid.write_cert_name('issuer.pem')
122            zf.write('issuer.pem')
123            for i, r in enumerate(id.roles):
124                cf = '%s%03d_attr.der' % (id.name, i)
125                cid = ABAC.ID(cert)
126                cid.load_privkey(key)
127                cattr = ABAC.Attribute(cid, r, 3600 * 24 * 365 * 10)
128                cattr.principal(k)
129                cattr.bake()
130                cattr.write_file(cf)
131                zf.write(cf)
132            os.chdir(cwd)
133            zf.close()
134        finally:
135            clear_dir(td)
136            os.rmdir(td)
137
138    return credfiles
139
140# The main line
141
142parser = Parser()
143opts, args = parser.parse_args()
144cert, key = None, None
145delete_certs = False
146
147if opts.key:
148    if os.access(opts.key, os.R_OK): key = opts.key
149    else: sys.exit('Cannot read %s (key file)' % opts.key)
150
151if opts.users: users = set(opts.users)
152else: users = None
153
154creds_dir = opts.dir
155
156if opts.cert:
157    if os.access(opts.cert, os.R_OK):
158        if not key:
159            if abac_pem_type(opts.cert) == 'both':
160                key, cert = abac_split_cert(opts.cert)
161                delete_certs = True
162        else:
163            cert = opts.cert
164    else:
165        sys.exit('Cannot read %s (certificate file)' % opts.cert)
166
167if not all([x for x in (cert, opts.dir, key)]):
168    print >>sys.stderr, "Need output dir, certificate and key to make creds"
169    print >>sys.stderr, "Reverting to debug mode"
170    debug = True
171else:
172    debug = opts.debug
173
174if opts.dir:
175    if opts.make_dir:
176        if not os.path.isdir(opts.dir) and not debug:
177            try:
178                os.mkdir(opts.dir, 0755)
179            except EnvironmentError, e:
180                sys.exit('Could not make %s: %s' % (opts.dir, e))
181        if not os.path.isdir(opts.dir):
182            sys.exit('%s is not a directory' % opts.dir)
183        elif not os.access(opts.dir, os.W_OK):
184            sys.exit('%s is not writable' % opts.dir)
185
186
187try:
188    roles = parse_configs(args)
189    if not roles:
190        print >>sys.stderr, "No roles found.  Did you specify a configuration?"
191
192    try:
193        credfiles = make_credentials(roles, cert, key, creds_dir, 
194                users, debug)
195    except EnvironmentError, e:
196        sys.exit("Can't create or write %s: %s" % (e.filename, 
197            e.strerror))
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)
Note: See TracBrowser for help on using the repository browser.