source: tools/suggest/suggest.py @ 39cd6e5

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

GUI views!

  • Property mode set to 100644
File size: 9.2 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    @staticmethod
114    def makeListView(l, title):
115        lm = gtk.ListStore(gobject.TYPE_STRING)
116        tv = gtk.TreeView(lm)
117        tv.append_column(gtk.TreeViewColumn(title, 
118            gtk.CellRendererText(), text=0))
119        for v in l:
120            lm.append((v,))
121        return tv
122
123
124    def report_error(self, message):
125        '''
126        Put a MessageDialog up with the given message.  This is a member method
127        so that it can be centered on the window.
128        '''
129        md = gtk.MessageDialog(self, gtk.DIALOG_MODAL, 
130                gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, 
131                message)
132        md.run()
133        md.destroy()
134
135    def __init__(self):
136        '''
137        Initialize all the GTK hooks for menus, put the various TreeViews up
138        (connected to the policy) and read teh configuration for current
139        position.
140        '''
141        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
142        self.set_title('ABAC Policy Tool')
143        self.connect('destroy', self.quit)
144        self.connect('show', self.shown)
145        self.connect('configure-event', self.changed)
146        self.pos = (0,0)
147        self.size = (500, 500)
148        self.read_config()
149        self.proofs = []
150
151        # Make the Menus real
152        ui = gtk.UIManager()
153        ag = gtk.ActionGroup('action')
154        ag.add_actions((
155            ('FileMenu', None, 'File'),
156            ('FileQuit', gtk.STOCK_QUIT, None, None, None, self.quit),
157            ))
158        # load and append call the same method with different user data -
159        # whether to clear current policy or not.
160        ag.add_actions((
161            ('FileLoad', gtk.STOCK_OPEN, None, None, None, self.load),
162            ), True)
163        ui.insert_action_group(ag, -1)
164        ui.add_ui_from_string(window.ui_def)
165
166        # Put it all together and show it.
167        mb = ui.get_widget('ui/menubar')
168        vb = gtk.VBox()
169        vb.pack_start(mb, False, False, 0)
170        #vb.pack_start(nb, True, True, 0)
171
172        self.add(vb)
173        self.show_all()
174   
175    def quit(self, widget=None, data=None):
176        '''
177        Called from File->Quit in the menu.  Save location/size and exit
178        '''
179        self.save_config()
180        gtk.main_quit()
181
182    def load(self, widget=None, data=None):
183        '''
184        Called to either load or append to the loaded policy.  data is the
185        clearit parameter to the load call.  Other than that, just put up a
186        requester and do the thing.
187        '''
188        d = gtk.FileChooserDialog('Load file', self, 
189                gtk.FILE_CHOOSER_ACTION_OPEN, (
190            gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
191            gtk.STOCK_OK, gtk.RESPONSE_OK))
192        d.set_select_multiple(False)
193        d.set_current_folder('.')
194        d.set_do_overwrite_confirmation(True)
195        rv = d.run()
196        d.hide()
197        if rv == gtk.RESPONSE_OK:
198            self.read_proofs(d.get_filename())
199            # XXX multiple proofs
200            self.get_child().add(self.interpret_proof(self.proofs[0]))
201            self.show_all()
202        d.destroy()
203
204
205    def shown(self, w):
206        '''
207        Handles an event where the window appears.  Move to the saved position
208        and size.
209        '''
210        self.move(*self.pos)
211        self.resize(*self.size)
212
213    def changed(self, w, e):
214        '''
215        Handles an event where the window changes (resizes or moves).  Remember
216        the size and position.
217        '''
218        self.pos = self.get_position()
219        self.size = self.get_size()
220
221    def get_intpair(self, sect, opt):
222        '''
223        Utility to pull a pair of integers from a configuration file.  The size
224        and position are thsi kind of data, so this is used a couple places.
225        '''
226        if not self.cfg.has_section(sect):
227            self.cfg.add_section(sect)
228
229        if self.cfg.has_option(sect, opt):
230            try:
231                return [int(x) for x in self.cfg.get(sect, opt).split(',', 1)]
232            except ValueError:
233                return None
234        else:
235            return None
236
237    def read_config(self):
238        '''
239        Get the saved size and position from the config file, if any
240        '''
241        self.cfg = ConfigParser.SafeConfigParser()
242        self.cfg.read(window.cfg_path)
243
244        self.pos = self.get_intpair('geom', 'pos') or ( 0, 0)
245        self.size = self.get_intpair('geom', 'size') or ( 500, 500)
246
247
248    def save_config(self):
249        '''
250        Save the current postion to the default config file.
251        '''
252        self.cfg.set('geom', 'pos', '%d,%d' % self.pos)
253        self.cfg.set('geom', 'size', '%d,%d' % self.size)
254        try:
255            f = open(window.cfg_path, 'w')
256            self.cfg.write(f)
257            f.close()
258        except EnvironmentError, e:
259            pass
260
261    def read_proofs(self, fn):
262        self.proofs = []
263        try:
264            f = open(fn, 'r')
265            creds = []
266            for line in f:
267                line = line.strip()
268                if line == '<proof>':
269                    prover = None
270                    principal = None
271                    role = None
272                    creds = []
273                elif line == '</proof>' :
274                    p = proof(name, prover, role, principal, creds)
275                    ok, pp = p.ctxt.query(role, principal)
276                    if not ok: self.proofs.append(p)
277               
278                m = re.match('<comment>(.*)</comment>', line)
279                if m is not None:
280                    name = m.group(1)
281
282                m = re.match('<principal>([0-9a-f]+)</principal>', line)
283                if m is not None:
284                    principal = m.group(1)
285
286                m = re.match('<prover>([0-9a-f]+)</prover>', line)
287                if m is not None:
288                    prover = m.group(1)
289
290                m = re.match('<attribute>(.*)</attribute>', line)
291                if m is not None:
292                    role = m.group(1)
293
294                m = re.match('<credential>(.*)</credential>', line)
295                if m is not None:
296                    creds.append(base64.b64decode(m.group(1)))
297            f.close()
298        except EnvironmentError, e:
299            self.report_error("Cannot open %s: %s" % (e.filename, e.strerror))
300            return
301
302    def interpret_proof(self, p):
303        roles = set()
304        direct_roles = {}
305        groles = set()
306        principals = set()
307        goals = set()
308        attrs = set()
309        ok, proof = p.ctxt.query(p.role, p.principal)
310        for c in p.ctxt.credentials():
311            role = c.tail()
312            if role.is_principal():
313                if role.string() != p.principal: 
314                    principals.add(role.string())
315                else:
316                    assigner, r = c.head().string().split('.')
317                    direct_roles[r] = assigner
318            else: 
319                r = role.string()
320                for s in r.split('&'):
321                    roles.add(s.strip())
322                groles.add(r)
323
324            role = c.head()
325            roles.add(role.string())
326            groles.add(role.string())
327
328        for r in groles:
329            ok, proof =  p.ctxt.query(p.role, r)
330            if ok :
331                goals.add(r)
332        for r in roles:
333            ok, proof = p.ctxt.query(r, p.principal)
334            if ok:
335                attrs.add(r)
336
337
338        split_goals = [ [s.strip() for s in g.split('&')] for g in goals ]
339        plans = []
340        for sg in split_goals:
341            p = []
342            for g in sg:
343                if g.count('.') == 2:
344                    # linking role
345                    pr, rr, lr = g.split('.')
346                    if lr in direct_roles:
347                        p.append('add %s.%s to %s' % (pr, rr, direct_roles[lr]))
348                    else:
349                        p.append('someone with %s.%s must delegate %s to %s' % \
350                                (pr, rr, lr, p.principal))
351                elif g.count('.') == 1:
352                    p.append('add %s to %s' % (g, principal))
353            plans.append('\n'.join(p))
354
355        vb = gtk.VBox()
356        vb.set_border_width(20)
357        vb.pack_start(
358                self.wrapit(
359                    self.makeListView(plans, "Suggested Actions")
360                ), True, True,0)
361        vb.pack_start(
362                self.wrapit(
363                    self.makeListView(goals, "Required Slice Attributes")
364                ), True, True,0)
365
366        vb.pack_start(
367                self.wrapit(
368                    self.makeListView(attrs, "Slice Attributes Present")
369                ), True, True,0)
370        vb.show_all()
371        return vb
372
373w = window()
374gtk.main()
Note: See TracBrowser for help on using the repository browser.