com.haulmont.cuba.gui.app.security.user.edit.UserEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.gui.app.security.user.edit.UserEditor.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.haulmont.cuba.gui.app.security.user.edit;

import com.haulmont.bali.util.ParamsMap;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.client.ClientConfig;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.WindowManager.OpenType;
import com.haulmont.cuba.gui.WindowParam;
import com.haulmont.cuba.gui.WindowParams;
import com.haulmont.cuba.gui.app.security.events.UserPasswordChangedEvent;
import com.haulmont.cuba.gui.app.security.user.NameBuilderListener;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.actions.BaseAction;
import com.haulmont.cuba.gui.components.actions.ItemTrackingAction;
import com.haulmont.cuba.gui.components.actions.RemoveAction;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.data.impl.AbstractDatasource;
import com.haulmont.cuba.gui.data.impl.DatasourceImplementation;
import com.haulmont.cuba.gui.events.UserSubstitutionsChangedEvent;
import com.haulmont.cuba.gui.icons.CubaIcon;
import com.haulmont.cuba.gui.icons.Icons;
import com.haulmont.cuba.gui.theme.ThemeConstants;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import com.haulmont.cuba.security.entity.*;
import com.haulmont.cuba.security.global.UserSession;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;

import javax.inject.Inject;
import java.util.*;

import static com.haulmont.cuba.gui.components.PickerField.LookupAction;

public class UserEditor extends AbstractEditor<User> {

    @Inject
    protected DsContext dsContext;

    @Inject
    protected DataSupplier dataSupplier;

    @Inject
    protected Datasource<User> userDs;

    @Inject
    protected CollectionDatasource<UserRole, UUID> rolesDs;

    @Inject
    protected CollectionDatasource<UserSubstitution, UUID> substitutionsDs;

    @Inject
    protected Table<UserRole> rolesTable;

    @Inject
    protected Table<UserSubstitution> substTable;

    @Inject
    protected FieldGroup fieldGroupLeft;

    @Inject
    protected FieldGroup fieldGroupRight;

    @Inject
    protected Icons icons;

    protected PasswordField passwField;
    protected PasswordField confirmPasswField;
    protected LookupField languageLookup;
    protected LookupField timeZoneLookup;

    @Inject
    protected UserSession userSession;

    @Inject
    protected ComponentsFactory factory;

    @Inject
    protected Configuration configuration;

    @Inject
    protected Metadata metadata;

    @Inject
    protected Security security;

    @Inject
    protected PasswordEncryption passwordEncryption;

    @Inject
    protected ThemeConstants themeConstants;

    @Inject
    protected TimeZones timeZones;

    @Inject
    protected Events events;

    @WindowParam(name = "initCopy")
    protected Boolean initCopy;

    public interface Companion {
        void initPasswordField(PasswordField passwordField);
    }

    @Override
    public void init(Map<String, Object> params) {
        super.init(params);

        userDs.addItemPropertyChangeListener(new NameBuilderListener<>(userDs));
        userDs.addItemPropertyChangeListener(e -> {
            if ("timeZoneAuto".equals(e.getProperty())) {
                timeZoneLookup.setEnabled(!Boolean.TRUE.equals(e.getValue()));
            }
        });

        AddRoleAction addRoleAction = new AddRoleAction();
        addRoleAction.setEnabled(security.isEntityOpPermitted(UserRole.class, EntityOp.CREATE));
        rolesTable.addAction(addRoleAction);
        EditRoleAction editRoleAction = new EditRoleAction();
        rolesTable.addAction(editRoleAction);

        RemoveRoleAction removeRoleAction = new RemoveRoleAction(rolesTable, false);
        boolean isUserRoleDeletePermitted = security.isEntityOpPermitted(UserRole.class, EntityOp.DELETE);
        boolean isUserUpdatePermitted = security.isEntityOpPermitted(User.class, EntityOp.UPDATE);
        removeRoleAction.setEnabled(isUserRoleDeletePermitted && isUserUpdatePermitted);
        rolesTable.addAction(removeRoleAction);

        AddSubstitutedAction addSubstitutedAction = new AddSubstitutedAction();
        addSubstitutedAction.setEnabled(security.isEntityOpPermitted(UserSubstitution.class, EntityOp.CREATE));

        substTable.addAction(addSubstitutedAction);
        EditSubstitutedAction editSubstitutedAction = new EditSubstitutedAction();
        substTable.addAction(editSubstitutedAction);
        RemoveAction removeSubstitutedAction = new RemoveAction(substTable, false);
        substTable.addAction(removeSubstitutedAction);

        boolean isUserRoleCreatePermitted = security.isEntityOpPermitted(UserRole.class, EntityOp.CREATE);
        addRoleAction.setEnabled(isUserRoleCreatePermitted && isUserUpdatePermitted);

        boolean isSubstitutedUserCreatePermitted = security.isEntityOpPermitted(UserSubstitution.class,
                EntityOp.CREATE);
        addSubstitutedAction.setEnabled(isSubstitutedUserCreatePermitted && isUserUpdatePermitted);

        boolean isSubstitutedUserDeletePermitted = security.isEntityOpPermitted(UserSubstitution.class,
                EntityOp.DELETE);
        removeSubstitutedAction.setEnabled(isSubstitutedUserDeletePermitted && isUserUpdatePermitted);

        boolean isRoleUpdatePermitted = security.isEntityOpPermitted(Role.class, EntityOp.UPDATE);
        editRoleAction.setEnabled(isRoleUpdatePermitted);

        boolean isSubstitutedUserUpdatePermitted = security.isEntityOpPermitted(UserSubstitution.class,
                EntityOp.UPDATE);
        editSubstitutedAction.setEnabled(isSubstitutedUserUpdatePermitted);

        initCustomFields(PersistenceHelper.isNew(WindowParams.ITEM.getEntity(params)));

        dsContext.addAfterCommitListener((context, result) -> {
            updateSessionSubstitutions(result);

            if (passwField.getValue() != null) {
                publishPasswordChangedEvent(getItem(), passwField.getValue());
            }
        });
    }

    protected void updateSessionSubstitutions(Set<Entity> committedEntities) {
        for (Entity entity : committedEntities) {
            if (entity.equals(userSession.getUser())) {
                userSession.setUser((User) entity);
            }
            if (entity.equals(userSession.getSubstitutedUser())) {
                userSession.setSubstitutedUser((User) entity);
            }
        }

        if (userSession.getUser().equals(getItem())) {
            for (Entity entity : committedEntities) {
                if (entity instanceof UserSubstitution) {
                    publishUserSubstitutionsChanged(userSession.getUser());

                    break;
                }
            }
        }
    }

    @Override
    protected void postInit() {
        setCaption(PersistenceHelper.isNew(getItem()) ? getMessage("createCaption")
                : formatMessage("editCaption", getItem().getLogin()));

        timeZoneLookup.setEnabled(!Boolean.TRUE.equals(getItem().getTimeZoneAuto()));

        // Do not show roles which are not allowed by security constraints
        LoadContext<Role> lc = new LoadContext<>(Role.class);
        lc.setQueryString("select r from sec$Role r");
        lc.setView(View.MINIMAL);
        List<Role> allowedRoles = dataSupplier.loadList(lc);

        Collection<UserRole> userRoles = new ArrayList<>(rolesDs.getItems());
        for (UserRole userRole : userRoles) {
            if (!allowedRoles.contains(userRole.getRole())) {
                rolesDs.excludeItem(userRole);
            }
        }

        if (BooleanUtils.isTrue(initCopy)) {
            initCopy();
        }

        // if we add default roles, rolesDs becomes modified on setItem
        ((AbstractDatasource) rolesDs).setModified(false);
    }

    @Override
    protected void initNewItem(User item) {
        addDefaultRoles(item);
        item.setLanguage(messages.getTools().localeToString(userSession.getLocale()));
        initUserGroup(item);
    }

    protected void initUserGroup(User user) {
        LoadContext<Group> ctx = new LoadContext<>(Group.class);
        ctx.setQueryString("select g from sec$Group g");
        ctx.setView(View.MINIMAL);
        List<Group> groups = dataSupplier.loadList(ctx);
        if (groups.size() == 1) {
            user.setGroup(groups.get(0));
        }
    }

    protected void addDefaultRoles(User user) {
        LoadContext<Role> ctx = new LoadContext<>(Role.class);
        ctx.setQueryString("select r from sec$Role r where r.defaultRole = true");
        List<Role> defaultRoles = dataSupplier.loadList(ctx);

        List<UserRole> newRoles = new ArrayList<>();
        if (user.getUserRoles() != null) {
            newRoles.addAll(user.getUserRoles());
        }

        MetaClass metaClass = rolesDs.getMetaClass();
        for (Role role : defaultRoles) {
            UserRole userRole = dataSupplier.newInstance(metaClass);
            userRole.setRole(role);
            userRole.setUser(user);
            newRoles.add(userRole);
        }

        user.setUserRoles(newRoles);
    }

    protected void initCustomFields(boolean isNew) {
        createPasswordFields(isNew);

        createLanguageLookup();

        createTimeZoneField();

        createGroupField();
    }

    protected void createTimeZoneField() {
        FieldGroup.FieldConfig timeZoneFc = fieldGroupRight.getFieldNN("timeZone");

        HBoxLayout hbox = factory.createComponent(HBoxLayout.class);
        hbox.setSpacing(true);

        timeZoneLookup = factory.createComponent(LookupField.class);

        timeZoneLookup.setDatasource(timeZoneFc.getTargetDatasource(), timeZoneFc.getProperty());
        timeZoneLookup.setRequired(false);

        MetaClass userMetaClass = userDs.getMetaClass();
        timeZoneLookup.setEditable(fieldGroupRight.isEditable()
                && security.isEntityAttrUpdatePermitted(userMetaClass, timeZoneFc.getProperty()));

        Map<String, Object> options = new TreeMap<>();
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone timeZone = TimeZone.getTimeZone(id);
            options.put(timeZones.getDisplayNameLong(timeZone), id);
        }
        timeZoneLookup.setOptionsMap(options);

        hbox.add(timeZoneLookup);

        CheckBox autoDetectField = factory.createComponent(CheckBox.class);
        autoDetectField.setDatasource(timeZoneFc.getTargetDatasource(), "timeZoneAuto");
        autoDetectField.setCaption(messages.getMainMessage("timeZone.auto"));
        autoDetectField.setDescription(messages.getMainMessage("timeZone.auto.descr"));
        autoDetectField.setAlignment(Alignment.MIDDLE_RIGHT);

        autoDetectField.setEditable(fieldGroupRight.isEditable()
                && security.isEntityAttrUpdatePermitted(userMetaClass, "timeZoneAuto"));

        hbox.add(autoDetectField);
        hbox.expand(timeZoneLookup);

        timeZoneFc.setComponent(hbox);
    }

    protected void createGroupField() {
        FieldGroup.FieldConfig groupFc = fieldGroupRight.getFieldNN("group");

        PickerField pickerField = factory.createComponent(PickerField.class);

        pickerField.setDatasource(groupFc.getTargetDatasource(), groupFc.getProperty());
        pickerField.setRequired(true);
        pickerField.setRequiredMessage(getMessage("groupMsg"));

        LookupAction action = LookupAction.create(pickerField);
        action.setLookupScreenOpenType(OpenType.DIALOG);
        action.setLookupScreenParamsSupplier(() -> {
            if (getItem().getGroup() != null) {
                return ParamsMap.of("selectedGroup", getItem().getGroup());
            }
            return Collections.emptyMap();
        });
        pickerField.addAction(action);

        groupFc.setComponent(pickerField);
    }

    protected void createPasswordFields(boolean isNew) {
        passwField = factory.createComponent(PasswordField.class);
        if (isNew) {
            passwField.setRequiredMessage(getMessage("passwMsg"));

            Companion companion = getCompanion();
            if (companion != null) {
                companion.initPasswordField(passwField);
            } else {
                passwField.setRequired(true);
            }
            passwField.addValueChangeListener(e -> ((DatasourceImplementation) userDs).setModified(true));
        } else {
            passwField.setVisible(false);
        }
        fieldGroupLeft.getFieldNN("passw").setComponent(passwField);

        confirmPasswField = factory.createComponent(PasswordField.class);
        if (isNew) {
            confirmPasswField.setRequiredMessage(getMessage("confirmPasswMsg"));

            Companion companion = getCompanion();
            if (companion != null) {
                companion.initPasswordField(confirmPasswField);
            } else {
                confirmPasswField.setRequired(true);
            }
            confirmPasswField.addValueChangeListener(e -> ((DatasourceImplementation) userDs).setModified(true));
        } else {
            confirmPasswField.setVisible(false);
        }
        fieldGroupLeft.getFieldNN("confirmPassw").setComponent(confirmPasswField);
    }

    protected void createLanguageLookup() {
        languageLookup = factory.createComponent(LookupField.class);
        FieldGroup.FieldConfig languageLookupFc = fieldGroupRight.getFieldNN("language");
        languageLookup.setDatasource(languageLookupFc.getTargetDatasource(), languageLookupFc.getProperty());
        languageLookup.setRequired(false);

        Map<String, Locale> locales = configuration.getConfig(GlobalConfig.class).getAvailableLocales();
        Map<String, Object> options = new TreeMap<>();
        for (Map.Entry<String, Locale> entry : locales.entrySet()) {
            options.put(entry.getKey(), messages.getTools().localeToString(entry.getValue()));
        }
        languageLookup.setOptionsMap(options);
        languageLookupFc.setComponent(languageLookup);
    }

    @Override
    protected boolean preCommit() {
        if (rolesDs.isModified()) {
            @SuppressWarnings("unchecked")
            DatasourceImplementation<UserRole> rolesDsImpl = (DatasourceImplementation) rolesDs;

            CommitContext ctx = new CommitContext(Collections.emptyList(), rolesDsImpl.getItemsToDelete());
            dataSupplier.commit(ctx);

            List<UserRole> modifiedRoles = new ArrayList<>(rolesDsImpl.getItemsToCreate());
            modifiedRoles.addAll(rolesDsImpl.getItemsToUpdate());
            rolesDsImpl.committed(Collections.emptySet());
            for (UserRole userRole : modifiedRoles) {
                rolesDsImpl.modified(userRole);
            }
        }

        User user = getItem();

        if (PersistenceHelper.isNew(user)) {
            String password = passwField.getValue();
            String passwordConfirmation = confirmPasswField.getValue();

            if (passwField.isRequired()
                    && (StringUtils.isBlank(password) || StringUtils.isBlank(passwordConfirmation))) {
                showNotification(getMessage("emptyPassword"), NotificationType.WARNING);
                return false;
            } else {
                if (Objects.equals(password, passwordConfirmation)) {
                    if (StringUtils.isNotEmpty(password)) {
                        ClientConfig passwordPolicyConfig = configuration.getConfig(ClientConfig.class);
                        if (passwordPolicyConfig.getPasswordPolicyEnabled()) {
                            String regExp = passwordPolicyConfig.getPasswordPolicyRegExp();
                            if (!password.matches(regExp)) {
                                showNotification(getMessage("simplePassword"), NotificationType.WARNING);
                                return false;
                            }
                        }

                        String passwordHash = passwordEncryption.getPasswordHash(user.getId(), password);
                        user.setPassword(passwordHash);
                    }

                    return true;
                } else {
                    showNotification(getMessage("passwordsDoNotMatch"), NotificationType.WARNING);
                    return false;
                }
            }
        } else {
            return true;
        }
    }

    public void initCopy() {
        @SuppressWarnings("unchecked")
        DatasourceImplementation<UserRole> rolesDsImpl = (DatasourceImplementation) rolesDs;
        for (UserRole item : rolesDs.getItems()) {
            rolesDsImpl.modified(item);
        }
    }

    protected void publishUserSubstitutionsChanged(User user) {
        events.publish(new UserSubstitutionsChangedEvent(user));
    }

    protected void publishPasswordChangedEvent(User user, String newPassword) {
        events.publish(new UserPasswordChangedEvent(this, user, newPassword));
    }

    protected class AddRoleAction extends AbstractAction {
        public AddRoleAction() {
            super("add");

            icon = icons.get(CubaIcon.ADD_ACTION);

            setCaption(getMessage("actions.Add"));

            ClientConfig clientConfig = configuration.getConfig(ClientConfig.class);
            setShortcut(clientConfig.getTableAddShortcut());
        }

        protected Collection<String> getExistingRoleNames() {
            User user = userDs.getItem();
            Collection<String> existingRoleNames = new HashSet<>();
            if (user.getUserRoles() != null) {
                for (UserRole userRole : user.getUserRoles()) {
                    if (userRole.getRole() != null)
                        existingRoleNames.add(userRole.getRole().getName());
                }
            }
            return existingRoleNames;
        }

        @Override
        public void actionPerform(Component component) {
            Lookup roleLookupWindow = openLookup(Role.class, items -> {
                Collection<String> existingRoleNames = getExistingRoleNames();
                rolesDs.suspendListeners();
                try {
                    for (Object item : items) {
                        Role role = (Role) item;

                        if (existingRoleNames.contains(role.getName())) {
                            continue;
                        }

                        MetaClass metaClass = rolesDs.getMetaClass();
                        UserRole userRole = dataSupplier.newInstance(metaClass);
                        userRole.setRole(role);
                        userRole.setUser(userDs.getItem());

                        rolesDs.addItem(userRole);
                        existingRoleNames.add(role.getName());
                    }
                } finally {
                    rolesDs.resumeListeners();
                }
            }, OpenType.THIS_TAB, ParamsMap.of("windowOpener", "sec$User.edit"));

            roleLookupWindow.addCloseListener(actionId -> {
                rolesTable.requestFocus();
            });

            Component lookupComponent = roleLookupWindow.getLookupComponent();
            if (lookupComponent instanceof Table) {
                ((Table) lookupComponent).setMultiSelect(true);
            }
        }
    }

    protected class EditRoleAction extends ItemTrackingAction {
        public EditRoleAction() {
            super("edit");

            icon = icons.get(CubaIcon.EDIT_ACTION);

            setCaption(getMessage("actions.Edit"));
        }

        @Override
        public void actionPerform(Component component) {
            if (rolesDs.getItem() == null)
                return;

            Window window = openEditor("sec$Role.edit", rolesDs.getItem().getRole(), OpenType.THIS_TAB);
            window.addCloseListener(actionId -> {
                if (Window.COMMIT_ACTION_ID.equals(actionId)) {
                    rolesDs.refresh();
                }
                rolesTable.requestFocus();
            });
        }
    }

    protected class RemoveRoleAction extends RemoveAction {

        private boolean hasDefaultRole = false;

        public RemoveRoleAction(ListComponent owner, boolean autocommit) {
            super(owner, autocommit);
        }

        @Override
        protected void confirmAndRemove(Set<Entity> selected) {
            hasDefaultRole = hasDefaultRole(selected);

            super.confirmAndRemove(selected);
        }

        @Override
        public String getConfirmationMessage() {
            if (hasDefaultRole)
                return getMessage("dialogs.Confirmation.RemoveDefaultRole");
            else
                return super.getConfirmationMessage();
        }

        private boolean hasDefaultRole(Set selected) {
            for (Object roleObj : selected) {
                UserRole role = (UserRole) roleObj;
                if (Boolean.TRUE.equals(role.getRole().getDefaultRole()))
                    return true;
            }
            return false;
        }
    }

    protected class AddSubstitutedAction extends BaseAction {
        public AddSubstitutedAction() {
            super("add");

            icon = icons.get(CubaIcon.ADD_ACTION);

            ClientConfig clientConfig = configuration.getConfig(ClientConfig.class);
            setShortcut(clientConfig.getTableAddShortcut());
        }

        @Override
        public void actionPerform(Component component) {
            UserSubstitution substitution = metadata.create(UserSubstitution.class);
            substitution.setUser(userDs.getItem());

            Window editor = openEditor(substitution, OpenType.DIALOG, ParamsMap.empty(), substitutionsDs);
            editor.addCloseListener(actionId -> {
                substTable.requestFocus();
            });
        }
    }

    protected class EditSubstitutedAction extends ItemTrackingAction {
        public EditSubstitutedAction() {
            super("edit");

            icon = icons.get(CubaIcon.EDIT_ACTION);
        }

        @Override
        public void actionPerform(Component component) {
            if (substitutionsDs.getItem() != null) {
                Window editor = openEditor(substitutionsDs.getItem(), OpenType.DIALOG, ParamsMap.empty(),
                        substitutionsDs);
                editor.addCloseListener(actionId -> {
                    substTable.requestFocus();
                });
            }
        }
    }
}