source: fedd/fedd_to_user_certs.py @ 45016ae

compt_changes
Last change on this file since 45016ae was 45016ae, checked in by Ted Faber <faber@…>, 12 years ago

Add fedd_to_user_certs.py

  • 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 Creddy
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                if id in roles: roles[id].add_roles(r)
91                else: roles[id] = identity(r[0], r)
92
93    return roles
94
95def make_credentials(roles, cert, key, creds_dir, users, debug):
96    """
97    From the dict of identities, indexed by fedid, call libcreddy to create the
98    ABAC certificates.  Return a list of the created files.  If debug is true,
99    just print the creddy attribute creation parameters.
100    """
101    credfiles = []
102    cwd = os.getcwd()
103    for k, id in roles.items():
104        if users is not None and id.name not in users:
105            continue
106        if debug:
107            print 'cert %s key %s role %s principal %s' % \
108                    (cert, key, ','.join(id.roles), k)
109            continue
110
111        # This user is requested and we're not debugging
112
113        zname = os.path.join(creds_dir, '%s.zip' % id.name)
114        zf = zipfile.ZipFile(zname,'w', zipfile.ZIP_DEFLATED)
115        credfiles.append(zname)
116        td = mkdtemp()
117        try:
118            os.chdir(td)
119            cid = Creddy.ID(cert)
120            cid.write_cert_name('issuer.pem')
121            zf.write('issuer.pem')
122            for i, r in enumerate(id.roles):
123                cf = '%s%03d_attr.der' % (id.name, i)
124                cid = Creddy.ID(cert)
125                cid.load_privkey(key)
126                cattr = Creddy.Attribute(cid, r, 3600 * 24 * 365 * 10)
127                cattr.principal(k)
128                cattr.bake()
129                cattr.write_name(cf)
130                zf.write(cf)
131            os.chdir(cwd)
132            zf.close()
133        finally:
134            clear_dir(td)
135            os.rmdir(td)
136
137    return credfiles
138
139# The main line
140
141parser = Parser()
142opts, args = parser.parse_args()
143cert, key = None, None
144delete_certs = False
145
146if opts.key:
147    if os.access(opts.key, os.R_OK): key = opts.key
148    else: sys.exit('Cannot read %s (key file)' % opts.key)
149
150if opts.users: users = set(opts.users)
151else: users = None
152
153creds_dir = opts.dir
154
155if opts.cert:
156    if os.access(opts.cert, os.R_OK):
157        if not key:
158            if abac_pem_type(opts.cert) == 'both':
159                key, cert = abac_split_cert(opts.cert)
160                delete_certs = True
161        else:
162            cert = opts.cert
163    else:
164        sys.exit('Cannot read %s (certificate file)' % opts.cert)
165
166if not all([x for x in (cert, opts.dir, key)]):
167    print >>sys.stderr, "Need output dir, certificate and key to make creds"
168    print >>sys.stderr, "Reverting to debug mode"
169    debug = True
170else:
171    debug = opts.debug
172
173if opts.dir:
174    if opts.make_dir:
175        if not os.path.isdir(opts.dir) and not debug:
176            try:
177                os.mkdir(opts.dir, 0755)
178            except EnvironmentError, e:
179                sys.exit('Could not make %s: %s' % (opts.dir, e))
180        if not os.path.isdir(opts.dir):
181            sys.exit('%s is not a directory' % opts.dir)
182        elif not os.access(opts.dir, os.W_OK):
183            sys.exit('%s is not writable' % opts.dir)
184
185
186try:
187    roles = parse_configs(args)
188    if not roles:
189        print >>sys.stderr, "No roles found.  Did you specify a configuration?"
190
191    try:
192        credfiles = make_credentials(roles, cert, key, creds_dir, 
193                users, debug)
194    except EnvironmentError, e:
195        sys.exit("Can't create or write %s: %s" % (e.filename, 
196            e.strerror))
197    except RuntimeError, e:
198        sys.exit('%s' % e)
199
200finally:
201    if delete_certs:
202        if cert: os.unlink(cert)
203        if key: os.unlink(key)
Note: See TracBrowser for help on using the repository browser.