/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2012 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.web.users; import static org.libreplan.web.I18nHelper._; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.libreplan.business.common.entities.Limits; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.resources.entities.Worker; import org.libreplan.business.users.entities.Profile; import org.libreplan.business.users.entities.User; import org.libreplan.business.users.entities.User.UserAuthenticationType; import org.libreplan.business.users.entities.UserRole; import org.libreplan.web.common.BaseCRUDController; import org.libreplan.web.common.ILimitsModel; import org.libreplan.web.common.Util; import org.libreplan.web.common.entrypoints.EntryPointsHandler; import org.libreplan.web.common.entrypoints.IURLHandlerRegistry; import org.libreplan.web.resources.worker.IWorkerCRUDControllerEntryPoints; import org.libreplan.web.security.SecurityUtils; import org.libreplan.web.users.bootstrap.PredefinedUsers; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zkplus.spring.SpringUtil; import org.zkoss.zul.Button; import org.zkoss.zul.Combobox; import org.zkoss.zul.Comboitem; import org.zkoss.zul.Constraint; import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.Textbox; import org.zkoss.zul.Groupbox; /** * Controller for CRUD actions over a {@link User} * * @author Jacobo Aragunde Perez <jaragunde@igalia.com> * @author Manuel Rego Casasnovas <rego@igalia.com> * @author Javier Moran Rua <jmoran@igalia.com> * @author Vova Perebykivskyi <vova@libreplan-enterprise.com> */ @SuppressWarnings("serial") public class UserCRUDController extends BaseCRUDController<User> implements IUserCRUDController { private IWorkerCRUDControllerEntryPoints workerCRUD; private ILimitsModel limitsModel; private IUserModel userModel; private Textbox passwordBox; private Textbox passwordConfirmationBox; private Combobox userRolesCombo; private Groupbox boundResourceGroupbox; private Combobox profilesCombo; private IURLHandlerRegistry URLHandlerRegistry; public UserCRUDController() { } private RowRenderer usersRenderer = (row, data, i) -> { final User user = (User) data; row.setValue(user); Util.appendLabel(row, user.getLoginName()); Util.appendLabel(row, user.isDisabled() ? _("Yes") : _("No")); Util.appendLabel(row, user.isSuperuser() ? _("Yes") : _("No")); Util.appendLabel(row, _(user.getUserType().toString())); Util.appendLabel(row, user.isBound() ? user.getWorker().getShortDescription() : ""); Button[] buttons = Util.appendOperationsAndOnClickEvent(row, event -> goToEditForm(user), event -> confirmDelete(user)); // Disable remove button for default admin as it's mandatory if ( isDefaultAdmin(user) ) { buttons[1].setDisabled(true); buttons[1].setTooltiptext(_("Default user \"admin\" cannot be removed as it is mandatory")); } }; @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); injectsObjects(); passwordBox = (Textbox) editWindow.getFellowIfAny("password"); passwordConfirmationBox = (Textbox) editWindow.getFellowIfAny("passwordConfirmation"); profilesCombo = (Combobox) editWindow.getFellowIfAny("profilesCombo"); userRolesCombo = (Combobox) editWindow.getFellowIfAny("userRolesCombo"); userRolesCombo.setWidth("320px"); appendAllUserRolesExceptRoleBoundUser(userRolesCombo); appendAllProfiles(profilesCombo); boundResourceGroupbox = (Groupbox) editWindow.getFellowIfAny("boundResourceGroupbox"); final EntryPointsHandler<IUserCRUDController> handler = URLHandlerRegistry.getRedirectorFor(IUserCRUDController.class); handler.register(this, page); } private void injectsObjects() { if ( userModel == null ) { userModel = (IUserModel) SpringUtil.getBean("userModel"); } if ( limitsModel == null ) { limitsModel = (ILimitsModel) SpringUtil.getBean("limitsModel"); } if ( workerCRUD == null ) { workerCRUD = (IWorkerCRUDControllerEntryPoints) SpringUtil.getBean("workerCRUD"); } if ( URLHandlerRegistry == null ) { URLHandlerRegistry = (IURLHandlerRegistry) SpringUtil.getBean("URLHandlerRegistry"); } } /** * Appends the existing UserRoles to the Combobox passed. * * @param combo */ private void appendAllUserRolesExceptRoleBoundUser(Combobox combo) { List<UserRole> roles = new ArrayList<>(Arrays.asList(UserRole.values())); roles.remove(UserRole.ROLE_BOUND_USER); // Sorting by ASC Collections.sort(roles, (arg0, arg1) -> _(arg0.getDisplayName()).compareTo(_(arg1.getDisplayName()))); for (UserRole role : roles) { Comboitem item = combo.appendItem(_(role.getDisplayName())); item.setValue(role); } } /** * Appends the existing Profiles to the Combobox passed. * * @param combo */ private void appendAllProfiles(Combobox combo) { List<Profile> profiles = userModel.getAllProfiles(); for (Profile profile : profiles) { Comboitem item = combo.appendItem(profile.getProfileName()); item.setValue(profile); } } public List<User> getUsers() { return userModel.getUsers(); } @Override protected void save() throws ValidationException { userModel.confirmSave(); PasswordUtil.showOrHideDefaultPasswordWarnings(); } public User getUser() { return userModel.getUser(); } public List<UserRole> getRoles() { return userModel.getRoles(); } public void addSelectedRole() { Comboitem comboItem = userRolesCombo.getSelectedItem(); if (comboItem != null) { addRole(comboItem.getValue()); userRolesCombo.removeItemAt(comboItem.getIndex()); userRolesCombo.setSelectedItem(null); userRolesCombo.setText(null); } } public void addRole(UserRole role) { userModel.addRole(role); Util.reloadBindings(editWindow); } public void removeRole(UserRole role) { userModel.removeRole(role); userRolesCombo.getItems().clear(); appendAllUserRolesExcept(UserRole.ROLE_BOUND_USER, userModel.getRoles(), userRolesCombo); Util.reloadBindings(editWindow); } /** * Appends the existing UserRoles to the {@link Combobox} passed. * * @param boundUserRole {@link UserRole#ROLE_BOUND_USER} that need to be excluded * @param userRoles a list of {@link UserRole} that need to be excluded * @param combo {@link Combobox} to which a list of user roles will be appended */ private void appendAllUserRolesExcept(UserRole boundUserRole, List<UserRole> userRoles, Combobox combo) { List<UserRole> roles = new ArrayList<>(Arrays.asList(UserRole.values())); roles.remove(boundUserRole); roles.removeAll(userRoles); // Sorting by ASC Collections.sort(roles, (arg0, arg1) -> _(arg0.getDisplayName()).compareTo(_(arg1.getDisplayName()))); for (UserRole role : roles) { Comboitem item = combo.appendItem(_(role.getDisplayName())); item.setValue(role); } } public List<Profile> getProfiles() { return userModel.getProfiles(); } public void addSelectedProfile() { Comboitem comboItem = profilesCombo.getSelectedItem(); if (comboItem != null) { addProfile(comboItem.getValue()); } } public void addProfile(Profile profile) { userModel.addProfile(profile); Util.reloadBindings(editWindow); } public void removeProfile(Profile profile) { userModel.removeProfile(profile); Util.reloadBindings(editWindow); } /** * Tells the XXXModel to set the password attribute of the inner {@link User} object. * * @param password String with the <b>unencrypted</b> password. */ public void setPassword(String password) { userModel.setPassword(password); // Update the constraint on the confirmation password box ((Textbox) editWindow.getFellowIfAny("passwordConfirmation")).clearErrorMessage(true); } public Constraint validatePasswordConfirmation() { return (comp, value) -> { ((Textbox) comp).setRawValue(value); if (!value.equals(passwordBox.getValue())) { throw new WrongValueException(comp, _("passwords don't match")); } }; } @Override protected String getEntityType() { return _("User"); } @Override protected String getPluralEntityType() { return _("Users"); } @Override protected void initCreate() { userModel.initCreate(); // Password is compulsory when creating passwordBox.setConstraint("no empty:" + _("Password cannot be empty")); // Clean the password boxes, they are not cleared automatically because they are not directly associated to an attribute passwordBox.setRawValue(""); passwordConfirmationBox.setRawValue(""); prepareAuthenticationTypesCombo(); } @Override protected void initEdit(User user) { userModel.initEdit(user); // Password is not compulsory when editing, so we remove the constraint passwordBox.setConstraint((Constraint)null); // Cleans the box and forces the check of the new Constraint (null) passwordBox.setValue(""); passwordConfirmationBox.setValue(""); // Setup authentication type combo box prepareAuthenticationTypesCombo(); } private void prepareAuthenticationTypesCombo() { Combobox combo = (Combobox) editWindow.getFellowIfAny("authenticationTypeCombo"); combo.getChildren().clear(); for (UserAuthenticationType type : UserAuthenticationType.values()) { Comboitem item = combo.appendItem(_(type.toString())); item.setValue(type); if (type.equals(getAuthenticationType())) { combo.setSelectedItem(item); } } Row comboRow = (Row) editWindow.getFellowIfAny("authenticationTypeComboRow"); comboRow.setVisible(true); } @Override protected User getEntityBeingEdited() { return userModel.getUser(); } @Override protected boolean beforeDeleting(User user) { Worker worker = user.getWorker(); return worker == null || Messagebox.show(_("User is bound to resource \"{0}\" and it will be unbound. " + "Do you want to continue with user removal?", worker.getShortDescription()), _("Confirm remove user"), Messagebox.YES | Messagebox.NO, Messagebox.QUESTION) == Messagebox.YES; } @Override protected void delete(User user) throws InstanceNotFoundException { userModel.confirmRemove(user); } public boolean isLdapUser() { User user = userModel.getUser(); return user != null && !user.isLibrePlanUser(); } public boolean isLdapUserLdapConfiguration() { return isLdapUser() && userModel.isLDAPBeingUsed(); } public boolean getLdapUserRolesLdapConfiguration() { return isLdapUser() && userModel.isLDAPRolesBeingUsed(); } public RowRenderer getRolesRenderer() { return (row, data, i) -> { final UserRole role = (UserRole) data; row.appendChild(new Label(_(role.getDisplayName()))); Button removeButton = Util.createRemoveButton(event -> removeRole(role)); removeButton.setDisabled(areRolesAndProfilesDisabled() || role.equals(UserRole.ROLE_BOUND_USER) || isUserDefaultAdmin()); row.appendChild(removeButton); }; } public RowRenderer getUsersRenderer() { return usersRenderer; } public String hasBoundResource() { User user = getUser(); return user != null && user.isBound() ? _("Yes") : _("No"); } public String getBoundResource() { User user = getUser(); return user != null && user.isBound() ? user.getWorker().getShortDescription() : ""; } public boolean isBound() { User user = getUser(); return user != null && user.isBound(); } public void goToWorkerEdition() { Worker worker = getUser().getWorker(); if (worker != null && showConfirmWorkerEditionDialog() == Messagebox.OK) { workerCRUD.goToEditForm(worker); } } private int showConfirmWorkerEditionDialog() { return Messagebox.show(_("Unsaved changes will be lost. Would you like to continue?"), _("Confirm edit worker"), Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION); } public void unboundResource() { userModel.unboundResource(); Util.reloadBindings(boundResourceGroupbox); } public boolean isNoRoleWorkers() { return !SecurityUtils.isSuperuserOrUserInRoles(UserRole.ROLE_WORKERS); } public String getWorkerEditionButtonTooltip() { return isNoRoleWorkers() ? _("You do not have permissions to go to edit worker window") : ""; } private boolean isDefaultAdmin(final User user) { return user.getLoginName().equals(PredefinedUsers.ADMIN.getLoginName()); } private boolean isUserDefaultAdmin() { User user = userModel.getUser(); return user != null && isDefaultAdmin(user); } public boolean areRolesAndProfilesDisabled() { return (isLdapUser() && userModel.isLDAPBeingUsed() && userModel.isLDAPRolesBeingUsed()) || isUserDefaultAdmin(); } public boolean isLdapUserOrDefaultAdmin() { return isLdapUser() || isUserDefaultAdmin(); } public UserAuthenticationType getAuthenticationType() { User user = getUser(); return user != null ? user.getUserType() : null; } public void setAuthenticationType(Comboitem item) { if (item == null) { throw new WrongValueException(editWindow.getFellowIfAny("authenticationTypeCombo"), _("cannot be empty")); } UserAuthenticationType authenticationType = item.getValue(); User user = getUser(); if (user != null) { user.setLibrePlanUser(authenticationType.equals(UserAuthenticationType.DATABASE)); } } /** * Should be public! * Used in _listUsers.zul */ public boolean isCreateButtonDisabled() { Limits usersTypeLimit = limitsModel.getUsersType(); if (isNullOrZeroValue(usersTypeLimit)) { return false; } else { Integer users = userModel.getRowCount().intValue(); return users >= usersTypeLimit.getValue(); } } /** * Should be public! * Used in _listUsers.zul */ public String getShowCreateFormLabel() { Limits usersTypeLimit = limitsModel.getUsersType(); if (isNullOrZeroValue(usersTypeLimit)) { return _("Create"); } Integer users = userModel.getRowCount().intValue(); int usersLeft = usersTypeLimit.getValue() - users; return users >= usersTypeLimit.getValue() ? _("User limit reached") : _("Create") + " ( " + usersLeft + " " + _("left") + " )"; } private boolean isNullOrZeroValue (Limits usersTypeLimit) { return usersTypeLimit == null || usersTypeLimit.getValue() == null || usersTypeLimit.getValue().equals(0); } }