def __init__(self): # Quite a hack, but working self.__datapath = os.path.realpath(os.path.join(__file__, os.path.pardir)) global lum_application if lum_application is not None: raise LumErorr("Cannot create more that one lum_application!") else: lum_application = self self.__group_store = GroupStore(self.__datapath) self.__user_store = UserStore(self.__datapath, self.__group_store) # Gobject constructor gobject.GObject.__init__ (self) # Load interface file self.__builder = create_builder("LumApp.ui") # Load main window self.__window = self.__builder.get_object("window") # Signal definition signals = { # Quit signals 'on_quit_menu_item_activate': gtk.main_quit, 'window_destroy_event_cb': gtk.main_quit, 'on_window_destroy': gtk.main_quit, # Callbacks 'on_about_menu_item_activate': self.show_about_dialog, 'on_new_user_menu_item_activate': self.create_new_user_dialog, 'on_connect_menu_item_activate': self.connect, 'on_reload_user_list_menu_item_activate': self.reload_user_list, 'on_delete_user_menu_item_activate': self.delete_user, 'on_forget_password_menu_item_activate': self.forget_password, 'on_edit_user_menu_item_activate': self.edit_user, 'on_change_password_menu_item_activate': self.change_password, 'on_disconnect_menu_item_activate': self.disconnect, # Group menu callbacks 'on_new_group_menuitem_activate': self.new_group, 'on_delete_group_menuitem_activate': self.delete_group, 'on_properties_group_menuitem_activate': self.group_properties, } # Autoconnect signals self.__builder.connect_signals (signals) # Create initial configuration self.__configuration = Configuration() # Change the model of the treeview self.__user_treeview = UserTreeView(self.__user_store, self) self.__user_treeview.apply_filter(self.__builder.get_object("filter_user_entry")) self.__builder.get_object("user_scrolled_window").add(self.__user_treeview) self.__group_treeview = GroupTreeView(self.__group_store, self) self.__group_treeview.apply_filter(self.__builder.get_object("filter_group_entry")) self.__builder.get_object("group_scrolled_window").add(self.__group_treeview) # Some initial values self.__uri, self.__bind_dn = None, None # Create popup menu self.__user_popup_menu = lumUserTreeViewMenu(self) self.__group_popup_menu = lumGroupTreeViewMenu(self)
class lumApp(gobject.GObject): def __init__(self): # Quite a hack, but working self.__datapath = os.path.realpath(os.path.join(__file__, os.path.pardir)) global lum_application if lum_application is not None: raise LumErorr("Cannot create more that one lum_application!") else: lum_application = self self.__group_store = GroupStore(self.__datapath) self.__user_store = UserStore(self.__datapath, self.__group_store) # Gobject constructor gobject.GObject.__init__ (self) # Load interface file self.__builder = create_builder("LumApp.ui") # Load main window self.__window = self.__builder.get_object("window") # Signal definition signals = { # Quit signals 'on_quit_menu_item_activate': gtk.main_quit, 'window_destroy_event_cb': gtk.main_quit, 'on_window_destroy': gtk.main_quit, # Callbacks 'on_about_menu_item_activate': self.show_about_dialog, 'on_new_user_menu_item_activate': self.create_new_user_dialog, 'on_connect_menu_item_activate': self.connect, 'on_reload_user_list_menu_item_activate': self.reload_user_list, 'on_delete_user_menu_item_activate': self.delete_user, 'on_forget_password_menu_item_activate': self.forget_password, 'on_edit_user_menu_item_activate': self.edit_user, 'on_change_password_menu_item_activate': self.change_password, 'on_disconnect_menu_item_activate': self.disconnect, # Group menu callbacks 'on_new_group_menuitem_activate': self.new_group, 'on_delete_group_menuitem_activate': self.delete_group, 'on_properties_group_menuitem_activate': self.group_properties, } # Autoconnect signals self.__builder.connect_signals (signals) # Create initial configuration self.__configuration = Configuration() # Change the model of the treeview self.__user_treeview = UserTreeView(self.__user_store, self) self.__user_treeview.apply_filter(self.__builder.get_object("filter_user_entry")) self.__builder.get_object("user_scrolled_window").add(self.__user_treeview) self.__group_treeview = GroupTreeView(self.__group_store, self) self.__group_treeview.apply_filter(self.__builder.get_object("filter_group_entry")) self.__builder.get_object("group_scrolled_window").add(self.__group_treeview) # Some initial values self.__uri, self.__bind_dn = None, None # Create popup menu self.__user_popup_menu = lumUserTreeViewMenu(self) self.__group_popup_menu = lumGroupTreeViewMenu(self) def __del__(self): lum_application = None def start(self): """Start lumApp""" # Show all self.__window.show_all() self.__connection = None # Give focus to something that should have it self.__builder.get_object("filter_user_entry").grab_focus() def missing_ou_cb(self, widget, missing_ou): """Callback to ask user if he/she wants to add missing ous before continue""" text = "\n".join(map(lambda x : "- <b>" + x + "</b>", missing_ou)) if ask_question(_("The following organizational units are missing in the database, add them?\n%s" % text)): for ou in missing_ou: self.__connection.add_ou(ou, True) else: show_info_dialog(_("You will not be able to perform any operation without these OUs")) self.disconnect() def disconnect(self, menu_item = None): self.clear_user_list() self.clear_group_list() self.__connection = None if self.__uri is not None: self.statusbar_update(_("Disconnected from %s") % self.__uri) self.__uri = None def connect(self, menu_item = None): """Connect to server""" # Determine which server to connect to connect_dialog = lumConnectDialog(self.__datapath, self.__configuration) uri, bind_dn, base_dn, users_ou, groups_ou = connect_dialog.run() # Update internal information self.__uri = uri self.__bind_dn = bind_dn self.__base_dn = base_dn self.__users_ou = users_ou self.__groups_ou = groups_ou if uri is None: return # Add base_dn if necessary if not self.__bind_dn.endswith(self.__base_dn): self.__bind_dn += ",%s" % self.__base_dn if not self.__users_ou.endswith(self.__base_dn): self.__users_ou += ",%s" % self.__base_dn if not self.__groups_ou.endswith(self.__base_dn): self.__groups_ou += ",%s" % self.__base_dn # Get password from keyring password = self.ask_password() # Notify user of connection self.statusbar_update(_("Connecting to %s.") % uri) self.__connection = Connection(uri = self.__uri, bind_dn = self.__bind_dn, password = password, base_dn = self.__base_dn, users_ou = self.__users_ou, groups_ou = self.__groups_ou) self.__connection.connect("missing-ou", self.missing_ou_cb) self.__connection.connect("connection-completed", self.connection_completed_cb) # Try to connect to the specified server try: self.__connection.start() except LumError: # If we can't, maybe password is wrong, so ask it again self.forget_password() password = self.ask_password() # and retry the connection. But if we fail even this time, then # abort try: self.__connection.set_password(password) self.__connection.start() except: # You had two opportunities, and both are gone. show_error_dialog( _("Error while connecting to the server, check your credentials and your connectivity")) self.__connection = None self.statusbar_update(_("Connection failed.")) def connection_completed_cb(self, widget): """Called when connection is initialized""" self.statusbar_update(_("Connection to %s initialized") % self.__uri) self.reload_user_list() def __get_selected_user(self): """Obtain usermodel and a treeview iter of the selected user in the treeview""" return self.__user_treeview.get_selected_user() return usermodel, it def __get_selected_group(self): """Obtain selected group and a treeview iter of it or None, None if there is no group selected""" return self.__group_treeview.get_selected_group() def ask_password(self): """A simple routine that ask for password, if it is not yet in the keyring""" display_name = "@".join([self.__bind_dn, self.__uri]) if gnomekeyring.is_available(): for pw_id in gnomekeyring.list_item_ids_sync('login'): pw = gnomekeyring.item_get_info_sync("login", pw_id) if pw.get_display_name() == display_name: return pw.get_secret() # Ask for password... password_dialog = lumPasswordEntry(self.__datapath) password = password_dialog.run() if password is not None: atts = { 'application': 'Ldap User Manager', 'username': self.__bind_dn, 'server': self.__uri, 'protocol': 'ldap', 'port': '389', } pw_id = gnomekeyring.item_create_sync('login', gnomekeyring.ITEM_GENERIC_SECRET, display_name, atts, password, True) return password def show_about_dialog(self, menu_item): """Show about dialog""" lumAbout(self.__datapath) def delete_user(self, menu_item = None): """Delete the selected user""" usermodel, t_iter = self.__get_selected_user() if t_iter is None: show_info_dialog(_("Select a user to delete!")) return # Users tend to delete many things they do not want # to delete if not ask_question(_("Really delete user <b>%s</b>?") % usermodel.get_username()): return # Delete user from ldap first try: self.__connection.delete_user(usermodel.get_dn()) except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to delete user")) return None # Delete user from internal dictionary self.__user_store.remove(t_iter) self.statusbar_update(_("User %s deleted.") % usermodel.get_username()) def forget_password(self, menu_item = None): if not gnomekeyring.is_available(): return None if self.__uri is None or self.__bind_dn is None: return None display_name = "@".join([self.__bind_dn, self.__uri]) for pw_id in gnomekeyring.list_item_ids_sync("login"): if gnomekeyring.item_get_info_sync("login", pw_id).get_display_name() == display_name: gnomekeyring.item_delete_sync('login', pw_id) def clear_user_list(self): self.__user_store.clear() self.__user_model_store = {} def reload_user_list(self, menu_item = None): """Reload user list in the main window""" if self.__check_connection(): # Groups have to updated before users # because we need gids <-> group_names # mapping to determine the group of every # user. self.update_group_list() self.clear_user_list() users = self.__connection.get_users() for user in users: self.push_user(user) def clear_group_list(self): """Clear group list""" self.__group_store.clear() def update_group_list(self, menu_item = None): """Update group list and internal group dictionary""" if self.__check_connection(): self.clear_group_list() for gid, group in self.__connection.get_groups().items(): self.__group_store.append(gid, group) def edit_group_members(self, menu_item = None): """Edit group members""" # Get selected group in the treeview group_name, it = self.__get_selected_group() if it is None: return # Create dialog dialog = lumEditGroupMembersDialog(self.__connection, group_name) # Get desidered modifications users_to_add, users_to_del = dialog.run() if users_to_add is None: return # Really modify users self.__connection.modify_group_members(group_name, users_to_add, users_to_del) self.statusbar_update(_("Group %s modified successfully") % group_name) def edit_user(self, menu_item = None): """Edit selected user""" usermodel, t_iter = self.__get_selected_user() if t_iter is None: show_info_dialog(_("Select a user to modify")) return # Create the dialog making a copy of the old usermodel # so we can check the difference after modification old_user = UserModel(usermodel) dialog = lumEditUserDialog(self.__datapath, usermodel, self.__group_store) new_usermodel = dialog.run() if (new_usermodel is not None): try: self.__connection.modify_user(old_user, new_usermodel) except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to edit user")) return None self.statusbar_update(_("User %s successfully modified") % new_usermodel.get_username()) # TODO: Reload only selected user self.reload_user_list() def change_password(self, menu_item = None): """Change password of selected user""" usermodel, t_iter = self.__get_selected_user() if t_iter is None: show_info_dialog(_("You need to select a user to change its password")) return password_dialog = lumChangeUserPasswordDialog(self.__datapath, usermodel.get_username()) new_password = password_dialog.run() if new_password is None: return False else: try: self.__connection.change_password(usermodel.get_username(), new_password) except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to change user password")) return False else: self.statusbar_update(_("Password of user %s changed succesfully") % usermodel.get_username()) return True def push_user(self, usermodel): """Add a user on the treeview in the main window""" self.__user_store.append(usermodel) def statusbar_update(self, message): """Update statusbar with new message""" statusbar = self.__builder.get_object("statusbar") statusbar.push(0, message) def create_new_user_dialog(self, menu_item): """Create new user""" if not self.__check_connection(): return None new_user_dialog = lumNewUserDialog(self.__datapath, self.__connection) try: new_user_dialog.run() except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to accomplish operation")) return None if new_user_dialog.usermodel is not None: if self.__check_connection(): new_user_dialog.usermodel.set_dn("uid=%s,%s" % (new_user_dialog.usermodel.get_username(), self.__users_ou)) try: self.__connection.add_user(new_user_dialog.usermodel) except LumAlreadyExistsError: show_error_dialog(_("User <b>%s</b> already exists in the ldap tree, " + "cannot create one more") % new_user_dialog.usermodel.get_username()) return None except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to create the user")) return None self.statusbar_update(_("User %s created correctly.") % new_user_dialog.usermodel.get_username()) # Reload user list because the dialog may have created a new group # and then pushing the user will not update the group list too self.reload_user_list() def new_group(self, menu_item = None): """Create a new group catching the callback from menu""" if not self.__check_connection(): return None new_group_dialog = lumNewGroupDialog(self.__datapath, self.__connection) group_name, gid = new_group_dialog.run() if group_name is not None: try: self.__connection.add_group(group_name, gid) except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to create group")) return None except LumAlreadyExistsError: show_error_dialog(_("Group <b>%s</b> already exists in the database," + " cannot add one more.") % group_name) return None self.__group_store.append(gid, group_name) self.statusbar_update(_("Group %s successfully created.") % group_name) def delete_group(self, menu_item = None): """Delete the group selected in the group_treeview""" if not self.__check_connection(): return None group, t_iter = self.__get_selected_group() # If nothing is selected we can return and do nothing. # We should only notify the user that what he would like # to do can not be done now. if t_iter is None: show_info_dialog(_("Select a group before asking for its deletion.")) return # Users tend to delete many things they do not want to delete if len(self.__connection.get_members(group)) == 0: question = _("Really delete group <b>%s</b>?") % group else: question = _("Really delete the non empty group <b>%s</b>?" + " This will lead to integrity problems.") % group if not ask_question(question): return # Finally we delete the group try: self.__connection.delete_group(group) except LumInsufficientPermissionsError: show_error_dialog(_("Insufficient permissions to delete group")) return None # and delete the group from the treeview self.__group_store.remove(t_iter) # Finally show the successful operation in the statusbar self.statusbar_update(_("Group %s successfully deleted.") % group) def group_properties(self, menu_item = None): """View selected group properties, such as members and gidNumber""" if not self.__check_connection(): return None group, t_iter = self.__get_selected_group() if t_iter is None: show_info_dialog(_("Select a group to view its properties")) # Get group_members, that are the only interesting property # we can get from ldap group_members = self.__connection.get_members(group) # Get gid gid = self.__group_store.get_gid(t_iter) # Show info dialog dialog_text = _("<b>Name:</b> %s\n") % group dialog_text += "<b>Gid</b>: %s\n" % gid if len(group_members) > 0: dialog_text += _("<b>Members</b>: ") dialog_text += ", ".join(group_members) else: dialog_text += _("This group is empty.") # Show info dialog. show_info_dialog(dialog_text) def __check_connection(self): if self.__connection is None: if ask_question(_("Not connected to any LDAP server, connect now?")): self.connect () else: return False if self.__connection is not None: return True else: show_error_dialog(_("Error while connecting to LDAP server, aborting operation.")) return False