/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.roles.ui.wizard; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.teiid.designer.core.metamodel.aspect.sql.SqlTableAspect; import org.teiid.designer.metamodels.relational.ProcedureParameter; import org.teiid.designer.roles.Crud; import org.teiid.designer.roles.Crud.Type; import org.teiid.designer.roles.ui.RolesUiPlugin; import org.teiid.designer.roles.Permission; import org.teiid.designer.transformation.util.TransformationHelper; /** * @since 8.0 */ public class PermissionHandler { private DataRolesModelTreeProvider tree; private Map<Object, Permission> objectsToPermissionsMap; public PermissionHandler(DataRolesModelTreeProvider tree) { super(); this.tree = tree; this.objectsToPermissionsMap = new HashMap<Object, Permission>(); } public void handlePermissionChanged(Permission permission) { Object targetObject = null; for( Object obj : objectsToPermissionsMap.keySet()) { Permission perm = objectsToPermissionsMap.get(obj); if( perm == permission) { targetObject = obj; } } if( targetObject != null) { cleanUpPermissions(targetObject); } } /* * Because child permission values can default to parent Crud values there may be instances where a permission's CRUD * either exactly matches values from parents, or the CRUD is all NULL. In both cases we need to remove these Permissions * from the cache. * */ private void cleanUpPermissions( Object changedElement ) { // walk the tree, Permission perm = objectsToPermissionsMap.get(changedElement); if (perm == null || perm.isPrimary() || perm.isAllowLanguage()) { return; } if (perm.isNullCrud()) { if( perm.getCondition() == null && perm.getMask() == null ) { // System.out.println(" Removing Stale Permission for: " + perm.getTargetName()); objectsToPermissionsMap.remove(changedElement); } } else { boolean sameCreate = false; boolean sameRead = false; boolean sameUpdate = false; boolean sameDelete = false; boolean sameExecute = false; boolean sameAlter = false; // look at all 6 CRUD values and their Parent Perms and if they are the same as the CRUD value Permission parentPerm = getFirstParentPermission(changedElement, Crud.Type.CREATE); if (parentPerm != null) { if( (perm.getCRUDValue(Crud.Type.CREATE) == parentPerm.getCRUDValue(Crud.Type.CREATE)) || (perm.getCRUDValue(Crud.Type.CREATE) == null && parentPerm.getCRUDValue(Crud.Type.CREATE) != null) ) { sameCreate = true; } } if (!sameCreate) return; parentPerm = getFirstParentPermission(changedElement, Crud.Type.READ); if (parentPerm != null) { if( (perm.getCRUDValue(Crud.Type.READ) == parentPerm.getCRUDValue(Crud.Type.READ)) || (perm.getCRUDValue(Crud.Type.READ) == null && parentPerm.getCRUDValue(Crud.Type.READ) != null) ) { sameRead = true; } } if (!sameRead) return; parentPerm = getFirstParentPermission(changedElement, Crud.Type.UPDATE); if (parentPerm != null) { if( (perm.getCRUDValue(Crud.Type.UPDATE) == parentPerm.getCRUDValue(Crud.Type.UPDATE)) || (perm.getCRUDValue(Crud.Type.UPDATE) == null && parentPerm.getCRUDValue(Crud.Type.UPDATE) != null) ) { sameUpdate = true; } } if (!sameUpdate) return; parentPerm = getFirstParentPermission(changedElement, Crud.Type.DELETE); if (parentPerm != null) { if( (perm.getCRUDValue(Crud.Type.DELETE) == parentPerm.getCRUDValue(Crud.Type.DELETE)) || (perm.getCRUDValue(Crud.Type.DELETE) == null && parentPerm.getCRUDValue(Crud.Type.DELETE) != null) ) { sameDelete = true; } } parentPerm = getFirstParentPermission(changedElement, Crud.Type.EXECUTE); if (parentPerm != null) { if( (perm.getCRUDValue(Crud.Type.EXECUTE) == parentPerm.getCRUDValue(Crud.Type.EXECUTE)) || (perm.getCRUDValue(Crud.Type.EXECUTE) == null && parentPerm.getCRUDValue(Crud.Type.EXECUTE) != null) ) { sameExecute = true; } } parentPerm = getFirstParentPermission(changedElement, Crud.Type.ALTER); if (parentPerm != null) { if( (perm.getCRUDValue(Crud.Type.ALTER) == parentPerm.getCRUDValue(Crud.Type.ALTER)) || (perm.getCRUDValue(Crud.Type.ALTER) == null && parentPerm.getCRUDValue(Crud.Type.ALTER) != null) ) { sameAlter = true; } } if (sameCreate && sameRead && sameUpdate && sameDelete && sameExecute && sameAlter) { if( perm.getCondition() != null || perm.getMask() != null ) { // null out all crud values perm.setCRUD(null,null,null,null,null,null); } else { // System.out.println(" Removing Stale Permission for: " + perm.getTargetName()); objectsToPermissionsMap.remove(changedElement); } } } } /* * Gather up all child and grand-child permissions */ private void getChildPermissions(Object parent, Collection<Permission> allChildPermissions) { for (Object child : tree.getChildren(parent)) { Permission perm = this.objectsToPermissionsMap.get(child); if (perm != null) { allChildPermissions.add(perm); } getChildPermissions(child, allChildPermissions); } } /* * Gather all children below the parent that have an associated permission. * */ private void getChildrenWithPermission(Object parent, Collection<Object> childrenWithPermission) { for (Object child : tree.getChildren(parent)) { Permission perm = this.objectsToPermissionsMap.get(child); if (perm != null) { childrenWithPermission.add(child); } getChildrenWithPermission(child, childrenWithPermission); } } /** * Finds the first parent permission which contains a non-null Boolean crud value * @param element * @param crudType * @return */ public Permission getExistingPermission(Object element, Crud.Type crudType) { Permission perm = this.objectsToPermissionsMap.get(element); if (perm == null || perm.getCRUDValue(crudType) == null) { Object parent = tree.getParent(element); while (parent != null && (perm == null || perm.getCRUDValue(crudType) == null)) { perm = this.objectsToPermissionsMap.get(parent); parent = tree.getParent(parent); } } return perm; } /** * Finds the parent permission which contains a non-null Boolean crud value * @param element * @param crudType * @return parent permission. may be null */ public Permission getParentPermission(Object element, Crud.Type crudType) { Permission perm = this.objectsToPermissionsMap.get(element); if (perm != null && perm.getCRUDValue(crudType) != null) { return perm; } return null; } private Permission getModelPermssion(Object element) { if( element instanceof Resource ) { return this.objectsToPermissionsMap.get(element); } Object parent = tree.getParent(element); while (parent != null ) { if( parent instanceof Resource ) { return this.objectsToPermissionsMap.get(parent); } parent = tree.getParent(parent); } return null; } private Object getModelElement(Object element) { Object parent = element; while (parent != null ) { Object theParent = parent; if( theParent instanceof Resource ) { return theParent; } parent = tree.getParent(theParent); } return null; } /* * Find the first parent above the element with an NON-NULL boolean value for the specified CRUD type * */ private Permission getFirstParentPermission(Object element, Crud.Type crudType) { Permission perm = null; Object parent = tree.getParent(element); while (parent != null && (perm == null || perm.getCRUDValue(crudType) == null)) { perm = this.objectsToPermissionsMap.get(parent); parent = tree.getParent(parent); } return perm; } public Permission getPermission( Object element ) { return this.objectsToPermissionsMap.get(element); } /** * Helper method to determine if any child below a parent contains a CRUD value different than the parent. * */ public boolean hasChildWithDifferentCrudValue(Permission parentPermission, Object parent, Crud.Type type) { Boolean parentValue = parentPermission.getCRUDValue(type); Collection<Permission> childPermissions = new ArrayList<Permission>(); getChildPermissions(parent, childPermissions); for (Permission perm : childPermissions) { Boolean childValue = perm.getCRUDValue(type); if( childValue != null && parentPermission.childCrudValueIsDifferent(parentValue, childValue)) { return true; } } return false; } public boolean hasPermissions() { return !this.objectsToPermissionsMap.isEmpty(); } public void addPermission(Object key, Permission perm) { if (!this.objectsToPermissionsMap.containsKey(key)) { this.objectsToPermissionsMap.put(key, perm); } } private void disableNearestParentPermission(Object element, Crud.Type crudType) { Object firstParent = tree.getParent(element); Permission perm = this.objectsToPermissionsMap.get(firstParent); if( perm != null && perm.getCRUDValue(crudType) != null && perm.getCRUDValue(crudType) == Boolean.FALSE ) { perm.setCRUDValue(true, crudType); } else { Permission permiss = null; Object parent = tree.getParent(firstParent); while (parent != null && !(parent instanceof Resource) && permiss == null) { permiss = this.objectsToPermissionsMap.get(parent); parent = tree.getParent(parent); if( permiss != null && permiss.getCRUDValue(crudType) == Boolean.FALSE ) { perm.setCRUDValue(true, crudType); return; } } } } private boolean hasParentPermission(Object element, Crud.Type crudType) { Object firstParent = tree.getParent(element); Permission perm = this.objectsToPermissionsMap.get(firstParent); if( (perm == null || perm.getCRUDValue(crudType) == null)) { return false; } else { Permission permiss = perm; if( permiss.getCRUDValue(crudType) == Boolean.FALSE ) { return true; } Object parent = tree.getParent(firstParent); while (parent != null && !(parent instanceof Resource) && (permiss == null || permiss.getCRUDValue(crudType) == null)) { permiss = this.objectsToPermissionsMap.get(parent); parent = tree.getParent(parent); if( permiss != null && permiss.getCRUDValue(crudType) == Boolean.FALSE ) { return true; } } } return false; } @SuppressWarnings("unused") private Permission togglePermissionValue(Object element, boolean newValue, Crud.Type crudType) { Permission permission = this.objectsToPermissionsMap.get(element); if( permission == null ) { permission = new Permission(tree.getTargetName(element), new Crud(null, null, null, null, null, null)); this.objectsToPermissionsMap.put(element, permission); permission.setCRUDValue(newValue, crudType); } else { permission.toggleCRUDValue(crudType); } // If toggling permission value to TRUE, then need to set all Children to TRUE if perm SET_CHILD_PERMISSIONS : { Boolean parentValue = permission.getCRUDValue(crudType); Collection<Permission> childPermissions = new ArrayList<Permission>(); getChildPermissions(element, childPermissions); for (Permission perm : childPermissions) { if( perm.getCRUDValue(crudType) != null ) { perm.setCRUDValue(parentValue, crudType); } } } return permission; } /** * Performs the necessary permission CRUD value changes based on the target element and the CRUD type. * This method is targeted for use by a single-click editor changing ONE CRUD boolean value for one object. * * @param element * @param crudType */ public void toggleElementPermission( Object element, Crud.Type crudType ) { if (!supportsUpdates(element, crudType)) { return; } // See if model permission is TRUE or FALSE Permission modelPermission = getModelPermssion(element); Object modelElement = getModelElement(element); boolean firstTimePermission = false; if( modelPermission == null || modelPermission.getCRUDValue(crudType) == Boolean.FALSE) { // Model needs to get turned ON because this is the FIRST time this crud type is enabled if( modelPermission == null ) { Crud targetCrud = new Crud(null, null, null, null, null, null); modelPermission = new Permission(tree.getTargetName(modelElement), targetCrud); modelPermission.setCRUDValue(true, crudType); this.objectsToPermissionsMap.put(modelElement, modelPermission); } else { modelPermission.setCRUDValue(true, crudType); } firstTimePermission = true; } // if firstTimePermission == TRUE // Then we just need to set all non-checked siblings with PERMISSION = FALSE and all parent's siblings to FALSE // etc... until we reach the model if( firstTimePermission ) { disableAllSiblingPermissions(element, crudType); Object parent = this.tree.getParent(element); while( parent != null && parent != modelElement ) { Object theParent = parent; disableAllSiblingPermissions(parent, crudType); parent = tree.getParent(theParent); } } else { // Now we have an enabled Model Permission and need to look only at the selected object and it's siblings // toggle existing permission (or create new with false) boolean hasParentPermissionFalse = hasParentPermission(element, crudType); if( hasParentPermissionFalse ) { disableNearestParentPermission(element, crudType); disableAllSiblingPermissions(element, crudType); } togglePermissionValue(element, hasParentPermissionFalse, crudType); } // Now check if we need to remove any permissions based on the selected element. cleanUpPermissions(element); // Check on all other permissions and clean them up if necessary Collection<Object> childPermissions = new ArrayList<Object>(); getChildrenWithPermission(element, childPermissions); for (Object childWithPerm : childPermissions) { cleanUpPermissions(childWithPerm); } // If all of this node's siblings have same status - which is different than parent - change parent status, // then do a clean-up Object workingElem = element; while (!isPrimary(workingElem) && allSiblingsHaveSameStatus(workingElem, crudType)) { Object parent = tree.getParent(workingElem); if (parent != null && !haveSameStatus(parent, workingElem, crudType)) { // Get parent permission directly Permission parentPerm = this.objectsToPermissionsMap.get(parent); Permission elementPerm = getExistingPermission(workingElem, crudType); Boolean elementStatus = elementPerm.getCRUDValue(crudType); // Paren permission is null means the crud was inherited, so add an override if (parentPerm == null) { Crud targetCrud = new Crud(null, null, null, null, null, null); Permission newParentPermission = new Permission(tree.getTargetName(parent), targetCrud); newParentPermission.setCRUDValue(elementStatus, crudType); this.objectsToPermissionsMap.put(parent, newParentPermission); } else { parentPerm.setCRUDValue(elementStatus, crudType); } // Now check if we need to remove any permissions based on the selected element. cleanUpPermissions(parent); // Check on all other permissions and clean them up if necessary Collection<Object> pchildPermissions = new ArrayList<Object>(); getChildrenWithPermission(parent, pchildPermissions); for (Object childWithPerm : pchildPermissions) { cleanUpPermissions(childWithPerm); } } workingElem = parent; } } private void disableAllSiblingPermissions(Object element, Crud.Type crudType) { Object parent = tree.getParent(element); if( parent != null ) { for( Object child : tree.getChildren(parent) ) { if( child == element ) { continue; } Permission existingPermission = this.objectsToPermissionsMap.get(child); if( existingPermission == null ) { Crud childCrud = new Crud(null, null, null, null, null, null); Permission newPermission = new Permission(tree.getTargetName(child), childCrud); newPermission.setCRUDValue(false, crudType); this.objectsToPermissionsMap.put(child, newPermission); } else { existingPermission.setCRUDValue(false, crudType); } } } } /** * Check the 'toggle-ability' of the crud type of the supplied element. The status may return INFO - means a message should be * displayed to the user before toggling WARNING - means the user should be shown a confirmation message before toggling OK - * OK to toggle without showing a message or getting user confirmation * * @param element the element to toggle * @param crudType the crud type to toggle * @return the status of the toggle request */ public IStatus getToggleStatus( Object element, Crud.Type crudType ) { // Return INFO - if element does not support updates if (!supportsUpdates(element, crudType)) { return createStatus(IStatus.INFO, RolesUiPlugin.UTIL.getString("PermissionHandler.infoNotUpdatable")); //$NON-NLS-1$ } // Return WARNING - if element can be toggled, but doing so will toggle all its children Object[] children = tree.getChildren(element); if (children.length > 0) { String msg = null; if (crudType == Crud.Type.READ) { msg = RolesUiPlugin.UTIL.getString("PermissionHandler.warningWillToggleChildren"); //$NON-NLS-1$ } else { msg = RolesUiPlugin.UTIL.getString("PermissionHandler.warningWillToggleChildrenIfUpdatable"); //$NON-NLS-1$ } return createStatus(IStatus.WARNING, msg); } // OK status - no message return createStatus(IStatus.OK, ""); //$NON-NLS-1$ } /** * Create a Status object with the given status and message * * @param statusFlag the desired status * @param msg the desired message * @return the status object */ private IStatus createStatus( int statusFlag, String msg ) { final IStatus status = new Status(statusFlag, RolesUiPlugin.PLUGIN_ID, msg); return status; } /** * Determine if all siblings of the supplied element have the same state for the specified crudType * * @param element the element to check * @param crudType the crud type to check * @return 'true' if all siblings have the same status */ private boolean allSiblingsHaveSameStatus( Object element, Crud.Type crudType ) { boolean allSame = true; // Get parent of the provided element Object parent = tree.getParent(element); // Compare status of all the children if (parent != null) { int i = 0; Boolean firstStatus = null; // Accumulate all of the sibling states for (Object child : tree.getChildren(parent)) { // Current child's permission. If it does not have a permission, it inherits the parents. Permission perm = getExistingPermission(child, crudType); Boolean currentStatus = null; if (perm != null) { currentStatus = perm.getCRUDValue(crudType); } if (i == 0) { firstStatus = currentStatus; } else { if (currentStatus != firstStatus) { allSame = false; break; } } i++; } } return allSame; } /** * Determine if element 'isPrimary' */ private boolean isPrimary( Object element ) { Permission perm = objectsToPermissionsMap.get(element); if (perm != null && perm.isPrimary()) { return true; } return false; } /** * Helper method - determine if the two supplied elements have the same status for the specified crud type. */ private boolean haveSameStatus( Object elem1, Object elem2, Crud.Type crudType ) { Permission elem1Perm = getExistingPermission(elem1, crudType); Permission elem2Perm = getExistingPermission(elem2, crudType); Boolean elem1Status = elem1Perm.getCRUDValue(crudType); Boolean elem2Status = elem2Perm.getCRUDValue(crudType); if (elem1Status != null && elem2Status != null && elem1Status == elem2Status) { return true; } return false; } public boolean supportsUpdates( Object element, Crud.Type crudType ) { Object targetObj = element; if (TransformationHelper.isSqlColumn(element) || element instanceof ProcedureParameter) { targetObj = ((EObject)element).eContainer(); if (crudType == Type.DELETE) { return false; } } boolean isVirtualTable = TransformationHelper.isVirtualSqlTable(targetObj); if (TransformationHelper.isSqlTable(targetObj) && !TransformationHelper.isXmlDocument(targetObj)) { SqlTableAspect tableAspect = (SqlTableAspect)org.teiid.designer.core.metamodel.aspect.sql.SqlAspectHelper.getSqlAspect((EObject)targetObj); if (tableAspect != null) { if (isVirtualTable) { EObject transMappingRoot = TransformationHelper.getTransformationMappingRoot((EObject)targetObj); switch (crudType) { case CREATE: return TransformationHelper.isInsertAllowed(transMappingRoot); case READ: return true; case UPDATE: return TransformationHelper.isUpdateAllowed(transMappingRoot); case DELETE: return TransformationHelper.isDeleteAllowed(transMappingRoot); default: return false; } } else { return tableAspect.supportsUpdate((EObject)targetObj); } } } else { return true; } return false; } public void loadPermissions(Collection<Permission> permissions) { for (Permission perm : permissions) { if( perm.isAllowLanguage() ) { this.objectsToPermissionsMap.put(perm.getTargetName(), perm); } else { Object obj = tree.getPermissionTargetObject(perm); if (obj != null) { if (obj instanceof Resource) { perm.setPrimary(true); } // load the actual object to permission into map perm.setCanFilter(tree.allowsRowFilter(obj)); perm.setCanMask(tree.allowsColumnMask(obj)); this.objectsToPermissionsMap.put(obj, perm); } } } } public Collection<Permission> getPermissions() { return this.objectsToPermissionsMap.values(); } public List<Permission> getPermissionsWithRowBasedSecurity() { List<Permission> perms = new ArrayList<Permission>(10); for( Permission perm : getPermissions()) { if( perm.canFilter() && perm.getCondition() != null ) { perms.add(perm); } } return perms; } public Permission getPermission(String targetName) { for( Permission perm : objectsToPermissionsMap.values()) { if( perm.getTargetName().equals(targetName) ) { return perm; } } return null; } public List<Permission> getPermissionsWithColumnMasking() { List<Permission> perms = new ArrayList<Permission>(10); for( Permission perm : getPermissions()) { if( perm.canMask() && (perm.getMask() != null || perm.getCondition() != null) ) { perms.add(perm); } } return perms; } public void removeRowBasedSecurity(Permission permission) { permission.setCondition(null); permission.setConstraint(true); } public void removeColumnMask(Permission permission) { permission.setMask(null); permission.setOrder(0); permission.setCondition(null); } public void removeColumnMask(String targetName) { Permission existingPerm = getPermission(targetName); if( existingPerm != null ) { existingPerm.setMask(null); existingPerm.setOrder(0); existingPerm.setCondition(null); } } public void setColumnMask(String targetName, String condition, String mask, int order) { Permission existingPerm = getPermission(targetName); if( existingPerm == null ) { existingPerm = new Permission(targetName, new Crud(null, null, null, null, null, null)); existingPerm.setCanMask(true); this.objectsToPermissionsMap.put(targetName, existingPerm); } existingPerm.setMask(mask); existingPerm.setOrder(order); existingPerm.setCondition(condition); } public void setRowsBasedSecurity(String targetName, String condition, boolean constraint) { Permission existingPerm = getPermission(targetName); if( existingPerm == null ) { existingPerm = new Permission(targetName, new Crud(null, null, null, null, null, null)); existingPerm.setCanFilter(true); this.objectsToPermissionsMap.put(targetName, existingPerm); } existingPerm.setCondition(condition); existingPerm.setConstraint(constraint); } public List<String> getAllowedLanguages() { List<String> allowedLanguages = new ArrayList<String>(10); for( Permission perm : getPermissions()) { if( perm.isAllowLanguage() ) { allowedLanguages.add(perm.getTargetName()); } } return allowedLanguages; } public void addAllowedLanguage(String language) { Permission perm = new Permission(language, false, false, false, false, false, false); perm.setAllowLanguage(true); this.objectsToPermissionsMap.put(language, perm); } public void removeAllowedLanguage(String language) { this.objectsToPermissionsMap.remove(language); } }