source: fedd/access_to_abac.py @ 6cc5c81

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

Convert existing access DB to ABAC formats. Initial import. Miles to go.

  • Property mode set to 100755
File size: 4.9 KB
Line 
1#!/usr/local/bin/python
2
3import sys, os
4import re
5
6from federation.fedid import fedid
7from optparse import OptionParser, OptionValueError
8
9class attribute:
10    def __init__(self, p, a):
11        self.principal = p
12        self.attr = a
13
14    def __str__(self):
15        return "%s.%s" % (self.principal, self.attr)
16
17class credential:
18    def __init__(self, p, a, req):
19        self.principal = p
20        if isinstance(a, (tuple, list, set)) and len(a) == 1:
21            self.attr = a[0]
22        else:
23            self.attr = a
24        self.req = req
25
26    def __str__(self):
27        if isinstance(self.req, (tuple, list, set)):
28            return "%s.%s <- %s" % (self.principal, self.attr, 
29                    " & ".join(["%s" % r for r in self.req]))
30        else:
31            return "%s.%s <- %s" % (self.principal, self.attr, self.req)
32
33class parse_error(RuntimeError): pass
34
35def parse_emulab(l, creds, me, to_id, p, gp, gu):
36    right_side_str = '\s*,\s*\(\s*%s\s*,\s*%s\s*,\s*%s\s*\)' % \
37            (id_same_str, id_same_str,id_same_str)
38
39    m = re.match(right_side_str, l)
40    if m:
41        project, user = m.group(1,2)
42        if project == '<same>':
43            if gp  is not None:
44                project = gp
45            else:
46                raise parse_error("Project cannot be decisively mapped: %s" % l)
47                return False
48        if user == '<same>':
49            if gu is not None:
50                project = gu
51            else:
52                raise parse_error("User cannot be decisively mapped: %s" % l)
53                return False
54        if project and user:
55            a = 'project_%s_user_%s' % (project, user)
56        elif project:
57            a = 'project_%s' % project
58        elif user:
59            a = 'user_%s' % user
60        else:
61            raise parse_error("No mapping for %s/%s!?" % (gp, gu))
62            return False
63
64        c = credential(me, a, 
65                [attribute(p, x) for x in (gp, gu) if x is not None])
66        creds.add(c)
67        if a in to_id: to_id[a].append(c)
68        else: to_id[a] = [ c ]
69        return True
70    else:
71        raise parse_error("Badly formatted local mapping: %s" % l)
72        return False
73
74
75def parse_dragon(l, creds, me, to_id, p, gp, gu):
76    right_side_str = '\s*,\s*\(\s*(%s)\s*\)' % \
77            (id_str)
78
79    m = re.match(right_side_str, l)
80    if m:
81        repo= m.group(1)
82        c = credential(me, 'repo_%s' % repo, 
83                [attribute(p, x) for x in (gp, gu) if x is not None])
84        creds.add(c)
85        if repo in to_id: to_id[repo].append(c)
86        else: to_id[repo] = [ c ]
87        return True
88    else:
89        raise parse_error("Badly formatted local mapping: %s" % l)
90        return False
91
92
93class access_opts(OptionParser):
94    mappers = { 'emulab': parse_emulab, 'dragon': parse_dragon }
95
96    @staticmethod
97    def parse_mapper(opt, s, val, parser, dest):
98        if val in access_opts.mappers:
99            setattr(parser.values, dest, access_opts.mappers[val])
100        else:
101            raise OptionValueError('%s must be one of %s' % \
102                    (s, ", ".join(access_opts.mappers.keys())))
103
104    def __init__(self):
105        OptionParser.__init__(self, usage='%prog [opts] file [...]')
106        self.add_option('--cert', dest='cert', default=None,
107                help='my fedid as an X.509 certificate')
108        self.add_option('--key', dest='key', default=None,
109                help='key for the certificate')
110        self.add_option('--type', action='callback', nargs=1, type='str',
111                callback=access_opts.parse_mapper, 
112                callback_kwargs = { 'dest': 'mapper'}, 
113                help='Type of access file to parse.  One of %s. ' %\
114                        ", ".join(access_opts.mappers.keys()) + \
115                        'Omit for generic parsing.')
116        self.set_defaults(mapper=None)
117
118
119
120comment_re = re.compile('^\s*#|^$')
121fedid_str = 'fedid:([0-9a-fA-F]{40})'
122id_str = '[a-zA-Z][\w-]*'
123id_any_str = '(%s|<any>)' % id_str
124id_same_str = '(%s|<same>)' % id_str
125left_side_str = '\(\s*%s\s*,\s*%s\s*,\s*%s\s*\)' % \
126        (fedid_str, id_any_str, id_any_str)
127right_side_str = '(%s)(\s*,\s*\(.*\))?' % (id_str)
128line_re = re.compile('%s\s*->\s*%s' % (left_side_str, right_side_str))
129
130p = access_opts()
131opts, args = p.parse_args()
132
133if len(args) < 1:
134    sys.exit('No filenames given to parse')
135
136if opts.cert: 
137    try:
138        me = fedid(file=opts.cert) 
139    except EnvironmentError, e:
140        sys.exit('Bad --cert: %s (%s)' % (e.strerror, e.filename or '?!'))
141else: 
142    print >>sys.stderr, 'No --cert, using dummy fedid'
143    me = fedid(hexstr='0123456789012345678901234567890123456789')
144
145mapper = opts.mapper
146
147for fn in args:
148    creds = set()
149    to_id = { }
150    try:
151        f = open(fn, "r")
152        for i, l in enumerate(f):
153            try:
154                if comment_re.match(l):
155                    continue
156                else:
157                    m =  line_re.match(l)
158                    if m:
159                        p, da = m.group(1, 4)
160                        gp, gu = m.group(2, 3)
161                        if gp == '<any>': gp = None
162                        if gu == '<any>': gu = None
163
164                        creds.add(credential(me, da, 
165                                [attribute(p, x) for x in (gp, gu) \
166                                    if x is not None]))
167                        if m.group(5) and mapper:
168                            mapper(m.group(5), creds, me, to_id, p, gp, gu)
169                    else:
170                        raise parse_error('Syntax error')
171            except parse_error, e:
172                raise parse_error('Error on line %d of %s: %s' % \
173                        (i, fn, e.message))
174               
175        f.close()
176    except parse_error, e:
177        print >> sys.stderr, "%s" % e
178        continue
179
180    except EnvironmentError, e:
181        print >>sys.stderr, "File error %s: %s" % \
182                (e.filename or '!?', e.strerror) 
183        continue
184
185    for c in creds:
186        print "%s" % c
187
188    for k, c in to_id.items():
189        print "%s: %s" % ( k , ", ".join(set(["%s.%s" % (x.principal, x.attr) \
190                for x in c])))
Note: See TracBrowser for help on using the repository browser.