From 3ef56da8511b4a52c59958819e167f411f63d5ee Mon Sep 17 00:00:00 2001 From: Nils Christian Ehmke <nie@informatik.uni-kiel.de> Date: Sun, 25 Nov 2012 15:57:35 +0100 Subject: [PATCH] Corrected and advanced the user management --- .../persistence/impl/DerbyUserDAOImpl.java | 2 + .../beans/view/CurrentUserManagementBean.java | 120 +++++++++++++++--- .../lang/UserManagementPage_de.properties | 1 + .../lang/UserManagementPage_en.properties | 1 + .../dialogs/UserManagementDialogs.xhtml | 75 ++++++++++- .../pages/admin/UserManagementPage.xhtml | 7 +- 6 files changed, 180 insertions(+), 26 deletions(-) diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java index b7756eda..b64ce0df 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java @@ -163,11 +163,13 @@ public class DerbyUserDAOImpl implements IUserDAO { // Choose the right update command, depending on whether the password has to be changed or not if (user.getPassword() == null) { updateCmd = this.connection.prepareStatement("UPDATE Users SET name=?, isGuest=?, isUser=?, isAdministrator=?, isEnabled=? WHERE name=?"); + updateCmd.setString(6, user.getName()); } else { // In this case we have to set the password as well updateCmd = this.connection .prepareStatement("UPDATE Users SET name=?, isGuest=?, isUser=?, isAdministrator=?, isEnabled=?, password=? WHERE name=?"); updateCmd.setString(6, user.getPassword()); + updateCmd.setString(7, user.getName()); } // Set the other values diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java index a87f46a0..1af38a82 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java @@ -26,6 +26,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import kieker.common.logging.Log; +import kieker.common.logging.LogFactory; import kieker.webgui.common.exception.DataAccessException; import kieker.webgui.domain.User; import kieker.webgui.domain.User.Role; @@ -33,16 +35,24 @@ import kieker.webgui.service.IUserService; import kieker.webgui.web.beans.application.GlobalPropertiesBean; /** + * The {@link CurrentUserManagementBean} contains the necessary data behind an instance of the user management page. + * The class is a Spring managed bean with view scope to make sure that one user (even in one session) can open multiple instances of the page at a time without + * causing any problems. + * * @author Nils Christian Ehmke */ @Component @Scope("view") public final class CurrentUserManagementBean { + private static final Log LOG = LogFactory.getLog(CurrentUserManagementBean.class); + @Autowired private IUserService userService; - private List<User> availableUses = new ArrayList<User>(); + private List<User> availableUsers = new ArrayList<User>(); + private User selectedUserCopy; + private User selectedUser; /** * Default constructor. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> @@ -55,7 +65,7 @@ public final class CurrentUserManagementBean { * This method initializes the bean. <b>Do not call this method manually. It will only be accessed by Spring.</b> */ @PostConstruct - protected void initialialize() { + protected synchronized void initialialize() { this.updateList(); } @@ -71,44 +81,84 @@ public final class CurrentUserManagementBean { * @param isEnabled * Determines whether the user is enabled already or not. */ - public void addUser(final String username, final String password, final Role role, final boolean isEnabled) { + public synchronized void addUser(final String username, final String password, final Role role, final boolean isEnabled) { final User user = new User(username, password, role, isEnabled); try { + // Add the user to the database and - if that didn't fail - to our list. this.userService.addUser(user); - this.availableUses.add(user); + this.availableUsers.add(user); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, "Added user to the database."); } catch (final DataAccessException ex) { + CurrentUserManagementBean.LOG.error("Could not add the user to the database.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "Could not add the user to the database."); } } /** - * This method removed the given user from the database and informs about success via the growl component. - * - * @param user - * The user to be deleted. + * This method removes the currently selected user from the database and informs about success via the growl component. */ - public void deleteUser(final User user) { + public synchronized void deleteUser() { try { - this.userService.deleteUser(user); - this.availableUses.remove(user); + this.userService.deleteUser(this.selectedUser); + this.availableUsers.remove(this.selectedUser); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, "Deleted user from the database."); } catch (final DataAccessException ex) { + CurrentUserManagementBean.LOG.error("Could not delete the user from the database.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "Could not delete the user from the database."); } } /** - * This method edits the given user within the database and informs about success via the growl component. + * This method edits the currently selected user within the database and informs about success via the growl component. The password will not be changed. + */ + public synchronized void editUser() { + try { + // This is the "normal" edit - make sure that the password won't be changed + this.selectedUserCopy.setPassword(null); + // Edit the user and replace the entry within our list + this.userService.editUser(this.selectedUserCopy); + this.availableUsers.set(this.availableUsers.indexOf(this.selectedUser), this.selectedUserCopy); + + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, "Modified user within the database."); + } catch (final DataAccessException ex) { + CurrentUserManagementBean.LOG.error("Could not modify the user within the database.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "Could not modify the user within the database."); + } + } + + /** + * This method edits the currently selected user within the database and informs about success via the growl component. The password will be changed. + */ + public synchronized void editUserWithPassword() { + try { + // Edit the user completely and replace the entry within our list + this.userService.editUser(this.selectedUserCopy); + this.availableUsers.set(this.availableUsers.indexOf(this.selectedUser), this.selectedUserCopy); + + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, "Modified user within the database."); + } catch (final DataAccessException ex) { + CurrentUserManagementBean.LOG.error("Could not modify the user within the database.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "Could not modify the user within the database."); + } + } + + /** + * This method changes the enabled state of the given user and informs about success via the growl component. * * @param user - * The user to be modified. + * The user whose state should be changed. */ - public void editUser(final User user) { + public synchronized void changeUserEnableState(final User user) { try { - this.userService.editUser(user); + this.setSelectedUser(user); + this.selectedUserCopy.setEnabled(!this.selectedUserCopy.isEnabled()); + this.selectedUserCopy.setPassword(null); + this.userService.editUser(this.selectedUserCopy); + + this.availableUsers.set(this.availableUsers.indexOf(this.selectedUser), this.selectedUserCopy); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, "Modified user within the database."); } catch (final DataAccessException ex) { + CurrentUserManagementBean.LOG.error("Could not modify the user within the database.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "Could not modify the user within the database."); } } @@ -116,10 +166,11 @@ public final class CurrentUserManagementBean { /** * Updates the list containing the available users. */ - public void updateList() { + public synchronized void updateList() { try { - this.availableUses = this.userService.getUsers(); + this.availableUsers = this.userService.getUsers(); } catch (final DataAccessException ex) { + CurrentUserManagementBean.LOG.error("An error occured while accessing the database.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while accessing the database."); } } @@ -129,7 +180,38 @@ public final class CurrentUserManagementBean { * * @return All currently available users. */ - public List<User> getUsers() { - return this.availableUses; + public synchronized List<User> getUsers() { + return this.availableUsers; + } + + /** + * Getter for the property {@link CurrentUserManagementBean#selectedUser}. + * + * @return The current value of the property. + */ + public User getSelectedUser() { + return this.selectedUser; + } + + /** + * Getter for the property {@link CurrentUserManagementBean#selectedUserCopy}. + * + * @return The current value of the property. + */ + public User getSelectedUserCopy() { + return this.selectedUserCopy; + } + + /** + * Setter for the property {@link CurrentUserManagementBean#selectedUser}. + * + * @param selectedUser + * The new value of the property. + */ + public void setSelectedUser(final User selectedUser) { + // We remember the selected user, but we make also a copy. This is necessary, because otherwise we would have to do something like a rollback, if for example + // an edit within the DB fails. + this.selectedUser = selectedUser; + this.selectedUserCopy = new User(selectedUser.getName(), null, selectedUser.getRole(), selectedUser.isEnabled()); } } diff --git a/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_de.properties b/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_de.properties index 878f37ba..2669b5ff 100644 --- a/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_de.properties +++ b/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_de.properties @@ -13,6 +13,7 @@ editUser = Benutzer Bearbeiten disableUser = Benutzer Deaktivieren enableUser = Benutzer Aktivieren deleteUser = Benutzer L\u00f6schen +editPassword = Password Ändern username = Benutzername userrole = Benutzerrolle diff --git a/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_en.properties b/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_en.properties index f705fdec..a76b097a 100644 --- a/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_en.properties +++ b/Kieker.WebGUI/src/main/resources/lang/UserManagementPage_en.properties @@ -13,6 +13,7 @@ editUser = Edit User disableUser = Disable User enableUser = Enable User deleteUser = Delete User +editPassword = Edit Password username = Username userrole = Userrole diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/UserManagementDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/UserManagementDialogs.xhtml index fb9e94bc..ce56e1e7 100644 --- a/Kieker.WebGUI/src/main/webapp/dialogs/UserManagementDialogs.xhtml +++ b/Kieker.WebGUI/src/main/webapp/dialogs/UserManagementDialogs.xhtml @@ -3,17 +3,18 @@ xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" - xmlns:p="http://primefaces.org/ui"> + xmlns:p="http://primefaces.org/ui" + xmlns:c="http://java.sun.com/jsp/jstl/core"> <p:dialog id="newUserDialog" header="Neuer Benutzer" resizable="false" modal="true" widgetVar="newUserDlg"> - <!-- Make sure that closing of the dialog also clears the input field. --> + <!-- Make sure that closing of the dialog also clears the input fields. --> <p:ajax event="close" update=":newUserDialogForm" /> <h:form id="newUserDialogForm"> <h:panelGrid columnClasses="col1 , col2" columns="2" cellpadding="5"> - <h:outputText value="Benutzername: " /> + <h:outputText value="#{localizedUserManagementMessages.username}: " /> <p:inputText value="#{newUserBean.username}" style="width: 100%" /> - <h:outputText value="Passwort: " /> + <h:outputText value="#{localizedUserManagementMessages.password}: " /> <p:password feedback="true" value="#{newUserBean.password}" style="width: 100%" /> <h:outputText value="Benutzerrolle: " /> @@ -36,4 +37,70 @@ </div> </h:form> </p:dialog> + + <p:dialog id="editUserDialog" header="Benutzer Bearbeiten" resizable="false" modal="true" widgetVar="editUserDlg"> + <!-- Make sure that closing of the dialog also clears the input field. --> + <p:ajax event="close" update=":editUserDialogForm" /> + <h:form id="editUserDialogForm"> + <ui:fragment rendered="#{not empty currentUserManagementBean.selectedUser}"> + <h:panelGrid columnClasses="col1 , col2" columns="2" cellpadding="5"> + <h:outputText value="#{localizedUserManagementMessages.username}: " /> + <p:inputText value="#{currentUserManagementBean.selectedUserCopy.name}" style="width: 100%" disabled="true" /> + + <h:outputText value="Benutzerrolle: " /> + <p:selectOneRadio value="#{currentUserManagementBean.selectedUserCopy.role}" converter="roleStringConverter"> + <f:selectItem itemLabel="Gast" itemValue="#{ROLE_GUEST}" /> + <f:selectItem itemLabel="Benutzer" itemValue="#{ROLE_USER}" /> + <f:selectItem itemLabel="Administrator" itemValue="#{ROLE_ADMIN}" /> + </p:selectOneRadio> + + <h:outputText value="Status: " /> + <p:selectOneRadio value="#{currentUserManagementBean.selectedUserCopy.enabled}"> + <f:selectItem itemLabel="Aktiviert" itemValue="#{true}" /> + <f:selectItem itemLabel="Deaktiviert" itemValue="#{false}" /> + </p:selectOneRadio> + </h:panelGrid> + </ui:fragment> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="#{localizedMessages.ok}" action="#{currentUserManagementBean.editUser()}" oncomplete="editUserDlg.hide()" ajax="true" update=":messages :usersListForm"/> + </div> + </h:form> + </p:dialog> + + <p:dialog id="editPasswordDialog" header="Passwort Bearbeiten" resizable="false" modal="true" widgetVar="editPasswordDlg"> + <!-- Make sure that closing of the dialog also clears the input field. --> + <p:ajax event="close" update=":editPasswordDialogForm" /> + <h:form id="editPasswordDialogForm"> + <ui:fragment rendered="#{not empty currentUserManagementBean.selectedUser}"> + <h:panelGrid columnClasses="col1 , col2" columns="2" cellpadding="5"> + <h:outputText value="Benutzername: " /> + <p:inputText value="#{currentUserManagementBean.selectedUserCopy.name}" style="width: 100%" disabled="true" /> + + <h:outputText value="Passwort: " /> + <p:password feedback="true" value="#{currentUserManagementBean.selectedUserCopy.password}" style="width: 100%" /> + </h:panelGrid> + </ui:fragment> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="#{localizedMessages.ok}" action="#{currentUserManagementBean.editUserWithPassword()}" oncomplete="editPasswordDlg.hide()" ajax="true" update=":messages :usersListForm"/> + </div> + </h:form> + </p:dialog> + + <p:dialog id="deleteUserDialog" header="#{localizedProjectOverviewMessages.deleteProject}" resizable="false" modal="true" widgetVar="deleteUserDlg"> + <h:form> + <div style="text-align: center"> + <h:outputText value="Wollen Sie den Benutzer wirklich löschen?" /> + </div> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="#{localizedMessages.ok}" action="#{currentUserManagementBean.deleteUser()}" update=":usersListForm :messages" oncomplete="deleteUserDlg.hide()" /> + </div> + </h:form> + </p:dialog> + </ui:composition> \ No newline at end of file diff --git a/Kieker.WebGUI/src/main/webapp/pages/admin/UserManagementPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/admin/UserManagementPage.xhtml index 0a341ca0..8fe6135f 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/admin/UserManagementPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/admin/UserManagementPage.xhtml @@ -41,10 +41,11 @@ <p:commandLink id="dynaButton" value="#{user.name}"/> <p:menu overlay="true" trigger="dynaButton" my="left top" at="left bottom" style="width:210px"> - <p:menuitem icon="ui-icon-edit" styleClass="element-with-whitespace" value=" #{localizedUserManagementMessages.editUser}"/> - <p:menuitem icon="ui-icon-userEnableDisable" styleClass="element-with-whitespace" value=" #{user.enabled ? localizedUserManagementMessages.disableUser : localizedUserManagementMessages.enableUser}"/> + <p:menuitem icon="ui-icon-edit" styleClass="element-with-whitespace" value=" #{localizedUserManagementMessages.editUser}" action="#{currentUserManagementBean.setSelectedUser(user)}" update=":editUserDialogForm" onclick="editUserDlg.show();"/> + <p:menuitem icon="ui-icon-edit" styleClass="element-with-whitespace" value=" #{localizedUserManagementMessages.editPassword}" action="#{currentUserManagementBean.setSelectedUser(user)}" update=":editPasswordDialogForm" onclick="editPasswordDlg.show();"/> + <p:menuitem icon="ui-icon-userEnableDisable" styleClass="element-with-whitespace" value=" #{user.enabled ? localizedUserManagementMessages.disableUser : localizedUserManagementMessages.enableUser}" action="#{currentUserManagementBean.changeUserEnableState(user)}" update=":messages :usersListForm"/> <p:separator/> - <p:menuitem icon="ui-icon-delete" styleClass="element-with-whitespace" value=" #{localizedUserManagementMessages.deleteUser}"/> + <p:menuitem icon="ui-icon-delete" styleClass="element-with-whitespace" value=" #{localizedUserManagementMessages.deleteUser}" action="#{currentUserManagementBean.setSelectedUser(user)}" onclick="deleteUserDlg.show();" update=":messages"/> </p:menu> </p:column> -- GitLab