/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* 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 org.uberfire.ext.security.management.client.widgets.management.editor.acl.node;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import org.uberfire.client.mvp.UberView;
import org.uberfire.ext.security.management.client.widgets.management.events.PermissionChangedEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.PermissionNodeAddedEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.PermissionNodeRemovedEvent;
import org.uberfire.ext.widgets.common.client.dropdown.LiveSearchDropDown;
import org.uberfire.ext.widgets.common.client.dropdown.LiveSearchService;
import org.uberfire.security.authz.AuthorizationResult;
import org.uberfire.security.authz.Permission;
import org.uberfire.security.client.authz.tree.HasResources;
import org.uberfire.security.client.authz.tree.PermissionNode;
import org.uberfire.security.client.authz.tree.PermissionTreeProvider;
import org.uberfire.security.client.authz.tree.impl.DefaultLoadOptions;
import org.uberfire.security.client.authz.tree.impl.PermissionResourceNode;
@Dependent
public class MultiplePermissionNodeEditor extends BasePermissionNodeEditor {
View view;
PermissionWidgetFactory widgetFactory;
LiveSearchDropDown liveSearchDropDown;
Event<PermissionChangedEvent> permissionChangedEvent;
Event<PermissionNodeAddedEvent> permissionNodeAddedEvent;
Event<PermissionNodeRemovedEvent> permissionNodeRemovedEvent;
Map<String, PermissionNode> childSelectorNodeMap = new TreeMap<>();
boolean expanded = false;
LiveSearchService childrenSearchService = (pattern, maxResults, callback) -> {
PermissionTreeProvider provider = permissionNode.getPermissionTreeProvider();
DefaultLoadOptions loadOptions = new DefaultLoadOptions();
loadOptions.setNodeNamePattern(pattern);
loadOptions.setMaxNodes(maxResults);
provider.loadChildren(permissionNode,
loadOptions,
children -> {
childSelectorNodeMap.clear();
for (PermissionNode childNode : children) {
String childName = childNode.getNodeName();
if (!childAlreadyAdded(childName)) {
childSelectorNodeMap.put(childName,
childNode);
}
}
List<String> result = new ArrayList<>(childSelectorNodeMap.keySet());
callback.afterSearch(result);
});
};
@Inject
public MultiplePermissionNodeEditor(View view,
LiveSearchDropDown liveSearchDropDown,
PermissionWidgetFactory widgetFactory,
Event<PermissionChangedEvent> permissionChangedEvent,
Event<PermissionNodeAddedEvent> permissionNodeAddedEvent,
Event<PermissionNodeRemovedEvent> permissionNodeRemovedEvent) {
this.view = view;
this.liveSearchDropDown = liveSearchDropDown;
this.widgetFactory = widgetFactory;
this.permissionChangedEvent = permissionChangedEvent;
this.permissionNodeAddedEvent = permissionNodeAddedEvent;
this.permissionNodeRemovedEvent = permissionNodeRemovedEvent;
}
@PostConstruct
public void init() {
view.init(this);
}
@Override
public Widget asWidget() {
return view.asWidget();
}
public boolean hasResources() {
return permissionNode instanceof HasResources;
}
@Override
public void edit(PermissionNode node) {
permissionNode = node;
permissionSwitchMap.clear();
String name = node.getNodeName();
String fullName = node.getNodeFullName();
view.setNodeName(name);
view.setNodePanelWidth(getNodePanelWidth());
view.setClearChildrenEnabled(false);
if (fullName != null && !fullName.equals(name)) {
view.setNodeFullName(fullName);
}
// Resources are only supported for dynamic nodes
view.setAddChildEnabled(false);
if (hasResources()) {
String resourceName = ((PermissionResourceNode) permissionNode).getResourceName();
liveSearchDropDown.setSelectorHint(view.getChildSelectorHint(resourceName));
liveSearchDropDown.setSearchHint(view.getChildSearchHint(resourceName));
liveSearchDropDown.setNotFoundMessage(view.getChildrenNotFoundMsg(resourceName));
liveSearchDropDown.setMaxItems(50);
liveSearchDropDown.setWidth(220);
liveSearchDropDown.setSearchService(childrenSearchService);
liveSearchDropDown.setOnChange(() -> onChildSelected(liveSearchDropDown.getSelectedItem()));
view.setAddChildEnabled(true);
view.setResourceName(resourceName);
view.setChildSelector(liveSearchDropDown);
}
// Init the switch control for every permission
for (Permission permission : permissionNode.getPermissionList()) {
String grantName = permissionNode.getPermissionGrantName(permission);
String denyName = permissionNode.getPermissionDenyName(permission);
boolean granted = AuthorizationResult.ACCESS_GRANTED.equals(permission.getResult());
PermissionSwitch permissionSwitch = widgetFactory.createSwitch();
permissionSwitch.init(grantName,
denyName,
granted,
0);
permissionSwitch.setOnChange(() -> {
permission.setResult(permissionSwitch.isOn() ? AuthorizationResult.ACCESS_GRANTED : AuthorizationResult.ACCESS_DENIED);
// Notify the change in the permission
super.onPermissionChanged(permission,
permissionSwitch.isOn());
permissionChangedEvent.fire(new PermissionChangedEvent(getACLEditor(),
permission,
permissionSwitch.isOn()));
});
super.registerPermissionSwitch(permission,
permissionSwitch);
}
// Update the switches status according to the inter-dependencies between their permissions
super.processAllPermissionDependencies();
// Add the switch controls to the view once initialized
for (PermissionSwitchToogle switchToogle : permissionSwitchMap.values()) {
view.addPermission(switchToogle);
}
// Load the children in order to initialize the exception counters properly
loadChildren();
}
@Override
protected void notifyPermissionChange(Permission permission,
boolean on) {
super.notifyPermissionChange(permission,
on);
// Update the exception count
PermissionSwitchToogle permissionSwitch = permissionSwitchMap.get(permission);
int n = getExceptionNumber(permission);
permissionSwitch.setNumberOfExceptions(n);
}
public void expand() {
expanded = true;
List<PermissionNodeEditor> childEditors = getChildEditors();
view.setExpanded(true);
view.clearChildren();
for (int i = 0; i < childEditors.size(); i++) {
PermissionNodeEditor nodeEditor = childEditors.get(i);
view.addChildEditor(nodeEditor,
hasResources());
if (i < childEditors.size() - 1) {
view.addChildSeparator();
}
}
if (!childEditors.isEmpty()) {
view.setClearChildrenEnabled(hasResources());
}
}
public void collapse() {
permissionNode.collapse();
expanded = false;
view.setExpanded(false);
view.clearChildren();
}
protected void loadChildren() {
permissionNode.expand(children -> {
for (PermissionNode child : children) {
registerChild(child);
}
updateExceptionCounters();
});
}
protected PermissionNodeEditor registerChild(PermissionNode child) {
PermissionNodeEditor nodeEditor = widgetFactory.createEditor(child);
nodeEditor.setACLEditor(this.getACLEditor());
nodeEditor.setTreeLevel(getTreeLevel() + 1);
nodeEditor.setParentEditor(this);
nodeEditor.edit(child);
super.addChildEditor(nodeEditor);
return nodeEditor;
}
@Override
public void onChildPermissionChanged(PermissionNodeEditor childEditor,
Permission permission,
boolean on) {
updateExceptionCounters();
}
@Override
protected void onNodePanelWidthChanged() {
int width = getNodePanelWidth();
view.setNodePanelWidth(width);
}
private void updateExceptionCounters() {
for (Permission p : permissionSwitchMap.keySet()) {
PermissionSwitchToogle pswitch = permissionSwitchMap.get(p);
int n = getExceptionNumber(p);
pswitch.setNumberOfExceptions(n);
}
}
// View events
public void onNodeClick() {
if (expanded) {
collapse();
} else {
expand();
}
}
public void onAddChildStart() {
view.showChildSelector();
}
public void onAddChildCancel() {
view.hideChildSelector();
}
public void onClearChildren() {
for (PermissionNodeEditor child : new ArrayList<>(getChildEditors())) {
removeChild(child);
}
view.setClearChildrenEnabled(false);
updateExceptionCounters();
}
public void onRemoveChild(PermissionNodeEditor child) {
removeChild(child);
updateExceptionCounters();
view.setClearChildrenEnabled(hasResources() && hasChildEditors());
}
protected void removeChild(PermissionNodeEditor child) {
super.removeChildEditor(child);
liveSearchDropDown.clear();
view.hideChildSelector();
view.clearChildren();
List<PermissionNodeEditor> childEditors = getChildEditors();
for (int i = 0; i < childEditors.size(); i++) {
PermissionNodeEditor nodeEditor = childEditors.get(i);
view.addChildEditor(nodeEditor,
hasResources());
if (i < childEditors.size() - 1) {
view.addChildSeparator();
}
}
permissionNodeRemovedEvent.fire(new PermissionNodeRemovedEvent(getACLEditor(),
permissionNode,
child.getPermissionNode()));
}
public void onChildSelected(String childName) {
PermissionNode childNode = childSelectorNodeMap.remove(childName);
overwritePermissions(childNode);
PermissionNodeEditor childEditor = registerChild(childNode);
if (view.hasChildren()) {
view.addChildSeparator();
}
view.addChildEditor(childEditor,
hasResources());
view.setClearChildrenEnabled(true);
view.hideChildSelector();
liveSearchDropDown.clear();
updateExceptionCounters();
permissionNodeAddedEvent.fire(new PermissionNodeAddedEvent(getACLEditor(),
permissionNode,
childNode));
}
protected void overwritePermissions(PermissionNode child) {
for (Permission p1 : permissionNode.getPermissionList()) {
for (Permission p2 : child.getPermissionList()) {
if (p1.impliesName(p2)) {
p2.setResult(p1.getResult().invert());
}
}
}
}
protected boolean childAlreadyAdded(String nodeName) {
for (PermissionNodeEditor childEditor : getChildEditors()) {
String existingName = childEditor.getPermissionNode().getNodeName();
if (existingName.equals(nodeName)) {
return true;
}
}
return false;
}
public interface View extends UberView<MultiplePermissionNodeEditor> {
void setNodeName(String name);
void setNodePanelWidth(int width);
void setNodeFullName(String name);
void setResourceName(String name);
void addPermission(PermissionSwitchToogle permissionSwitch);
void addChildEditor(PermissionNodeEditor editor,
boolean dynamic);
void addChildSeparator();
boolean hasChildren();
void clearChildren();
String getChildSelectorHint(String resourceName);
String getChildSearchHint(String resourceName);
String getChildrenNotFoundMsg(String resourceName);
void setChildSelector(IsWidget childSelector);
void showChildSelector();
void hideChildSelector();
void setAddChildEnabled(boolean enabled);
void setClearChildrenEnabled(boolean enabled);
void setExpanded(boolean expanded);
}
}