/* * 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.group.workflow; import java.util.Collection; import javax.annotation.PostConstruct; import javax.enterprise.context.Dependent; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import org.jboss.errai.bus.client.api.messaging.Message; import org.jboss.errai.common.client.api.Caller; import org.jboss.errai.common.client.api.ErrorCallback; import org.jboss.errai.security.shared.api.Group; import org.uberfire.backend.authz.AuthorizationService; import org.uberfire.client.authz.PerspectiveAction; import org.uberfire.client.mvp.PerspectiveActivity; import org.uberfire.ext.security.management.client.ClientUserSystemManager; import org.uberfire.ext.security.management.client.editor.group.GroupEditorDriver; import org.uberfire.ext.security.management.client.resources.i18n.UsersManagementWidgetsConstants; import org.uberfire.ext.security.management.client.widgets.management.editor.group.GroupEditor; import org.uberfire.ext.security.management.client.widgets.management.editor.workflow.EntityWorkflowView; import org.uberfire.ext.security.management.client.widgets.management.events.ContextualEvent; import org.uberfire.ext.security.management.client.widgets.management.events.DeleteGroupEvent; import org.uberfire.ext.security.management.client.widgets.management.events.HomePerspectiveChangedEvent; import org.uberfire.ext.security.management.client.widgets.management.events.OnDeleteEvent; import org.uberfire.ext.security.management.client.widgets.management.events.OnEditEvent; import org.uberfire.ext.security.management.client.widgets.management.events.OnErrorEvent; 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.security.management.client.widgets.management.events.PriorityChangedEvent; import org.uberfire.ext.security.management.client.widgets.management.events.SaveGroupEvent; import org.uberfire.ext.security.management.client.widgets.popup.ConfirmBox; import org.uberfire.ext.security.management.client.widgets.popup.LoadingBox; import org.uberfire.mvp.Command; import org.uberfire.security.authz.AuthorizationPolicy; import org.uberfire.security.authz.AuthorizationResult; import org.uberfire.security.authz.Permission; import org.uberfire.security.authz.PermissionCollection; import org.uberfire.security.authz.PermissionManager; import org.uberfire.workbench.events.NotificationEvent; import static org.uberfire.workbench.events.NotificationEvent.NotificationType.INFO; import static org.uberfire.workbench.events.NotificationEvent.NotificationType.SUCCESS; /** * <p>Main entry point for viewing a group instance.</p> * @since 0.8.0 */ @Dependent public class GroupEditorWorkflow implements IsWidget { public EntityWorkflowView view; ClientUserSystemManager userSystemManager; Caller<AuthorizationService> authorizationService; PermissionManager permissionManager; Event<OnErrorEvent> errorEvent; protected final ErrorCallback<Message> errorCallback = (Message message, Throwable throwable) -> { showError(throwable); return false; }; Event<NotificationEvent> workbenchNotification; Event<SaveGroupEvent> saveGroupEvent; Event<DeleteGroupEvent> deleteGroupEvent; ConfirmBox confirmBox; LoadingBox loadingBox; GroupEditor groupEditor; GroupEditorDriver groupEditorDriver; Group group; boolean isDirty; PerspectiveActivity selectedHomePerspective = null; @Inject public GroupEditorWorkflow(final ClientUserSystemManager userSystemManager, final Caller<AuthorizationService> authorizationService, final PermissionManager permissionManager, final Event<OnErrorEvent> errorEvent, final ConfirmBox confirmBox, final LoadingBox loadingBox, final Event<NotificationEvent> workbenchNotification, final Event<SaveGroupEvent> saveGroupEvent, final Event<DeleteGroupEvent> deleteGroupEvent, final GroupEditor groupEditor, final GroupEditorDriver groupEditorDriver, final EntityWorkflowView view) { this.userSystemManager = userSystemManager; this.authorizationService = authorizationService; this.permissionManager = permissionManager; this.errorEvent = errorEvent; this.confirmBox = confirmBox; this.workbenchNotification = workbenchNotification; this.saveGroupEvent = saveGroupEvent; this.deleteGroupEvent = deleteGroupEvent; this.groupEditor = groupEditor; this.view = view; this.groupEditorDriver = groupEditorDriver; this.loadingBox = loadingBox; this.isDirty = false; } @PostConstruct public void setup() { } @Override public Widget asWidget() { return view.asWidget(); } public GroupEditor getGroupEditor() { return groupEditor; } public void show(final String name) { doShow(name); } protected String getSaveButtonText() { return UsersManagementWidgetsConstants.INSTANCE.saveChanges(); } protected void onSave() { doSave(); } protected void onCancel() { doShow(group.getName()); } public void clear() { groupEditor.clear(); view.clearNotification(); group = null; } /* ****************************************************************************************************** PROTECTED PRESENTER API ****************************************************************************************************** */ void delete() { final String name = group.getName(); userSystemManager.groups((Void v) -> { deleteGroupEvent.fire(new DeleteGroupEvent(name)); workbenchNotification.fire(new NotificationEvent(UsersManagementWidgetsConstants.INSTANCE.groupRemoved(name), INFO)); clear(); }, errorCallback).delete(name); } protected void doShow(final String groupName) { assert groupName != null; // Configure the view. doInitView(); // Start the workflow's logic. checkDirty(() -> doLoad(groupName)); } protected void doInitView() { // Configure the workflow view. view.setWidget(groupEditor.asWidget()) .setCancelButtonVisible(true) .setSaveButtonVisible(true) .setSaveButtonEnabled(isDirty) .setSaveButtonText(getSaveButtonText()) .setCallback(new EntityWorkflowView.Callback() { @Override public void onSave() { GroupEditorWorkflow.this.onSave(); } @Override public void onCancel() { GroupEditorWorkflow.this.onCancel(); } }); } protected void doLoad(String name) { clear(); // Call backend service. showLoadingBox(); userSystemManager.groups((Group o) -> { hideLoadingBox(); GroupEditorWorkflow.this.group = o; assert group != null; edit(); }, errorCallback).get(name); } protected void doSave() { assert group != null; final boolean isValid = groupEditorDriver.flush(); this.group = groupEditorDriver.getValue(); PermissionCollection groupPermissions = groupEditorDriver.getPermissions(); PerspectiveActivity homePerspective = groupEditorDriver.getHomePerspective(); int groupPriority = groupEditorDriver.getGroupPriority(); if (isValid) { showLoadingBox(); // Update the current active policy AuthorizationPolicy authzPolicy = permissionManager.getAuthorizationPolicy(); authzPolicy.setHomePerspective(group, homePerspective.getIdentifier()); authzPolicy.setPriority(group, groupPriority); Collection<Permission> pc = authzPolicy.getPermissions(group).collection(); pc.clear(); pc.addAll(groupPermissions.collection()); // Save the policy in the backend authorizationService.call(r -> { hideLoadingBox(); isDirty = false; workbenchNotification.fire(new NotificationEvent(UsersManagementWidgetsConstants.INSTANCE.groupSaved(group.getName()), SUCCESS)); saveGroupEvent.fire(new SaveGroupEvent(group.getName())); doShow(group.getName()); }, errorCallback).savePolicy(authzPolicy); } else { throw new RuntimeException("Group must be valid before updating it."); } } protected void showNotification(String message) { view.showNotification(message); } protected void setDirty(final boolean isDirty) { this.isDirty = isDirty; view.setSaveButtonVisible(isDirty); view.setSaveButtonEnabled(isDirty); view.setCancelButtonVisible(true); if (isDirty) { view.showNotification(UsersManagementWidgetsConstants.INSTANCE.groupModified(group.getName())); } else { view.clearNotification(); } } protected void checkDirty(final Command callback) { if (isDirty) { confirmBox.show(UsersManagementWidgetsConstants.INSTANCE.confirmAction(), UsersManagementWidgetsConstants.INSTANCE.groupIsDirty(), () -> { GroupEditorWorkflow.this.isDirty = false; callback.execute(); }, () -> { }); } else { callback.execute(); } } protected void showLoadingBox() { loadingBox.show(); } protected void hideLoadingBox() { loadingBox.hide(); } protected void showError(final Throwable throwable) { final String msg = throwable != null ? throwable.getMessage() : UsersManagementWidgetsConstants.INSTANCE.genericError(); errorEvent.fire(new OnErrorEvent(GroupEditorWorkflow.this, msg)); } // Event observers protected void edit() { groupEditorDriver.edit(group, groupEditor); view.setCancelButtonVisible(false); view.setSaveButtonVisible(false); selectedHomePerspective = groupEditor.getAclSettings().getHomePerspective(); if (isPerspectiveReadDenied(selectedHomePerspective)) { showNotification(UsersManagementWidgetsConstants.INSTANCE.homePerspectiveReadDenied()); } } void onEditGroupEvent(@Observes final OnEditEvent onEditEvent) { if (checkEventContext(onEditEvent, groupEditor)) { edit(); } } void onDeleteGroupEvent(@Observes final OnDeleteEvent onDeleteEvent) { if (checkEventContext(onDeleteEvent, groupEditor)) { confirmBox.show(UsersManagementWidgetsConstants.INSTANCE.confirmAction(), UsersManagementWidgetsConstants.INSTANCE.ensureRemoveGroup(), this::delete, () -> { }); } } void onHomePerspectiveChangedEvent(@Observes final HomePerspectiveChangedEvent event) { if (checkEventContext(event, groupEditor.getAclSettings())) { selectedHomePerspective = event.getPerspective(); checkStatus(); } } void onPriorityChangedEvent(@Observes final PriorityChangedEvent event) { if (checkEventContext(event, groupEditor.getAclSettings())) { checkStatus(); } } void onPermissionChangedEvent(@Observes final PermissionChangedEvent event) { if (checkEventContext(event, groupEditor.getAclEditor())) { checkStatus(); } } void onPermissionAddedEvent(@Observes final PermissionNodeAddedEvent event) { if (checkEventContext(event, groupEditor.getAclEditor())) { checkStatus(); } } void onPermissionRemovedEvent(@Observes final PermissionNodeRemovedEvent event) { if (checkEventContext(event, groupEditor.getAclEditor())) { checkStatus(); } } protected boolean checkEventContext(final ContextualEvent contextualEvent, final Object context) { return contextualEvent != null && contextualEvent.getContext() != null && contextualEvent.getContext().equals(context); } protected void checkStatus() { boolean readDenied = isPerspectiveReadDenied(selectedHomePerspective); if (readDenied) { setDirty(false); showNotification(UsersManagementWidgetsConstants.INSTANCE.homePerspectiveReadDenied()); } else { setDirty(true); } } protected boolean isPerspectiveReadDenied(PerspectiveActivity perspectiveActivity) { if (perspectiveActivity == null) { return false; } PermissionCollection permissionCollection = groupEditor.permissions(); if (permissionCollection == null) { return false; } Permission p = permissionManager.createPermission(perspectiveActivity, PerspectiveAction.READ, false); Permission existing = permissionCollection.get(p.getName()); if (existing != null) { return existing.getResult().equals(AuthorizationResult.ACCESS_DENIED); } return permissionCollection.implies(p); } }