source: trunk/debathena/debathena/nautilus-afs/afs-property-page.py @ 24445

Revision 24445, 12.4 KB checked in by jdreed, 12 years ago (diff)
Initial checkin of nautilus-afs-integration
Line 
1import urllib
2import os
3import gtk
4import gtk.glade
5import re
6import nautilus
7import subprocess
8
9gladeFile = "/usr/share/debathena-nautilus-afs-integration/afs-property-page.glade"
10
11import sys
12try:
13    import afs.acl
14except ImportError:
15    pass
16
17class AFSPermissionsPane():
18    def __init__(self, fp):
19        self.filepath = fp
20        self.acl = None
21        self.noPermissions = False
22        self.inAFS = True
23        if 'afs.acl' not in sys.modules.keys():
24            if (not self.filepath.startswith("/afs")) and (not self.filepath.startswith("/mit")):
25                self.inAFS = False
26            b = gtk.VBox()
27            l = gtk.Label("Sorry, this feature is not available on this platform.\n(afs.acl module could not be imported.)\n")
28            l.set_single_line_mode(False)
29            l.show()
30            b.pack_start(l)
31            b.show()
32            self.rootWidget = b
33            return
34        if not os.path.exists(gladeFile):
35            b = gtk.VBox()
36            l = gtk.Label("Cannot find Glade file:\n%s" % gladeFile)
37            l.set_single_line_mode(False)
38            l.show()
39            b.pack_start(l)
40            b.show()
41            self.rootWidget = b
42            return
43        self.widgets = gtk.glade.XML(gladeFile, "vboxMain")
44        self.rootWidget = self.widgets.get_widget("vboxMain")
45        try:
46            self.acl = afs.acl.ACL.retrieve(self.filepath)
47        except OSError as (errno, errstr):
48            if errno == 13:
49                self.noPermissions = True
50                self.widgets = gtk.glade.XML(gladeFile, "vboxNoPerms")
51                self.rootWidget = self.widgets.get_widget("vboxNoPerms")
52            elif errno == 22:
53                self.inAFS = False
54       
55               
56        if self.acl != None:
57            self._initWidgets()
58            self.refreshACL()
59
60    def _initWidgets(self):
61        self.listStore = gtk.ListStore(str, int, bool, str)
62        self.buttonActions = {"btnAdd": self.addItem,
63                              "btnEdit": self.editItem,
64                              "btnRemove": self.removeItem}
65        for btn in self.buttonActions:
66            self.widgets.get_widget(btn).connect("clicked", self.buttonHandler)
67        self.treeView = self.widgets.get_widget("tvACL")
68        self.treeView.set_model(self.listStore)
69        colUser = gtk.TreeViewColumn('User')
70        colBits = gtk.TreeViewColumn('Permissions')
71        self.treeView.append_column(colUser)
72        self.treeView.append_column(colBits)
73        cellRenderer = gtk.CellRendererText()
74        colUser.pack_start(cellRenderer)
75        colBits.pack_start(cellRenderer)
76        colUser.set_cell_data_func(cellRenderer, self.renderUser)
77        colBits.set_cell_data_func(cellRenderer, self.renderBitmask)
78        self.treeView.set_tooltip_column(3)
79
80    def getWidget(self):
81        if self.noPermissions:
82            return NoPermissionsPane()
83        else:
84            return self.widgets.get_widget("vboxMain")
85
86    def renderBitmask(self, col, cell, model, iter, user_data=None):
87        rights = afs.acl.showRights(model.get_value(iter, 1))
88        if afs.acl.rightsToEnglish(rights):
89            rights += '  (' + afs.acl.rightsToEnglish(rights) + ')'
90        cell.set_property('text', rights)
91
92    def renderUser(self, col, cell, model, iter, user_data=None):
93        cell.set_property('strikethrough-set', True)
94        isNeg = model.get_value(iter, 2)
95        if isNeg:
96            cell.set_property('strikethrough',True)
97        else:
98            cell.set_property('strikethrough', False)
99        cell.set_property('text', model.get_value(iter, 0))
100
101    def refreshACL(self):
102        if self.acl != None:
103            self.widgets.get_widget("lblPath").set_text(self.filepath)
104            self.listStore.clear()
105            try:
106                self.acl = afs.acl.ACL.retrieve(self.filepath)
107            except OSError as (errno, errstr):
108                if errno == 13:
109                    msg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
110                                            gtk.MESSAGE_QUESTION,
111                                            gtk.BUTTONS_OK,
112                                            "Cannot reload ACL for directory.  Perhaps your AFS tokens expired or someone else changed permissions?")
113                    msg.run()
114                    msg.destroy()
115            for i in self.acl.pos.items():
116                self.listStore.append(i + (False,i[0]))
117            for i in self.acl.neg.items():
118                self.listStore.append(i + (True,i[0] + ' (Negative Rights)'))
119
120    def removeItem(self):
121        selectedRowIter = self.treeView.get_selection().get_selected()[1]
122        if selectedRowIter == None:
123            return
124        entity, isNeg = self.listStore.get(selectedRowIter, 0, 2)
125        if isNeg:
126            msg = gtk.MessageDialog(None,gtk.DIALOG_MODAL,
127                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
128                                    "Removing negative ACLS is not yet supported")
129            msg.run()
130            msg.destroy()
131            return
132        msg = 'Are you sure you want to remove %s from the AFS ACL for this directory?'
133        if (entity == os.getenv('ATHENA_USER')) or (entity == os.getenv('USER')):
134            msg += "\n\nWARNING: You are about to remove yourself from this ACL!"
135        question = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
136                                     gtk.MESSAGE_QUESTION,
137                                     gtk.BUTTONS_YES_NO,
138                                     msg % entity)
139        response = question.run()
140        question.destroy()
141        if response == gtk.RESPONSE_YES:
142            self.acl.remove(entity)
143            self.applyChanges()
144
145    def applyChanges(self):
146        try:
147            self.acl.apply(self.filepath)
148        except OSError as (errno, errstr):
149            msg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
150                                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
151                                    "Error: %s" % errstr)
152            msg.run()
153            msg.destroy()
154        self.refreshACL()
155
156    def editItem(self):
157        selectedRowIter = self.treeView.get_selection().get_selected()[1]
158        if selectedRowIter == None:
159            return
160        entity, acl, isNeg = self.listStore.get(selectedRowIter, 0, 1, 2)
161        if isNeg:
162            msg = gtk.MessageDialog(None,gtk.DIALOG_MODAL,
163                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
164                                    "Editing negative ACLS is not yet supported")
165            msg.run()
166            msg.destroy()
167            return
168        add = AddEditACLDialog()
169        add.editMode(entity, afs.acl.showRights(acl))
170        response = add.run()
171        while (response == gtk.RESPONSE_OK) and not add.validate():
172            response=add.run()
173        if (response == gtk.RESPONSE_OK):
174            self.acl.set(add.getEntity(), afs.acl.readRights(add.getAcl()))
175            self.applyChanges()
176        add.destroy()
177
178    def addItem(self):
179        add = AddEditACLDialog()
180        response = add.run()
181        while (response == gtk.RESPONSE_OK) and not add.validate():
182            response=add.run()
183        if (response == gtk.RESPONSE_OK):
184            self.acl.set(add.getEntity(), afs.acl.readRights(add.getAcl()))
185            self.applyChanges()
186        add.destroy()
187
188    def buttonHandler(self, btn):
189        self.buttonActions[btn.name]()
190
191class AddEditACLDialog():
192    def __init__(self, editMode=False):
193        self.widgets = gtk.glade.XML(gladeFile, "dlgAdd")
194        self.window = self.widgets.get_widget("dlgAdd")
195        self.entry = self.widgets.get_widget("entryName")
196        for rb in self.widgets.get_widget_prefix("rb"):
197            rb.connect("toggled", self.toggleHandler)
198
199    def editMode(self, user, acl):
200        self.window.set_title('Edit ACL Entry')
201        self.entry.set_text(user)
202        self.entry.set_editable(False)
203        self.widgets.get_widget('lblType').set_property('visible', False)
204        for rb in self.widgets.get_widget_prefix("rb"):
205            rb.set_property('visible', False)
206        self.widgets.get_widget("cbAcl").get_child().set_text(acl)
207
208    def run(self):
209        return self.window.run()
210
211    def destroy(self):
212        self.window.destroy()
213
214    def toggleHandler(self, radioBtn, data=None):
215        if not radioBtn.get_active():
216            return
217        if radioBtn.name == "rbUser":
218            self.entry.set_text("")
219            self.entry.set_editable(True)
220        if radioBtn.name == "rbGroup":
221            # Do this in the GUI, so that people can override it if they
222            # really know what they're doing, or are using user groups or
223            # something. 
224            if re.search(":", self.entry.get_text()) == None:
225                self.entry.set_text("system:")
226            self.entry.set_editable(True)
227        if radioBtn.name == "rbAuthuser":
228            self.entry.set_text("system:authuser")
229            self.entry.set_editable(False)
230        if radioBtn.name == "rbAnyuser":
231            self.entry.set_text("system:anyuser")
232            self.entry.set_editable(False)
233
234    def getEntity(self):
235        return self.entry.get_text().strip()
236
237    def getAcl(self):
238        combobox = self.widgets.get_widget("cbAcl")
239        model = combobox.get_model()
240        active = combobox.get_active()
241        if active < 0:
242            # in case someone typos whitespace in the box, which would parse
243            # to "none".   
244            acl = combobox.get_child().get_text().strip()
245        else:
246            acl = model[active][0]
247        return acl
248
249    def validate(self):
250        if not self.getEntity():
251            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
252                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
253                                    "You did not specify the user or group.")
254            msg.run()
255            msg.destroy()
256            return False
257
258        acl = self.getAcl()
259        if not acl:
260            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
261                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
262                                    "You did not specify the permissions.")
263            msg.run()
264            msg.destroy()
265            return False
266        try:
267            r = afs.acl.readRights(acl)
268        except ValueError:
269            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
270                                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
271                                    "Invalid permissions '%s'.  Select one from the drop down list or type a valid AFS permission string into the box provided." % acl)
272            msg.run()
273            msg.destroy()
274            return False
275        if re.search('[widka]', afs.acl.showRights(r)) and self.widgets.get_widget("rbAnyuser").get_active():
276            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
277                                    gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO,
278                                    "WARNING:  You are attempting to assign '%s' permissions to system:anyuser (i.e. any user, anywhere on the Internet).  This is EXTREMELY DANGEROUS.  You may experience data loss and your directory may be used by spammers to create malicious websites.  MIT IS&T reserves the right to disable access to any AFS directories with these permissions.  Consider selecting 'All MIT Users' instead.\n\nAre you absolutely sure you want to continue?" % acl)
279
280            response = msg.run()
281            msg.destroy()
282            if response != gtk.RESPONSE_YES:
283                return False
284        return True
285
286
287class AFSPropertyPage(nautilus.PropertyPageProvider):
288    def __init__(self):
289        self.property_label = gtk.Label('AFS')
290
291    def get_property_pages(self, files):
292        # Does not work for multiple selections
293        if len(files) != 1:
294            return
295       
296        file = files[0]
297        # Does not work on non-file:// URIs
298        if file.get_uri_scheme() != 'file':
299            return
300
301        # Only works on directories
302        if not file.is_directory():
303            return
304
305#        if 'afs.acl' not in sys.modules.keys():
306#            return
307
308        filepath = urllib.unquote(file.get_uri()[7:])
309       
310        pane = AFSPermissionsPane(filepath)
311
312        # If the file does not appear to be in AFS, don't show the pane
313        if not pane.inAFS:
314            return
315       
316        self.property_label.show()
317
318        return nautilus.PropertyPage("NautilusPython::afs",
319                                     self.property_label,
320                                     pane.rootWidget),
321
Note: See TracBrowser for help on using the repository browser.