org.jboss.hal.client.accesscontrol.RoleColumn.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.hal.client.accesscontrol.RoleColumn.java

Source

/*
 * Copyright 2015-2016 Red Hat, Inc, and individual contributors.
 *
 * 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
 *
 * https://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 org.jboss.hal.client.accesscontrol;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Provider;

import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.web.bindery.event.shared.EventBus;
import elemental2.dom.HTMLElement;
import org.jboss.hal.ballroom.LabelBuilder;
import org.jboss.hal.ballroom.autocomplete.ReadChildrenAutoComplete;
import org.jboss.hal.ballroom.dialog.DialogFactory;
import org.jboss.hal.ballroom.form.Form;
import org.jboss.hal.ballroom.form.SingleSelectBoxItem;
import org.jboss.hal.ballroom.form.SwitchItem;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.AddRoleMapping;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.AddScopedRole;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.CheckRoleMapping;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.ModifyIncludeAll;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.ModifyScopedRole;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.RemoveAssignments;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.RemoveRoleMapping;
import org.jboss.hal.client.accesscontrol.AccessControlTasks.RemoveScopedRole;
import org.jboss.hal.config.Environment;
import org.jboss.hal.config.Role;
import org.jboss.hal.config.RolesChangedEvent;
import org.jboss.hal.config.Settings;
import org.jboss.hal.core.SuccessfulOutcome;
import org.jboss.hal.core.finder.ColumnAction;
import org.jboss.hal.core.finder.ColumnActionFactory;
import org.jboss.hal.core.finder.Finder;
import org.jboss.hal.core.finder.FinderColumn;
import org.jboss.hal.core.finder.ItemAction;
import org.jboss.hal.core.finder.ItemDisplay;
import org.jboss.hal.core.mbui.dialog.AddResourceDialog;
import org.jboss.hal.core.mbui.dialog.ModifyResourceDialog;
import org.jboss.hal.core.mbui.dialog.NameItem;
import org.jboss.hal.core.mbui.form.ModelNodeForm;
import org.jboss.hal.dmr.ModelNode;
import org.jboss.hal.dmr.dispatch.Dispatcher;
import org.jboss.hal.flow.FlowContext;
import org.jboss.hal.flow.Progress;
import org.jboss.hal.flow.Task;
import org.jboss.hal.meta.AddressTemplate;
import org.jboss.hal.meta.Metadata;
import org.jboss.hal.meta.MetadataRegistry;
import org.jboss.hal.meta.StatementContext;
import org.jboss.hal.resources.Ids;
import org.jboss.hal.resources.Resources;
import org.jboss.hal.spi.AsyncColumn;
import org.jboss.hal.spi.Footer;
import org.jboss.hal.spi.Message;
import org.jboss.hal.spi.MessageEvent;
import org.jboss.hal.spi.Requires;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import static org.jboss.gwt.elemento.core.Elements.small;
import static org.jboss.gwt.elemento.core.Elements.span;
import static org.jboss.hal.client.accesscontrol.AddressTemplates.*;
import static org.jboss.hal.config.Settings.Key.RUN_AS;
import static org.jboss.hal.dmr.ModelDescriptionConstants.*;
import static org.jboss.hal.flow.Flow.series;
import static org.jboss.hal.resources.CSS.itemText;
import static org.jboss.hal.resources.CSS.pfIcon;
import static org.jboss.hal.resources.CSS.subtitle;

@AsyncColumn(Ids.ROLE)
@Requires({ ROLE_MAPPING_ADDRESS, HOST_SCOPED_ROLE_ADDRESS, SERVER_GROUP_SCOPED_ROLE_ADDRESS })
public class RoleColumn extends FinderColumn<Role> {

    static List<String> filterData(Role role) {
        List<String> data = new ArrayList<>();
        data.add(role.getName());
        if (role.isScoped()) {
            data.add("scoped"); //NON-NLS
            data.add(role.getType().name().toLowerCase());
            data.add(String.join(" ", role.getScope()));
        } else {
            data.add("standard"); //NON-NLS
        }
        return data;
    }

    private final MetadataRegistry metadataRegistry;
    private final StatementContext statementContext;
    private final Dispatcher dispatcher;
    private final EventBus eventBus;
    private final Provider<Progress> progress;
    private final AccessControl accessControl;
    private final Resources resources;
    private final List<String> standardRoleNames;

    @Inject
    public RoleColumn(Finder finder, ColumnActionFactory columnActionFactory, MetadataRegistry metadataRegistry,
            StatementContext statementContext, Dispatcher dispatcher, EventBus eventBus,
            @Footer Provider<Progress> progress, AccessControl accessControl, AccessControlTokens tokens,
            Environment environment, Settings settings, Resources resources) {

        super(new Builder<Role>(finder, Ids.ROLE, resources.constants().role())
                .itemsProvider((context, callback) -> {
                    List<Role> roles = new ArrayList<>();
                    accessControl.roles().standardRoles().stream().sorted(comparing(Role::getName))
                            .forEach(roles::add);
                    if (!environment.isStandalone()) {
                        accessControl.roles().scopedRoles().stream().sorted(comparing(Role::getName))
                                .forEach(roles::add);
                    }
                    callback.onSuccess(roles);
                }).onPreview(item -> new RolePreview(accessControl, tokens, item, resources)).showCount()
                .filterDescription(resources.messages().roleColumnFilterDescription()).withFilter());

        this.metadataRegistry = metadataRegistry;
        this.statementContext = statementContext;
        this.dispatcher = dispatcher;
        this.eventBus = eventBus;
        this.progress = progress;
        this.accessControl = accessControl;
        this.resources = resources;
        this.standardRoleNames = accessControl.roles().standardRoles().stream().sorted(comparing(Role::getName))
                .map(Role::getName).collect(toList());

        if (!environment.isStandalone()) {
            List<ColumnAction<Role>> actions = new ArrayList<>();
            actions.add(new ColumnAction.Builder<Role>(Ids.ROLE_HOST_SCOPED_ADD)
                    .title(resources.constants().hostScopedRole())
                    .handler(column -> addScopedRole(Role.Type.HOST, resources.constants().hostScopedRole(),
                            HOST_SCOPED_ROLE_TEMPLATE, AddressTemplate.of("/host=*"), Ids.ROLE_HOST_SCOPED_FORM,
                            HOSTS))
                    .build());
            actions.add(new ColumnAction.Builder<Role>(Ids.ROLE_SERVER_GROUP_SCOPED_ADD)
                    .title(resources.constants().serverGroupScopedRole())
                    .handler(column -> addScopedRole(Role.Type.SERVER_GROUP,
                            resources.constants().serverGroupScopedRole(), SERVER_GROUP_SCOPED_ROLE_TEMPLATE,
                            AddressTemplate.of("/server-group=*"), Ids.ROLE_SERVER_GROUP_SCOPED_FORM,
                            SERVER_GROUPS))
                    .build());
            addColumnActions(Ids.ROLE_ADD, pfIcon("add-circle-o"), resources.constants().add(), actions);
        }
        addColumnAction(columnActionFactory.refresh(Ids.ROLE_REFRESH,
                column -> accessControl.reload(() -> refresh(RefreshMode.RESTORE_SELECTION))));

        setItemRenderer(item -> new ItemDisplay<Role>() {
            @Override
            public String getId() {
                return item.getId();
            }

            @Override
            public String getTitle() {
                return item.getName();
            }

            @Override
            public HTMLElement element() {
                if (item.isIncludeAll() && item.isScoped()) {
                    String scopedInfo = item.getBaseRole().getName() + " / " + String.join(", ", item.getScope());
                    return span().css(itemText).add(span().textContent(item.getName()))
                            .add(small().css(subtitle).title(scopedInfo).innerHtml(new SafeHtmlBuilder()
                                    .appendEscaped(resources.constants().includesAll()).appendHtmlConstant("<br/>") //NON-NLS
                                    .appendEscaped(scopedInfo).toSafeHtml()))
                            .get();

                } else if (item.isIncludeAll()) {
                    return ItemDisplay.withSubtitle(item.getName(), resources.constants().includesAll());

                } else if (item.isScoped()) {
                    return ItemDisplay.withSubtitle(item.getName(),
                            item.getBaseRole().getName() + " / " + String.join(", ", item.getScope()));
                }
                return null;
            }

            @Override
            public String getFilterData() {
                return String.join(" ", filterData(item));
            }

            @Override
            public List<ItemAction<Role>> actions() {
                List<ItemAction<Role>> actions = new ArrayList<>();
                actions.add(new ItemAction.Builder<Role>().title(resources.constants().edit()).handler(itm -> {
                    switch (itm.getType()) {
                    case STANDARD:
                        editStandardRole(itm);
                        break;
                    case HOST:
                        editScopedRole(itm, resources.constants().hostScopedRole(), HOST_SCOPED_ROLE_TEMPLATE,
                                AddressTemplate.of("/host=*"), Ids.ROLE_HOST_SCOPED_FORM, HOSTS);
                        break;
                    case SERVER_GROUP:
                        editScopedRole(itm, resources.constants().serverGroupScopedRole(),
                                SERVER_GROUP_SCOPED_ROLE_TEMPLATE, AddressTemplate.of("/server-group=*"),
                                Ids.ROLE_SERVER_GROUP_SCOPED_FORM, SERVER_GROUPS);
                        break;
                    default:
                        break;
                    }
                }).build());
                if (item.isScoped()) {
                    String type = item.getType() == Role.Type.HOST ? resources.constants().hostScopedRole()
                            : resources.constants().serverGroupScopedRole();
                    actions.add(
                            new ItemAction.Builder<Role>().title(resources.constants().remove()).handler(itm -> {
                                if (settings.get(RUN_AS).asSet().contains(itm.getName())) {
                                    MessageEvent.fire(eventBus, Message
                                            .error(resources.messages().removeRunAsRoleError(item.getName())));
                                } else {
                                    DialogFactory.showConfirmation(
                                            resources.messages().removeConfirmationTitle(type),
                                            resources.messages().removeRoleQuestion(itm.getName()),
                                            () -> removeScopedRole(itm, type));
                                }
                            }).build());
                }
                return actions;
            }

            @Override
            public String nextColumn() {
                return Ids.MEMBERSHIP;
            }
        });
    }

    // ------------------------------------------------------ add roles

    @SuppressWarnings("ConstantConditions")
    private void addScopedRole(Role.Type type, String typeName, AddressTemplate template,
            AddressTemplate typeaheadTemplate, String formId, String scopeAttribute) {
        Metadata metadata = metadataRegistry.lookup(template);
        Form<ModelNode> form = new ModelNodeForm.Builder<>(formId, metadata).addOnly()
                .unboundFormItem(new NameItem(), 0)
                .unboundFormItem(new SwitchItem(INCLUDE_ALL, new LabelBuilder().label(INCLUDE_ALL)), 3,
                        resources.messages().includeAllHelpText())
                .include(BASE_ROLE, scopeAttribute).customFormItem(BASE_ROLE, attributeDescription -> {
                    SingleSelectBoxItem item = new SingleSelectBoxItem(BASE_ROLE,
                            new LabelBuilder().label(BASE_ROLE), standardRoleNames, false);
                    item.setRequired(true);
                    return item;
                }).build();
        form.getFormItem(scopeAttribute).setRequired(true);
        form.getFormItem(scopeAttribute).registerSuggestHandler(
                new ReadChildrenAutoComplete(dispatcher, statementContext, typeaheadTemplate));
        form.attach();

        AddResourceDialog dialog = new AddResourceDialog(resources.messages().addResourceTitle(typeName), form,
                (name, model) -> {
                    List<Task<FlowContext>> tasks = new ArrayList<>();
                    tasks.add(new AddScopedRole(dispatcher, type, name, model));
                    Boolean includeAll = form.<Boolean>getFormItem(INCLUDE_ALL).getValue();
                    Role transientRole = new Role(name, null, type, null);
                    // We only need the role name in the functions,
                    // so it's ok to setup a transient role w/o the other attributes.
                    tasks.add(new CheckRoleMapping(dispatcher, transientRole));
                    tasks.add(new AddRoleMapping(dispatcher, transientRole, status -> status == 404));
                    if (includeAll != null && includeAll) {
                        tasks.add(new ModifyIncludeAll(dispatcher, transientRole, includeAll));
                    }
                    series(new FlowContext(progress.get()), tasks)
                            .subscribe(new SuccessfulOutcome<FlowContext>(eventBus, resources) {
                                @Override
                                public void onSuccess(FlowContext context) {
                                    MessageEvent.fire(eventBus, Message
                                            .success(resources.messages().addResourceSuccess(typeName, name)));
                                    accessControl.reload(() -> {
                                        refresh(Ids.role(name));
                                        eventBus.fireEvent(new RolesChangedEvent());
                                    });
                                }
                            });
                });
        dialog.getForm().<String>getFormItem(NAME).addValidationHandler(createUniqueValidation());
        dialog.show();
    }

    // ------------------------------------------------------ modify roles

    private void editStandardRole(Role role) {
        Metadata metadata = metadataRegistry.lookup(ROLE_MAPPING_TEMPLATE);
        Form<ModelNode> form = new ModelNodeForm.Builder<>(Ids.ROLE_MAPPING_FORM, metadata)
                .unboundFormItem(new NameItem(), 0).include(INCLUDE_ALL).build();
        form.getFormItem(NAME).setEnabled(false);
        form.getFormItem(NAME).setRequired(false);

        ModelNode modelNode = new ModelNode();
        modelNode.get(INCLUDE_ALL).set(role.isIncludeAll());
        new ModifyResourceDialog(resources.messages().modifyResourceTitle(resources.constants().role()), form, (frm,
                changedValues) -> series(new FlowContext(progress.get()), new CheckRoleMapping(dispatcher, role),
                        new AddRoleMapping(dispatcher, role, status -> status == 404),
                        new ModifyIncludeAll(dispatcher, role, frm.getModel().get(INCLUDE_ALL).asBoolean()))
                                .subscribe(new SuccessfulOutcome<FlowContext>(eventBus, resources) {
                                    @Override
                                    public void onSuccess(FlowContext context) {
                                        MessageEvent.fire(eventBus,
                                                Message.success(resources.messages().modifyResourceSuccess(
                                                        resources.constants().role(), role.getName())));
                                        accessControl.reload(() -> {
                                            refresh(role.getId());
                                            eventBus.fireEvent(new RolesChangedEvent());
                                        });
                                    }
                                })).show(modelNode);
        form.getFormItem(NAME).setValue(role.getName());
    }

    private void editScopedRole(Role role, String type, AddressTemplate template, AddressTemplate typeaheadTemplate,
            String formId, String scopeAttribute) {
        Metadata metadata = metadataRegistry.lookup(template);
        Form<ModelNode> form = new ModelNodeForm.Builder<>(formId, metadata).include(BASE_ROLE, scopeAttribute)
                .customFormItem(BASE_ROLE, attributeDescription -> {
                    SingleSelectBoxItem item = new SingleSelectBoxItem(BASE_ROLE,
                            new LabelBuilder().label(BASE_ROLE), standardRoleNames, false);
                    item.setRequired(true);
                    return item;
                }).unboundFormItem(new SwitchItem(INCLUDE_ALL, new LabelBuilder().label(INCLUDE_ALL)), 2,
                        resources.messages().includeAllHelpText())
                .build();
        form.getFormItem(scopeAttribute).setRequired(true);
        form.getFormItem(scopeAttribute).registerSuggestHandler(
                new ReadChildrenAutoComplete(dispatcher, statementContext, typeaheadTemplate));
        form.getFormItem(INCLUDE_ALL).setValue(role.isIncludeAll());
        form.attach();
        ModelNode modelNode = new ModelNode();
        modelNode.get(BASE_ROLE).set(role.getBaseRole().getName());
        role.getScope().forEach(scope -> modelNode.get(scopeAttribute).add(scope));
        new ModifyResourceDialog(resources.messages().modifyResourceTitle(type), form, (frm, changedValues) -> {
            boolean includeAll = frm.<Boolean>getFormItem(INCLUDE_ALL).getValue();
            boolean includeAllChanged = includeAll != role.isIncludeAll();

            List<Task<FlowContext>> tasks = new ArrayList<>();
            if (!changedValues.isEmpty()) {
                tasks.add(new ModifyScopedRole(dispatcher, role, changedValues, metadata));
            }
            if (includeAllChanged) {
                tasks.add(new ModifyIncludeAll(dispatcher, role, includeAll));
            }
            series(new FlowContext(progress.get()), tasks)
                    .subscribe(new SuccessfulOutcome<FlowContext>(eventBus, resources) {
                        @Override
                        public void onSuccess(FlowContext context) {
                            MessageEvent.fire(eventBus, Message
                                    .success(resources.messages().modifyResourceSuccess(type, role.getName())));
                            accessControl.reload(() -> {
                                refresh(role.getId());
                                eventBus.fireEvent(new RolesChangedEvent());
                            });
                        }
                    });
        }).show(modelNode);
    }

    // ------------------------------------------------------ remove roles

    private void removeScopedRole(Role role, String type) {
        List<Task<FlowContext>> tasks = new ArrayList<>();
        List<Assignment> assignments = accessControl.assignments().byRole(role).collect(toList());
        if (!assignments.isEmpty()) {
            tasks.add(new RemoveAssignments(dispatcher, assignments));
        }
        tasks.add(new CheckRoleMapping(dispatcher, role));
        tasks.add(new RemoveRoleMapping(dispatcher, role, status -> status == 200));
        tasks.add(new RemoveScopedRole(dispatcher, role));

        series(new FlowContext(progress.get()), tasks)
                .subscribe(new SuccessfulOutcome<FlowContext>(eventBus, resources) {
                    @Override
                    public void onSuccess(FlowContext context) {
                        MessageEvent.fire(eventBus,
                                Message.success(resources.messages().removeResourceSuccess(type, role.getName())));
                        accessControl.reload(() -> {
                            refresh(RefreshMode.CLEAR_SELECTION);
                            eventBus.fireEvent(new RolesChangedEvent());
                        });
                    }
                });
    }
}