org.artifactory.webapp.wicket.page.security.acl.PermissionTargetCreateUpdatePanel.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.webapp.wicket.page.security.acl.PermissionTargetCreateUpdatePanel.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2012 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.webapp.wicket.page.security.acl;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxCallDecorator;
import org.apache.wicket.extensions.markup.html.basic.SmartLinkLabel;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.OddEvenItem;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.artifactory.api.repo.RepositoryService;
import org.artifactory.api.security.AclService;
import org.artifactory.api.security.AuthorizationService;
import org.artifactory.api.security.UserGroupService;
import org.artifactory.common.wicket.ajax.NoAjaxIndicatorDecorator;
import org.artifactory.common.wicket.behavior.CssClass;
import org.artifactory.common.wicket.behavior.defaultbutton.DefaultButtonBehavior;
import org.artifactory.common.wicket.component.CreateUpdateAction;
import org.artifactory.common.wicket.component.CreateUpdatePanel;
import org.artifactory.common.wicket.component.border.titled.TitledBorder;
import org.artifactory.common.wicket.component.checkbox.styled.StyledCheckbox;
import org.artifactory.common.wicket.component.links.TitledAjaxLink;
import org.artifactory.common.wicket.component.links.TitledAjaxSubmitLink;
import org.artifactory.common.wicket.component.modal.ModalHandler;
import org.artifactory.common.wicket.component.table.SortableTable;
import org.artifactory.common.wicket.component.table.columns.checkbox.AjaxCheckboxColumn;
import org.artifactory.common.wicket.util.AjaxUtils;
import org.artifactory.descriptor.repo.LocalRepoDescriptor;
import org.artifactory.descriptor.repo.RealRepoDescriptor;
import org.artifactory.descriptor.repo.RemoteRepoDescriptor;
import org.artifactory.factory.InfoFactoryHolder;
import org.artifactory.security.AccessLogger;
import org.artifactory.security.GroupInfo;
import org.artifactory.security.MutableAclInfo;
import org.artifactory.security.MutablePermissionTargetInfo;
import org.artifactory.security.PermissionTargetInfo;
import org.artifactory.security.UserInfo;
import org.artifactory.util.AlreadyExistsException;
import org.artifactory.webapp.wicket.page.config.security.general.SecurityGeneralConfigPage;
import org.artifactory.webapp.wicket.page.security.acl.tabs.PermissionPanel;
import org.artifactory.webapp.wicket.page.security.acl.tabs.RepositoriesTabPanel;
import org.artifactory.webapp.wicket.panel.tabbed.StyledTabbedPanel;
import org.artifactory.webapp.wicket.panel.tabbed.SubmittingTabbedPanel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author Yoav Landman
 * @author Yoav Aharoni
 */
public class PermissionTargetCreateUpdatePanel extends CreateUpdatePanel<MutablePermissionTargetInfo> {
    private static final Logger log = LoggerFactory.getLogger(PermissionTargetCreateUpdatePanel.class);

    @SpringBean
    private UserGroupService userGroupService;

    @SpringBean
    private RepositoryService repositoryService;

    @SpringBean
    private AclService aclService;

    @SpringBean
    private AuthorizationService authService;

    private PermissionTargetInfo permissionTarget;
    private RepoKeysData repoKeysData;
    private MutableAclInfo aclInfo;
    private PermissionTargetAceInfoRowDataProvider groupsDataProvider;
    private PermissionTargetAceInfoRowDataProvider usersDataProvider;

    public PermissionTargetCreateUpdatePanel(CreateUpdateAction action, MutablePermissionTargetInfo target,
            final Component targetsTable) {
        super(action, target);
        add(new CssClass("permissions-panel"));
        setWidth(600);
        setInitialHeight(740);
        setMinimalHeight(400);
        repoKeysData = new RepoKeysData(target);

        form.setOutputMarkupId(true);
        add(form);

        TitledBorder border = new TitledBorder("border");
        form.add(border);

        permissionTarget = target;

        if (isCreate()) {
            aclInfo = InfoFactoryHolder.get().createAcl(permissionTarget);
        } else {
            aclInfo = InfoFactoryHolder.get().copyAcl(aclService.getAcl(permissionTarget));
        }

        groupsDataProvider = new PermissionTargetAceInfoRowDataProvider(userGroupService, aclInfo) {
            @Override
            protected List<UserInfo> getUsers() {
                return Collections.emptyList();
            }
        };
        usersDataProvider = new PermissionTargetAceInfoRowDataProvider(userGroupService, aclInfo) {
            @Override
            protected List<GroupInfo> getGroups() {
                return Collections.emptyList();
            }
        };

        addTabs(border);
        addPermissionTargetNameField(border);

        addCancelButton();
        addSubmitButton(targetsTable);
    }

    @Override
    public void onShow(AjaxRequestTarget target) {
        super.onShow(target);
        target.appendJavaScript("PermissionTabPanel.onShow()");
    }

    private void addPermissionTargetNameField(TitledBorder border) {
        TextField nameTf = new TextField("name");
        setDefaultFocusField(nameTf);
        border.add(nameTf);
        if (!isSystemAdmin() || !isCreate()) {
            nameTf.setEnabled(false);
        }
    }

    /**
     * @return True if the current user is a system admin (not just the current permission target admin). Non system
     *         admins can only change the recipients table.
     */
    public boolean isSystemAdmin() {
        return authService.isAdmin();
    }

    public RepoKeysData getRepoKeysData() {
        return repoKeysData;
    }

    private void addCancelButton() {
        TitledAjaxLink cancel = new TitledAjaxLink("cancel", "Cancel") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                cancel();
                ModalHandler.closeCurrent(target);
            }
        };
        form.add(cancel);
    }

    private void addSubmitButton(final Component targetsTable) {
        String submitCaption = isCreate() ? "Create" : "Save";
        TitledAjaxSubmitLink submit = new TitledAjaxSubmitLink("submit", submitCaption, form) {
            @Override
            protected void onSubmit(AjaxRequestTarget target, Form form) {
                final String name = entity.getName();
                if (StringUtils.isBlank(name)) {
                    error("Field 'Name' is required.");
                    AjaxUtils.refreshFeedback(target);
                    return;
                }

                boolean anySelected = repoKeysData.isAnyRemoteRepository() && repoKeysData.isAnyLocalRepository();
                repoKeysData.setAnyRepository(anySelected);

                entity.setRepoKeys(repoKeysData.getSelectedKeysList());
                aclInfo.setPermissionTarget(entity);
                if (isCreate()) {
                    try {
                        aclService.createAcl(aclInfo);
                        AccessLogger.created("Successfully created permission target '" + name + "'");
                    } catch (Exception e) {
                        String msg;
                        if (e instanceof AlreadyExistsException) {
                            msg = "Permission target '" + name + "' already exists";
                        } else {
                            msg = "Failed to create permissions target: " + e.getMessage();
                            log.error(msg, e);
                        }
                        getPage().error(msg);
                        AjaxUtils.refreshFeedback(target);
                        return;
                    }
                    getPage().info("Successfully created permission target '" + name + "'");
                } else {
                    try {
                        aclService.updateAcl(aclInfo);
                        reloadData();
                        String message = "Successfully updated permission target '" + name + "'";
                        AccessLogger.updated(message);
                        getPage().info(message);
                        target.add(PermissionTargetCreateUpdatePanel.this);
                    } catch (Exception e) {
                        String msg = "Failed to update permissions target: " + e.getMessage();
                        log.error(msg, e);
                        getPage().error(msg);
                        AjaxUtils.refreshFeedback(target);
                        return;
                    }
                }
                //Close the modal window and re-render the table
                targetsTable.modelChanged();
                target.add(targetsTable);
                AjaxUtils.refreshFeedback(target);
                ModalHandler.closeCurrent(target);
            }
        };
        form.add(submit);
        form.add(new DefaultButtonBehavior(submit));
    }

    public List<LocalRepoDescriptor> getLocalRepositoryDescriptors() {
        return repositoryService.getLocalRepoDescriptors();
    }

    public List<? extends RemoteRepoDescriptor> getRemoteRepositoryDescriptors() {
        return repositoryService.getRemoteRepoDescriptors();
    }

    @Override
    public String getCookieName() {
        return null;
    }

    private void addTabs(TitledBorder border) {
        List<ITab> tabs = new ArrayList<>(2);

        tabs.add(new AbstractTab(Model.of("Repositories")) {
            @Override
            public Panel getPanel(String panelId) {
                return new RepositoriesTabPanel(panelId, PermissionTargetCreateUpdatePanel.this);
            }
        });

        tabs.add(new AbstractTab(Model.of("Users")) {
            @Override
            public Panel getPanel(String panelId) {
                return new PermissionPanel(PermissionTargetCreateUpdatePanel.this, panelId, false);
            }
        });

        if (!groupsDataProvider.getGroups().isEmpty()) {
            tabs.add(new AbstractTab(Model.of("Groups")) {
                @Override
                public Panel getPanel(String panelId) {
                    return new PermissionPanel(PermissionTargetCreateUpdatePanel.this, panelId, true);
                }
            });
        }

        StyledTabbedPanel permissionsTabs = new SubmittingTabbedPanel("permissionsTabs", tabs);

        border.add(permissionsTabs);
    }

    public SortableTable getPermissionsTable(final boolean isGroup) {
        List<IColumn<AceInfoRow>> columns = Lists.newArrayList();
        columns.add(new PropertyColumn<AceInfoRow>(Model.of("Principal"), "principal", "principal") {
            @Override
            public void populateItem(Item<ICellPopulator<AceInfoRow>> item, String componentId,
                    IModel<AceInfoRow> model) {

                //If the item is an anonymous user and the access is disabled, warn
                String username = model.getObject().getPrincipal();
                if (UserInfo.ANONYMOUS.equals(username) && !authService.isAnonAccessEnabled()) {
                    CharSequence pageUrl = urlFor(SecurityGeneralConfigPage.class, new PageParameters());

                    StringBuilder usernameLabelBuilder = new StringBuilder(username).append(" (");
                    if (authService.isAdmin()) {
                        usernameLabelBuilder.append("<a href=\"").append(pageUrl).append("\">");
                    }
                    usernameLabelBuilder.append("disabled");
                    if (authService.isAdmin()) {
                        usernameLabelBuilder.append("</a>");
                    }
                    usernameLabelBuilder.append(")");
                    item.add(new SmartLinkLabel(componentId, usernameLabelBuilder.toString())
                            .setEscapeModelStrings(false));
                } else {
                    super.populateItem(item, componentId, model);
                }

                if (isGroup) {
                    item.add(new CssClass("group"));
                } else {
                    item.add(new CssClass("user"));
                }
            }
        });
        columns.add(new RoleCheckboxColumn("Manage", "manage") {
            @Override
            protected void onUpdate(FormComponent checkbox, AceInfoRow row, boolean value,
                    AjaxRequestTarget target) {
                super.onUpdate(checkbox, row, value, target);
                if (sanityCheckAdmin() && isEnabled(row)) {
                    row.setManage(value);
                    onCheckboxUpdate(checkbox, target);
                }
            }

            @Override
            protected boolean isEnabled(AceInfoRow row) {
                if (!super.isEnabled(row)) {
                    return false;
                }

                String currentUsername = authService.currentUsername();
                String username = row.getPrincipal();
                //Do not allow admin user to change (revoke) his admin bit
                return !username.equals(currentUsername);

            }
        });
        columns.add(new RoleCheckboxColumn("Delete", "delete") {
            @Override
            protected void onUpdate(FormComponent checkbox, AceInfoRow row, boolean value,
                    AjaxRequestTarget target) {
                super.onUpdate(checkbox, row, value, target);
                if (sanityCheckAdmin()) {
                    row.setDelete(value);
                    AccessLogger.deleted("Permission target" + row.getPrincipal() + " was successfully deleted");
                    onCheckboxUpdate(checkbox, target);
                }
            }
        });
        columns.add(new RoleCheckboxColumn("Deploy", "deploy") {
            @Override
            protected void onUpdate(FormComponent checkbox, AceInfoRow row, boolean value,
                    AjaxRequestTarget target) {
                super.onUpdate(checkbox, row, value, target);
                if (sanityCheckAdmin()) {
                    row.setDeploy(value);
                    onCheckboxUpdate(checkbox, target);
                }
            }
        });
        columns.add(new RoleCheckboxColumn("Annotate", "annotate") {
            @Override
            protected void onUpdate(FormComponent checkbox, AceInfoRow row, boolean value,
                    AjaxRequestTarget target) {
                super.onUpdate(checkbox, row, value, target);
                if (sanityCheckAdmin()) {
                    row.setAnnotate(value);
                    onCheckboxUpdate(checkbox, target);
                }
            }
        });
        columns.add(new RoleCheckboxColumn("Read", "read") {
            @Override
            protected void onUpdate(FormComponent checkbox, AceInfoRow row, boolean value,
                    AjaxRequestTarget target) {
                super.onUpdate(checkbox, row, value, target);
                if (sanityCheckAdmin()) {
                    row.setRead(value);
                    onCheckboxUpdate(checkbox, target);
                }
            }
        });

        PermissionTargetAceInfoRowDataProvider dataProvider = isGroup ? groupsDataProvider : usersDataProvider;

        SortableTable table = new SortableTable<>("recipients", columns, dataProvider, 15);
        //Recipients header
        Label recipientsHeader = new Label("recipientsHeader");
        recipientsHeader.setDefaultModel(Model.of("Permissions for \"" + permissionTarget.getName() + "\""));

        return table;
    }

    private void onCheckboxUpdate(FormComponent checkbox, AjaxRequestTarget target) {
        final MarkupContainer row = checkbox.findParent(OddEvenItem.class);
        target.addChildren(row, StyledCheckbox.class);
    }

    private void reloadData() {
        //Reload from backend
        aclInfo = InfoFactoryHolder.get().copyAcl(aclService.getAcl(permissionTarget));
        groupsDataProvider.setAclInfo(aclInfo);
        groupsDataProvider.loadData();

        usersDataProvider.setAclInfo(aclInfo);
        usersDataProvider.loadData();
    }

    public void cancel() {
        if (!isCreate()) {
            reloadData();
        }
    }

    public MutableAclInfo getAclInfo() {
        return aclInfo;
    }

    private boolean sanityCheckAdmin() {
        if (!isCreate() && !aclService.canManage(permissionTarget)) {
            String username = authService.currentUsername();
            log.error(username + " operation ignored: not enough permissions to administer '" + permissionTarget
                    + "'.");
            return false;
        }
        return true;
    }

    private class RoleCheckboxColumn extends AjaxCheckboxColumn<AceInfoRow> {
        private RoleCheckboxColumn(String title, String expression) {
            super(title, expression, expression);
        }

        @Override
        protected IAjaxCallDecorator getAjaxCallDecorator() {
            return new NoAjaxIndicatorDecorator();
        }

        @Override
        protected FormComponent<Boolean> newCheckBox(String id, IModel<Boolean> model, AceInfoRow rowObject) {
            FormComponent<Boolean> component = super.newCheckBox(id, model, rowObject);
            component.setOutputMarkupId(true);
            return component;
        }

        @Override
        protected boolean isEnabled(AceInfoRow row) {
            return true;
        }
    }

    public class RepoKeysData implements Serializable {
        private boolean anyRepository;
        private boolean anyLocalRepository;
        private boolean anyRemoteRepository;
        private final Boolean REMOTE_REPO = Boolean.TRUE;
        private Multimap<Boolean, RealRepoDescriptor> repoKeyMap = HashMultimap.create();

        private RepoKeysData(PermissionTargetInfo info) {
            List<String> repoKeys = aclService.convertCachedRepoKeysToRemote(info.getRepoKeys());
            List<RealRepoDescriptor> repoDescriptorList = getRepoDescriptors(repoKeys);
            anyRepository = repoKeys.contains(PermissionTargetInfo.ANY_REPO);
            if (anyRepository) {
                anyLocalRepository = true;
                anyRemoteRepository = true;
            } else {
                anyLocalRepository = repoKeys.contains(PermissionTargetInfo.ANY_LOCAL_REPO);
                anyRemoteRepository = repoKeys.contains(PermissionTargetInfo.ANY_REMOTE_REPO);
            }

            if (anyLocalRepository) {
                repoKeyMap.putAll(!REMOTE_REPO, getLocalRepositoryDescriptors());
            }

            if (anyRemoteRepository) {
                repoKeyMap.putAll(REMOTE_REPO, getRemoteRepositoryDescriptors());
            }

            if (!anyRepository) {
                for (RealRepoDescriptor repoDescriptor : repoDescriptorList) {
                    if (repoDescriptor.isLocal()) {
                        repoKeyMap.put(!REMOTE_REPO, repoDescriptor);
                    } else {
                        repoKeyMap.put(REMOTE_REPO, repoDescriptor);
                    }
                }
            }
        }

        public List<String> getSelectedKeysList() {
            List<String> repoKeys = new ArrayList<>();
            if (anyRepository) {
                repoKeys.add(PermissionTargetInfo.ANY_REPO);
            } else {
                if (anyLocalRepository) {
                    repoKeys.add(PermissionTargetInfo.ANY_LOCAL_REPO);
                } else {
                    Collection<RealRepoDescriptor> collection = repoKeyMap.get(!REMOTE_REPO);
                    if (collection != null) {
                        for (RealRepoDescriptor repoDescriptor : collection) {
                            repoKeys.add(repoDescriptor.getKey());
                        }
                    }
                }
                if (anyRemoteRepository) {
                    repoKeys.add(PermissionTargetInfo.ANY_REMOTE_REPO);
                } else {
                    Collection<RealRepoDescriptor> collection = repoKeyMap.get(REMOTE_REPO);
                    if (collection != null) {
                        for (RealRepoDescriptor localRepoDescriptor : collection) {
                            repoKeys.add(localRepoDescriptor.getKey());
                        }
                    }
                }
            }
            return repoKeys;
        }

        public List<RealRepoDescriptor> getRepoDescriptors() {
            return new ArrayList<>(repoKeyMap.values());
        }

        public void setRepoDescriptors(List<? extends RealRepoDescriptor> repoDescriptors) {
            repoKeyMap.clear();
            addRepoDescriptors(repoDescriptors);
        }

        public void addRepoDescriptors(List<? extends RealRepoDescriptor> repoDescriptors) {
            for (RealRepoDescriptor repoDescriptor : repoDescriptors) {
                if (!repoKeyMap.containsValue(repoDescriptor)) {
                    repoKeyMap.put(!repoDescriptor.isLocal(), repoDescriptor);
                }
            }
        }

        public void removeRepoDescriptors(List<? extends RealRepoDescriptor> repoDescriptors) {
            for (RealRepoDescriptor repoDescriptor : repoDescriptors) {
                if (repoKeyMap.containsValue(repoDescriptor)) {
                    repoKeyMap.remove(!repoDescriptor.isLocal(), repoDescriptor);
                }
            }
        }

        public boolean isAnyRepository() {
            return anyRepository;
        }

        public void setAnyRepository(boolean anyRepository) {
            this.anyRepository = anyRepository;
        }

        public boolean isAnyLocalRepository() {
            return anyLocalRepository;
        }

        public void setAnyLocalRepository(boolean anyLocalRepository) {
            this.anyLocalRepository = anyLocalRepository;
        }

        public boolean isAnyRemoteRepository() {
            return anyRemoteRepository;
        }

        public void setAnyRemoteRepository(boolean anyRemoteRepository) {
            this.anyRemoteRepository = anyRemoteRepository;
        }

        private List<RealRepoDescriptor> getRepoDescriptors(List<String> repoKeys) {
            List<RealRepoDescriptor> descriptorList = new ArrayList<>();
            for (String repoKey : repoKeys) {
                RealRepoDescriptor localRepoDescriptor = repositoryService.localRepoDescriptorByKey(repoKey);
                RealRepoDescriptor remoteRepoDescriptor = repositoryService.remoteRepoDescriptorByKey(repoKey);
                if (localRepoDescriptor != null) {
                    descriptorList.add(localRepoDescriptor);
                } else if (remoteRepoDescriptor != null) {
                    descriptorList.add(remoteRepoDescriptor);
                }
            }
            return descriptorList;
        }
    }
}