blob: ec8ba1247c42f1d9bf4031e8ad09966ad83b1206 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.syncope.client.console.wizards.resources;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.console.panels.AbstractModalPanel;
import org.apache.syncope.client.console.panels.ListViewPanel;
import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
import org.apache.syncope.client.console.rest.AnyTypeRestClient;
import org.apache.syncope.client.console.rest.ResourceRestClient;
import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
import org.apache.syncope.client.console.wizards.WizardMgtPanel;
import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.ItemTO;
import org.apache.syncope.common.lib.to.ResourceTO;
import org.apache.syncope.common.lib.types.IdMEntitlement;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
private static final long serialVersionUID = -7982691107029848579L;
private final ResourceTO resourceTO;
private final List<ResourceProvision> provisions;
private final ObjectTypeTogglePanel objectTypeTogglePanel;
private final WizardMgtPanel<ResourceProvision> list;
private final ProvisionWizardBuilder wizard;
private final AjaxLink<ResourceProvision> addAjaxLink;
protected ActionLinksTogglePanel<ResourceProvision> actionTogglePanel;
public ResourceProvisionPanel(
final BaseModal<Serializable> modal,
final ResourceTO resourceTO,
final String adminRealm,
final PageReference pageRef) {
super(modal, pageRef);
this.resourceTO = resourceTO;
setOutputMarkupId(true);
actionTogglePanel = new ActionLinksTogglePanel<>("toggle", pageRef);
add(actionTogglePanel);
wizard = new ProvisionWizardBuilder(resourceTO, adminRealm, pageRef);
ListViewPanel.Builder<ResourceProvision> builder = new ListViewPanel.Builder<>(
ResourceProvision.class, pageRef) {
private static final long serialVersionUID = 4907732721283972943L;
@Override
protected ResourceProvision getActualItem(
final ResourceProvision item, final List<ResourceProvision> list) {
return Optional.ofNullable(item)
.map(resourceProvision -> list.stream()
.filter(in -> ((resourceProvision.getKey() == null && in.getKey() == null)
|| (in.getKey() != null && in.getKey().equals(resourceProvision.getKey())))
&& ((resourceProvision.getAnyType() == null && in.getAnyType() == null)
|| (in.getAnyType() != null && in.getAnyType().equals(resourceProvision.getAnyType())))).
findAny().orElse(null)).orElse(null);
}
@Override
protected void customActionCallback(final AjaxRequestTarget target) {
// change modal footer visibility
send(ResourceProvisionPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
}
@Override
protected void customActionOnCancelCallback(final AjaxRequestTarget target) {
// change modal footer visibility
send(ResourceProvisionPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
}
@Override
protected void customActionOnFinishCallback(final AjaxRequestTarget target) {
checkAddButton(adminRealm);
// keep list ordered - SYNCOPE-1154
sortProvisions();
// change modal footer visibility
send(ResourceProvisionPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
}
@Override
protected ActionLinksTogglePanel<ResourceProvision> getTogglePanel() {
return actionTogglePanel;
}
};
provisions = new ArrayList<>();
if (resourceTO.getOrgUnit() != null) {
provisions.add(new ResourceProvision(resourceTO.getOrgUnit()));
}
resourceTO.getProvisions().forEach(provision -> provisions.add(new ResourceProvision(provision)));
// keep list ordered - SYNCOPE-1154
sortProvisions();
builder.setItems(provisions);
builder.includes("anyType", "objectClass", "auxClasses");
builder.setReuseItem(false);
builder.addAction(new ActionLink<>() {
private static final long serialVersionUID = -3722207913631435504L;
@Override
public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
try {
send(ResourceProvisionPanel.this, Broadcast.DEPTH,
new AjaxWizard.NewItemActionEvent<>(provision, 1, target).setResourceModel(
new StringResourceModel("inner.provision.mapping",
ResourceProvisionPanel.this,
Model.of(provision))));
} catch (SyncopeClientException e) {
LOG.error("While contacting resource", e);
SyncopeConsoleSession.get().onException(e);
((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
}
}
}, ActionLink.ActionType.MAPPING, IdMEntitlement.RESOURCE_READ).
addAction(new ActionLink<>() {
private static final long serialVersionUID = -7780999687733432439L;
@Override
public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
try {
ResourceRestClient.setLatestSyncToken(resourceTO.getKey(), provision.getAnyType());
SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
} catch (Exception e) {
LOG.error("While setting latest sync token for {}/{}",
resourceTO.getKey(), provision.getAnyType(), e);
SyncopeConsoleSession.get().onException(e);
}
((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
}
}, ActionLink.ActionType.SET_LATEST_SYNC_TOKEN, IdMEntitlement.RESOURCE_UPDATE).
addAction(new ActionLink<>() {
private static final long serialVersionUID = -7780999687733432439L;
@Override
public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
try {
ResourceRestClient.removeSyncToken(resourceTO.getKey(), provision.getAnyType());
SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
} catch (Exception e) {
LOG.error("While removing sync token for {}/{}",
resourceTO.getKey(), provision.getAnyType(), e);
SyncopeConsoleSession.get().onException(e);
}
((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
}
}, ActionLink.ActionType.REMOVE_SYNC_TOKEN, IdMEntitlement.RESOURCE_UPDATE).
addAction(new ActionLink<>() {
private static final long serialVersionUID = -3722207913631435544L;
@Override
public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
if (provision.getOrgUnitTO() != null) {
resourceTO.setOrgUnit(null);
} else if (provision.getProvisionTO() != null) {
resourceTO.getProvisions().remove(provision.getProvisionTO());
}
provisions.remove(provision);
checkAddButton(adminRealm);
send(ResourceProvisionPanel.this, Broadcast.DEPTH, new ListViewReload<>(target));
}
}, ActionLink.ActionType.DELETE, IdMEntitlement.RESOURCE_UPDATE, true);
builder.addNewItemPanelBuilder(wizard);
list = builder.build("provision");
list.setReadOnly(!SyncopeConsoleSession.get().owns(IdMEntitlement.RESOURCE_UPDATE, adminRealm));
addAjaxLink = new AjaxLink<>("add") {
private static final long serialVersionUID = -7978723352517770644L;
@Override
public void onClick(final AjaxRequestTarget target) {
send(ResourceProvisionPanel.this, Broadcast.BREADTH,
new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
objectTypeTogglePanel.setHeaderLabel(target);
objectTypeTogglePanel.toggle(target, true);
}
};
list.addOrReplaceInnerObject(addAjaxLink);
add(list);
// ----------------------------------------------------------------------
// toggle panel, used to choose 'type' before starting wizard - SYNCOPE-1167
final ResourceProvision provision = new ResourceProvision();
provision.setAnyType("");
objectTypeTogglePanel = new ObjectTypeTogglePanel("objectTypeToggle", provision, getAnyTypes(), pageRef) {
private static final long serialVersionUID = 7878063325027015067L;
@Override
protected void onSubmit(final String type, final AjaxRequestTarget target) {
provision.setAnyType(type);
send(list, Broadcast.BREADTH,
new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
send(list, Broadcast.DEPTH,
new AjaxWizard.NewItemActionEvent<>(provision, target));
wizard.setObjectClassModelObject(type);
}
};
checkAddButton(adminRealm);
add(objectTypeTogglePanel);
}
private void checkConnObjectKeyCount(final String anyType, final List<ItemTO> items) {
long connObjectKeyCount = items.stream().filter(ItemTO::isConnObjectKey).count();
if (connObjectKeyCount != 1) {
throw new IllegalArgumentException(anyType + ": "
+ new StringResourceModel("connObjectKeyValidation", ResourceProvisionPanel.this).getString());
}
}
@Override
public void onSubmit(final AjaxRequestTarget target) {
try {
if (resourceTO.getOrgUnit() != null) {
checkConnObjectKeyCount(SyncopeConstants.REALM_ANYTYPE, resourceTO.getOrgUnit().getItems());
}
new ArrayList<>(resourceTO.getProvisions()).stream().
filter(Objects::nonNull).
forEachOrdered(provision -> {
if (provision.getMapping() == null || provision.getMapping().getItems().isEmpty()) {
resourceTO.getProvisions().remove(provision);
} else {
checkConnObjectKeyCount(provision.getAnyType(), provision.getMapping().getItems());
}
});
ResourceRestClient.update(resourceTO);
SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
modal.close(target);
} catch (Exception e) {
LOG.error("While creating or updating {}", resourceTO, e);
SyncopeConsoleSession.get().onException(e);
}
((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
}
private void sortProvisions() {
provisions.sort((o1, o2) -> AnyTypeRestClient.KEY_COMPARATOR.compare(o1.getAnyType(), o2.getAnyType()));
}
private LoadableDetachableModel<List<String>> getAnyTypes() {
return new LoadableDetachableModel<>() {
private static final long serialVersionUID = 5275935387613157437L;
@Override
protected List<String> load() {
List<String> anyTypes = AnyTypeRestClient.list().stream().
filter(anyType -> resourceTO.getProvision(anyType).isEmpty()).
collect(Collectors.toList());
if (resourceTO.getOrgUnit() == null) {
anyTypes.add(SyncopeConstants.REALM_ANYTYPE);
}
anyTypes.sort(AnyTypeRestClient.KEY_COMPARATOR);
return anyTypes;
}
};
}
private void checkAddButton(final String adminRealm) {
boolean enabled = SyncopeConsoleSession.get().owns(IdMEntitlement.RESOURCE_UPDATE, adminRealm)
&& !getAnyTypes().getObject().isEmpty();
addAjaxLink.setVisible(enabled);
objectTypeTogglePanel.setEnabled(enabled);
}
}