source: tools/suggest/suggest.py @ 08f776a

abac0-leakabac0-meicompt_changesmei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since 08f776a was 08f776a, checked in by Ted Faber <faber@…>, 12 years ago

Better names

  • Property mode set to 100644
File size: 9.7 KB
Line 
1#!/usr/local/bin/python
2
3import gtk
4import gobject
5
6import ABAC
7import Creddy
8
9import os
10import re
11import copy
12import base64
13import ConfigParser
14
15from tempfile import mkdtemp
16from shutil import rmtree
17
18class proof:
19    def __init__(self, name, prover, role, principal, creds):
20        self.name = name
21        self.prover = prover
22        self.role = role
23        self.principal = principal
24        self.ctxt = ABAC.Context()
25        self.keyid_to_cn = { }
26        self.cn_to_keyid = { }
27        attrs = []
28        try:
29            d = mkdtemp()
30            for c in creds:
31                cc = self.pem_to_der(c)
32                succ = self.ctxt.load_id_chunk(cc) 
33                if succ == ABAC.ABAC_CERT_SUCCESS: 
34                    try:
35                        fn = os.path.join(d, 'file.pem')
36                        f = open(fn, 'w')
37                        f.write(cc)
38                        f.close()
39                        cid = Creddy.ID(fn)
40                        base_cn = cn = re.sub('_ID.pem$','',cid.cert_filename())
41                        i = 0
42                        while cn in self.cn_to_keyid:
43                            cn = '%s%03d' % (base_cn, i)
44                            i += 1
45                        self.cn_to_keyid[cn] = cid.keyid()
46                        self.keyid_to_cn[cid.keyid()] = cn
47                    except EnvironmentError, e:
48                        print >>sys.stderr, '%s: %s' % (e.filename, e.strerror)
49                else:
50                    attrs.append(cc)
51            for c in attrs:
52                self.ctxt.load_attribute_chunk(c) 
53        finally:
54            rmtree(d)
55
56
57    @staticmethod
58    def pem_to_der(c):
59        pat = '-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE'
60        m = re.match(pat, c, re.DOTALL)
61        if m: return base64.b64decode(m.group(1))
62        else: return c
63
64    def replace_keyids(self, s):
65        for k, v in self.keyid_to_cn.items():
66            s = re.sub(k, v, s)
67        return s
68
69    def __str__(self):
70        s = 'Name: %s\n' % self.name
71        s += 'Prover: %s\n' % self.prover
72        s += 'Principal: %s\n' % self.principal
73        s += 'Role: %s\n' % self.role
74        s += 'Creds: \n'
75        for c in self.ctxt.credentials():
76            s += self.replace_keyids(
77                    '%s <- %s\n' % ( c.head().string(), c.tail().string()))
78        return s
79
80class window(gtk.Window):
81    '''
82    The main GUI class.  It presents the various TreeViews and menus to
83    save/load/add, to add credentials, identities and actions and to change the
84    policy translation variable.  It keeps its current size and location in the
85    .abac_policy_tool.cfg file in the user's home.
86    '''
87
88    # Definition of the menus
89    ui_def = '''
90    <ui>
91        <menubar>
92            <menu action="FileMenu">
93                <menuitem name="Load" action="FileLoad"/>
94                <menuitem name="Quit" action="FileQuit"/>
95            </menu>
96        </menubar>
97    </ui>
98    '''
99    # Path to the configuration
100    cfg_path = os.path.join(os.path.expanduser('~'), '.abac_proof_explainer.cfg')
101
102    @staticmethod
103    def wrapit(widget):
104        '''
105        Put widget into a ScrolledWindow with automatic scrollbars on both
106        directions, and return the ScrolledWindow.
107        '''
108        sw = gtk.ScrolledWindow()
109        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
110        sw.add(widget)
111        return sw
112
113    def translate_keyids(self, s):
114        for k, n in self.key_to_name:
115            s = re.sub(k, n, s)
116        return s
117
118    def makeListView(self, l, title):
119        lm = gtk.ListStore(gobject.TYPE_STRING)
120        tv = gtk.TreeView(lm)
121        tv.append_column(gtk.TreeViewColumn(title, 
122            gtk.CellRendererText(), text=0))
123        for v in l:
124            lm.append((self.translate_keyids(v),))
125        return tv
126
127
128    def report_error(self, message):
129        '''
130        Put a MessageDialog up with the given message.  This is a member method
131        so that it can be centered on the window.
132        '''
133        md = gtk.MessageDialog(self, gtk.DIALOG_MODAL, 
134                gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, 
135                message)
136        md.run()
137        md.destroy()
138
139    def __init__(self):
140        '''
141        Initialize all the GTK hooks for menus, put the various TreeViews up
142        (connected to the policy) and read teh configuration for current
143        position.
144        '''
145        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
146        self.key_to_name = []
147        try:
148            f = open('./names', 'r')
149            for l in f:
150                m = re.match('(\\S+)\s+(\\S+)', l)
151                if m:
152                    self.key_to_name.append((m.group(1), m.group(2)))
153            f.close()
154        except EnvironmentError:
155            pass
156
157        self.set_title('ABAC Policy Tool')
158        self.connect('destroy', self.quit)
159        self.connect('show', self.shown)
160        self.connect('configure-event', self.changed)
161        self.pos = (0,0)
162        self.size = (500, 500)
163        self.read_config()
164        self.proofs = []
165
166        # Make the Menus real
167        ui = gtk.UIManager()
168        ag = gtk.ActionGroup('action')
169        ag.add_actions((
170            ('FileMenu', None, 'File'),
171            ('FileQuit', gtk.STOCK_QUIT, None, None, None, self.quit),
172            ))
173        # load and append call the same method with different user data -
174        # whether to clear current policy or not.
175        ag.add_actions((
176            ('FileLoad', gtk.STOCK_OPEN, None, None, None, self.load),
177            ), True)
178        ui.insert_action_group(ag, -1)
179        ui.add_ui_from_string(window.ui_def)
180
181        # Put it all together and show it.
182        mb = ui.get_widget('ui/menubar')
183        vb = gtk.VBox()
184        vb.pack_start(mb, False, False, 0)
185        #vb.pack_start(nb, True, True, 0)
186
187        self.add(vb)
188        self.show_all()
189   
190    def quit(self, widget=None, data=None):
191        '''
192        Called from File->Quit in the menu.  Save location/size and exit
193        '''
194        self.save_config()
195        gtk.main_quit()
196
197    def load(self, widget=None, data=None):
198        '''
199        Called to either load or append to the loaded policy.  data is the
200        clearit parameter to the load call.  Other than that, just put up a
201        requester and do the thing.
202        '''
203        d = gtk.FileChooserDialog('Load file', self, 
204                gtk.FILE_CHOOSER_ACTION_OPEN, (
205            gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
206            gtk.STOCK_OK, gtk.RESPONSE_OK))
207        d.set_select_multiple(False)
208        d.set_current_folder('.')
209        d.set_do_overwrite_confirmation(True)
210        rv = d.run()
211        d.hide()
212        if rv == gtk.RESPONSE_OK:
213            self.read_proofs(d.get_filename())
214            # XXX multiple proofs
215            self.get_child().add(self.interpret_proof(self.proofs[0]))
216            self.show_all()
217        d.destroy()
218
219
220    def shown(self, w):
221        '''
222        Handles an event where the window appears.  Move to the saved position
223        and size.
224        '''
225        self.move(*self.pos)
226        self.resize(*self.size)
227
228    def changed(self, w, e):
229        '''
230        Handles an event where the window changes (resizes or moves).  Remember
231        the size and position.
232        '''
233        self.pos = self.get_position()
234        self.size = self.get_size()
235
236    def get_intpair(self, sect, opt):
237        '''
238        Utility to pull a pair of integers from a configuration file.  The size
239        and position are thsi kind of data, so this is used a couple places.
240        '''
241        if not self.cfg.has_section(sect):
242            self.cfg.add_section(sect)
243
244        if self.cfg.has_option(sect, opt):
245            try:
246                return [int(x) for x in self.cfg.get(sect, opt).split(',', 1)]
247            except ValueError:
248                return None
249        else:
250            return None
251
252    def read_config(self):
253        '''
254        Get the saved size and position from the config file, if any
255        '''
256        self.cfg = ConfigParser.SafeConfigParser()
257        self.cfg.read(window.cfg_path)
258
259        self.pos = self.get_intpair('geom', 'pos') or ( 0, 0)
260        self.size = self.get_intpair('geom', 'size') or ( 500, 500)
261
262
263    def save_config(self):
264        '''
265        Save the current postion to the default config file.
266        '''
267        self.cfg.set('geom', 'pos', '%d,%d' % self.pos)
268        self.cfg.set('geom', 'size', '%d,%d' % self.size)
269        try:
270            f = open(window.cfg_path, 'w')
271            self.cfg.write(f)
272            f.close()
273        except EnvironmentError, e:
274            pass
275
276    def read_proofs(self, fn):
277        self.proofs = []
278        try:
279            f = open(fn, 'r')
280            creds = []
281            for line in f:
282                line = line.strip()
283                if line == '<proof>':
284                    prover = None
285                    principal = None
286                    role = None
287                    creds = []
288                elif line == '</proof>' :
289                    p = proof(name, prover, role, principal, creds)
290                    ok, pp = p.ctxt.query(role, principal)
291                    if not ok: self.proofs.append(p)
292               
293                m = re.match('<comment>(.*)</comment>', line)
294                if m is not None:
295                    name = m.group(1)
296
297                m = re.match('<principal>([0-9a-f]+)</principal>', line)
298                if m is not None:
299                    principal = m.group(1)
300
301                m = re.match('<prover>([0-9a-f]+)</prover>', line)
302                if m is not None:
303                    prover = m.group(1)
304
305                m = re.match('<attribute>(.*)</attribute>', line)
306                if m is not None:
307                    role = m.group(1)
308
309                m = re.match('<credential>(.*)</credential>', line)
310                if m is not None:
311                    creds.append(base64.b64decode(m.group(1)))
312            f.close()
313        except EnvironmentError, e:
314            self.report_error("Cannot open %s: %s" % (e.filename, e.strerror))
315            return
316
317    def interpret_proof(self, p):
318        roles = set()
319        direct_roles = {}
320        groles = set()
321        principals = set()
322        goals = set()
323        attrs = set()
324        ok, proof = p.ctxt.query(p.role, p.principal)
325        for c in p.ctxt.credentials():
326            role = c.tail()
327            if role.is_principal():
328                if role.string() != p.principal: 
329                    principals.add(role.string())
330                else:
331                    assigner, r = c.head().string().split('.')
332                    direct_roles[r] = assigner
333            else: 
334                r = role.string()
335                for s in r.split('&'):
336                    roles.add(s.strip())
337                groles.add(r)
338
339            role = c.head()
340            roles.add(role.string())
341            groles.add(role.string())
342
343        for r in groles:
344            ok, proof =  p.ctxt.query(p.role, r)
345            if ok :
346                goals.add(r)
347        for r in roles:
348            ok, proof = p.ctxt.query(r, p.principal)
349            if ok:
350                attrs.add(r)
351
352
353        split_goals = [ [s.strip() for s in g.split('&')] for g in goals ]
354        plans = []
355        for sg in split_goals:
356            pl = []
357            for g in sg:
358                if g.count('.') == 2:
359                    # linking role
360                    pr, rr, lr = g.split('.')
361                    if lr in direct_roles:
362                        pl.append('add %s.%s to %s' % (pr, rr, direct_roles[lr]))
363                    else:
364                        pl.append('someone with %s.%s must delegate %s to %s' % \
365                                (pr, rr, lr, p.principal))
366                elif g.count('.') == 1:
367                    pl.append('add %s to %s' % (g, principal))
368            plans.append('\n'.join(pl))
369
370        vb = gtk.VBox()
371        vb.set_border_width(20)
372        vb.pack_start(
373                self.wrapit(
374                    self.makeListView([p.prover], "Entity Blocking Access")
375                ), True, True,0)
376        vb.pack_start(
377                self.wrapit(
378                    self.makeListView(plans, "Suggested Actions")
379                ), True, True,0)
380        vb.pack_start(
381                self.wrapit(
382                    self.makeListView(goals, "Required Slice Attributes")
383                ), True, True,0)
384
385        vb.pack_start(
386                self.wrapit(
387                    self.makeListView(attrs, "Slice Attributes Present")
388                ), True, True,0)
389        vb.show_all()
390        return vb
391
392w = window()
393gtk.main()
Note: See TracBrowser for help on using the repository browser.