package org.sakaiproject.delegatedaccess.entity; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.swing.tree.DefaultMutableTreeNode; import lombok.Getter; import lombok.Setter; import org.sakaiproject.delegatedaccess.logic.ProjectLogic; import org.sakaiproject.delegatedaccess.logic.SakaiProxy; import org.sakaiproject.delegatedaccess.model.AccessNode; import org.sakaiproject.delegatedaccess.model.ListOptionSerialized; import org.sakaiproject.delegatedaccess.model.NodeModel; import org.sakaiproject.delegatedaccess.util.DelegatedAccessConstants; import org.sakaiproject.delegatedaccess.util.DelegatedAccessMutableTreeNode; import org.sakaiproject.entitybroker.EntityReference; import org.sakaiproject.entitybroker.EntityView; import org.sakaiproject.entitybroker.entityprovider.CoreEntityProvider; import org.sakaiproject.entitybroker.entityprovider.annotations.EntityCustomAction; import org.sakaiproject.entitybroker.entityprovider.capabilities.AutoRegisterEntityProvider; import org.sakaiproject.entitybroker.entityprovider.capabilities.PropertyProvideable; import org.sakaiproject.entitybroker.entityprovider.capabilities.RESTful; import org.sakaiproject.entitybroker.entityprovider.capabilities.RequestAware; import org.sakaiproject.entitybroker.entityprovider.capabilities.RequestStorable; import org.sakaiproject.entitybroker.entityprovider.extension.Formats; import org.sakaiproject.entitybroker.entityprovider.extension.RequestGetter; import org.sakaiproject.entitybroker.entityprovider.extension.RequestStorage; import org.sakaiproject.entitybroker.entityprovider.search.Search; import org.sakaiproject.tool.api.Tool; import org.sakaiproject.user.api.User; /** * This is the RESTful service for the Shopping Period Admin. This allows an instructor to * update their own shopping period information through site-manage * * @author Bryan Holladay (holladay@longsight.com) * */ public class DelegatedAccessEntityProviderImpl implements DelegatedAccessEntityProvider, CoreEntityProvider, AutoRegisterEntityProvider, PropertyProvideable, RequestStorable, RESTful, RequestAware { @Getter @Setter private ProjectLogic projectLogic; @Getter @Setter private SakaiProxy sakaiProxy; public String getEntityPrefix() { return ENTITY_PREFIX; } private String getFirstNodeId(String siteRef, String hierarchyId){ Map<String, List<String>> nodeIds = projectLogic.getNodesBySiteRef(new String[]{siteRef}, hierarchyId); if(nodeIds != null && nodeIds.containsKey(siteRef) && nodeIds.get(siteRef) != null && nodeIds.get(siteRef).size() == 1){ return nodeIds.get(siteRef).get(0); }else{ return null; } } public boolean entityExists(String id) { String firstNodeId = getFirstNodeId("/site/" + id, DelegatedAccessConstants.HIERARCHY_ID); return firstNodeId != null; } public String getPropertyValue(String reference, String name) { return getProperties(reference).get(name); } public Map<String, String> getProperties(String reference) { String siteRef = "/site/" + reference.substring(reference.lastIndexOf("/") + 1); String nodeId = getFirstNodeId(siteRef, DelegatedAccessConstants.HIERARCHY_ID); if(nodeId == null){ throw new IllegalArgumentException("NodeId: " + reference + " doesn't exist"); } NodeModel node = projectLogic.getNodeModel(nodeId, DelegatedAccessConstants.SHOPPING_PERIOD_USER); if(node == null){ throw new IllegalArgumentException("NodeId: " + nodeId + " doesn't exist"); } Map<String,String> valuesMap = new HashMap<String, String>(); valuesMap.put("shoppingStartDate", Long.toString(node.getNodeShoppingPeriodStartDate().getTime())); valuesMap.put("shoppingEndDate", Long.toString(node.getNodeShoppingPeriodEndDate().getTime())); valuesMap.put("shoppingRealm", node.getNodeAccessRealmRole()[0]); valuesMap.put("shoppingRole", node.getNodeAccessRealmRole()[1]); valuesMap.put("directAccess", "" + node.isDirectAccess()); return valuesMap; } public void setPropertyValue(String reference, String name, String value) { // TODO Auto-generated method stub } public List<String> findEntityRefs(String[] prefixes, String[] name, String[] searchValue, boolean exactMatch) { // TODO Auto-generated method stub return null; } public String createEntity(EntityReference ref, Object entity, Map<String, Object> params) { // TODO Auto-generated method stub return null; } public Object getSampleEntity() { // TODO Auto-generated method stub return null; } public void updateEntity(EntityReference ref, Object entity, Map<String, Object> params) { boolean isSuperUser = sakaiProxy.isSuperUser(); boolean isInstructor = sakaiProxy.isUserInstructor(sakaiProxy.getCurrentUserId(), ref.getId()); if(!(isSuperUser || (isInstructor && sakaiProxy.isShoppingPeriodInstructorEditable()))){ //we only want to allow user's who are either an admin or is an actual member of the site and has "instructor" permsission (site.upd) //otherwise, they can just use the Delegated Access interface to make modifications throw new IllegalArgumentException("User: " + sakaiProxy.getCurrentUserId() + " is not a member of the site with site.upd permission or an admin"); } String siteRef = "/site/" + ref.getId(); String nodeId = getFirstNodeId(siteRef, DelegatedAccessConstants.HIERARCHY_ID); if(nodeId == null){ throw new IllegalArgumentException("Node doesn't exist or has multiple instances: " + ref.getId()); } String shoppingStartDateStr = (String) params.get("shoppingStartDate"); String shoppingEndDateStr = (String) params.get("shoppingEndDate"); String role = (String) params.get("shoppingRole"); String realm = (String) params.get("shoppingRealm"); Object authToolsList = params.get("shoppingShowAuthTools"); Object publicToolsList = params.get("shoppingShowPublicTools"); boolean directAccess = true; if(params.get("directAccess") != null){ directAccess = Boolean.valueOf("" + params.get("directAccess")); } String[] authTools = null; if(authToolsList != null){ if(authToolsList instanceof String[]){ authTools = (String[]) params.get("shoppingShowAuthTools"); }else if(authToolsList instanceof String && !"".equals(authToolsList)){ authTools = new String[]{(String) authToolsList}; } }; String[] publicTools = null; if(publicToolsList != null){ if(publicToolsList instanceof String[]){ publicTools = (String[]) params.get("shoppingShowPublicTools"); }else if(publicToolsList instanceof String && !"".equals(publicToolsList)){ publicTools = new String[]{(String) publicToolsList}; } }; Date shoppingStartDate = null; Date shoppingEndDate = null; if(shoppingStartDateStr != null && !"".equals(shoppingStartDateStr)){ try{ shoppingStartDate = new Date(Long.parseLong(shoppingStartDateStr)); }catch (Exception e) { throw new IllegalArgumentException("shoppingStartDate: " + shoppingStartDateStr + " is not a valid date time."); } } if(shoppingEndDateStr != null && !"".equals(shoppingEndDateStr)){ try{ shoppingEndDate = new Date(Long.parseLong(shoppingEndDateStr)); }catch (Exception e) { throw new IllegalArgumentException("shoppingEndDate: " + shoppingEndDateStr + " is not a valid date time."); } } //get the node to store the information: NodeModel node = projectLogic.getNodeModel(nodeId, DelegatedAccessConstants.SHOPPING_PERIOD_USER); //lets verify the instructor is able to make these modifications: //is this disabled: if(node.getNodeShoppingPeriodRevokeInstructorEditable()){ throw new IllegalArgumentException("This node has the ability for an instructor to make edits to the shopping period settings revoked. Node: " + node.getNodeId() + ", ref: " + ref.getId()); } if(!directAccess){ //no need to continue, just set direct access to false and save (will clear out the rest and cause //the node to inherrit it's settings node.setDirectAccess(false); DefaultMutableTreeNode treeNode = new DelegatedAccessMutableTreeNode(); treeNode.setUserObject(node); projectLogic.updateNodePermissionsForUser(treeNode, DelegatedAccessConstants.SHOPPING_PERIOD_USER); return; } //Get Original Settings before we modify it boolean directAccessOrig = node.isDirectAccess(); Date startDateOrig = node.getShoppingPeriodStartDate(); Date endDateOrig = node.getShoppingPeriodEndDate(); String realmOrig = node.getRealm(); String roleOrig = node.getRole(); String[] authToolsOrig = node.convertListToArray(node.getSelectedRestrictedAuthTools()); String[] publicToolsOrig = node.convertListToArray(node.getSelectedRestrictedPublicTools()); boolean instructorEditedOrig = node.isInstructorEdited(); //modify the setting to the new settings node.setShoppingPeriodStartDate(shoppingStartDate); node.setShoppingPeriodEndDate(shoppingEndDate); node.setRealm(realm); node.setRole(role); node.setInstructorEdited(isInstructor); node.setRestrictedAuthTools(projectLogic.getEntireToolsList()); if(authTools != null){ for(String toolId : authTools){ node.setAuthToolRestricted(toolId, true); } } node.setRestrictedPublicTools(projectLogic.getEntireToolsList()); if(node.getNodeShoppingPeriodRevokeInstructorPublicOpt()){ //since the instructor isn't allowed to edit public options, make sure that the inherritted //options are stored in this node since they could have chosen "override": if(directAccessOrig != directAccess){ //if direct access is changing from not accessed to access, then just grab the inheritted tools, otherwise, keep what is already saved publicTools = node.convertListToArray(node.getInheritedRestrictedPublicTools()); }else{ //direct access isn't change, so just keep the existing restricted tools for this node publicTools = publicToolsOrig; } } if(publicTools != null){ for(String toolId : publicTools){ node.setPublicToolRestricted(toolId, true); } } //user could have checked overrideDirectAccess and changed nothing else node.setDirectAccess(directAccess); //Get new modified settings Date startDateNew = node.getShoppingPeriodStartDate(); Date endDateNew = node.getShoppingPeriodEndDate(); String realmNew = node.getRealm(); String roleNew = node.getRole(); String[] authToolsNew = node.convertListToArray(node.getSelectedRestrictedAuthTools()); String[] publicToolsNew = node.convertListToArray(node.getSelectedRestrictedPublicTools()); //Set advanced options to what it inherits since instructors can't edit this if this is the first time this node is being selected, //otherwise, keep whatever was assigned to it if(directAccessOrig != directAccess){ node.setShoppingPeriodRevokeInstructorEditable(node.getInheritedShoppingPeriodRevokeInstructorEditable()); node.setShoppingPeriodRevokeInstructorPublicOpt(node.getInheritedShoppingPeriodRevokeInstructorPublicOpt()); }else{ node.setShoppingPeriodRevokeInstructorEditable(node.isShoppingPeriodRevokeInstructorEditable()); node.setShoppingPeriodRevokeInstructorPublicOpt(node.isShoppingPeriodRevokeInstructorPublicOpt()); } //only update if there were true modifications if(directAccessOrig != directAccess || node.isModified(startDateOrig, startDateNew, endDateOrig, endDateNew, realmOrig, realmNew, roleOrig, roleNew, authToolsOrig, authToolsNew, publicToolsOrig, publicToolsNew, false, false, false, false, node.isAllowBecomeUser(), node.isAllowBecomeUser(), node.isInstructorEdited(), instructorEditedOrig)){ DefaultMutableTreeNode treeNode = new DelegatedAccessMutableTreeNode(); treeNode.setUserObject(node); projectLogic.updateNodePermissionsForUser(treeNode, DelegatedAccessConstants.SHOPPING_PERIOD_USER); } } public Object getEntity(EntityReference ref) { String siteRef = "/site/" + ref.getId(); String nodeId = getFirstNodeId(siteRef, DelegatedAccessConstants.HIERARCHY_ID); if(nodeId == null){ throw new IllegalArgumentException("NodeId for Site: " + ref + " doesn't exist"); } NodeModel node = projectLogic.getNodeModel(nodeId, DelegatedAccessConstants.SHOPPING_PERIOD_USER); if(node == null){ throw new IllegalArgumentException("NodeId: " + nodeId + " doesn't exist"); } Map valuesMap = new HashMap<String, String>(); valuesMap.put("shoppingStartDate", node.getNodeShoppingPeriodStartDate()); valuesMap.put("shoppingEndDate", node.getNodeShoppingPeriodEndDate()); valuesMap.put("shoppingRealm", node.getNodeAccessRealmRole()[0]); valuesMap.put("shoppingRole", node.getNodeAccessRealmRole()[1]); valuesMap.put("directAccess", node.isDirectAccess()); valuesMap.put("revokeInstructorEditable", node.getNodeShoppingPeriodRevokeInstructorEditable()); valuesMap.put("revokeInstructorPublicOpt", node.getNodeShoppingPeriodRevokeInstructorPublicOpt()); valuesMap.put("shoppingShowAuthTools", node.getNodeRestrictedAuthTools()); valuesMap.put("shoppingShowPublicTools", node.getNodeRestrictedPublicTools()); return valuesMap; } @EntityCustomAction(action="canEditShopping",viewKey=EntityView.VIEW_LIST) public List<?> canEditShopping(EntityView view, Map<String, Object> params) { Boolean canEdit = Boolean.FALSE; String option = view.getPathSegment(2); if(option == null || "".equals(option)){ throw new IllegalArgumentException("Expected url path is /canEditShopping/site/{id}"); } if("site".equals(option)){ String siteId = view.getPathSegment(3); if(siteId != null && !"".equals(siteId)){ if(sakaiProxy.isSuperUser() || (sakaiProxy.isUserInstructor(sakaiProxy.getCurrentUserId(), siteId)) && sakaiProxy.isShoppingPeriodInstructorEditable()){ String siteRef = "/site/" + siteId; String nodeId = getFirstNodeId(siteRef, DelegatedAccessConstants.HIERARCHY_ID); if(nodeId == null){ throw new IllegalArgumentException("Node doesn't exist or has multiple instances: " + siteRef); }else{ //get the node to find the settings NodeModel node = projectLogic.getNodeModel(nodeId, DelegatedAccessConstants.SHOPPING_PERIOD_USER); //lets verify the instructor is able to make these modifications: //is this disabled: if(!node.getNodeShoppingPeriodRevokeInstructorEditable()){ canEdit = Boolean.TRUE; } } } } } return Arrays.asList(canEdit); } /** * shoppingOptions/roles * shoppingOptions/tools * * @param view * @param params * @return */ @EntityCustomAction(action="shoppingOptions",viewKey=EntityView.VIEW_LIST) public List<?> getShoppingOptions(EntityView view, Map<String, Object> params) { String option = view.getPathSegment(2); if(option == null || "".equals(option)){ throw new IllegalArgumentException("An option is required: shoppingOptions/roles, shoppingOptions/tools, shoppingOptions/authorization"); } if("roles".equals(option)){ return convertMapToSerializedList(projectLogic.getRealmRoleDisplay(true)); }else if("tools".equals(option)){ return convertListToSerializedList(projectLogic.getEntireToolsList()); }else{ throw new IllegalArgumentException("A valid option is required: shoppingOptions/roles, shoppingOptions/tools, shoppingOptions/authorization"); } } private List<GenericOutputSerialized> convertListToSerializedList(List<ListOptionSerialized> list){ List<GenericOutputSerialized> returnList = new ArrayList<GenericOutputSerialized>(); for(ListOptionSerialized l : list){ returnList.add(new GenericOutputSerialized(l.getId(), l.getName())); } sortGenericOutputList(returnList); return returnList; } private List<GenericOutputSerialized> convertMapToSerializedList(Map<String, String> map){ List<GenericOutputSerialized> returnList = new ArrayList<GenericOutputSerialized>(); for(Entry<String, String> entry : map.entrySet()){ returnList.add(new GenericOutputSerialized(entry.getKey(), entry.getValue())); } sortGenericOutputList(returnList); return returnList; } private void sortGenericOutputList(List<GenericOutputSerialized> l){ Collections.sort(l, new Comparator<GenericOutputSerialized>() { public int compare(GenericOutputSerialized arg0, GenericOutputSerialized arg1) { return arg0.getValue().compareToIgnoreCase(arg1.getValue()); } }); } @EntityCustomAction(action="initialize",viewKey=EntityView.VIEW_LIST) public List initializeAccessForSite(EntityView view, Map<String, Object> params) { String option = view.getPathSegment(2); if(option == null || "".equals(option)){ throw new IllegalArgumentException("Expected url path is /initialize/site/{id}"); } if("site".equals(option)){ String siteId = view.getPathSegment(3); if(siteId != null && !"".equals(siteId)){ return Arrays.asList(projectLogic.getCurrentUsersAccessToSite("/site/" + siteId)); }else{ throw new IllegalArgumentException("Expected url path is /initialize/site/{id}"); } }else{ throw new IllegalArgumentException("Expected url path is /initialize/site/{id}"); } } @EntityCustomAction(action="access",viewKey=EntityView.VIEW_LIST) public List getUsersWithAccessToSite(EntityView view, Map<String, Object> params) { String option = view.getPathSegment(2); if(option == null || "".equals(option)){ throw new IllegalArgumentException("Expected url path is /access/site/{id}"); } if("site".equals(option)){ String siteId = view.getPathSegment(3); if(siteId != null && !"".equals(siteId)){ //check the user is actually an instructor or super admin: if(!sakaiProxy.isSuperUser() && !sakaiProxy.isUserInstructor(sakaiProxy.getCurrentUserId(), siteId)){ throw new IllegalArgumentException("You must be an instructor or admin user to view this information."); } List<Map<String, Object>> returnList = new ArrayList<Map<String,Object>>(); for(AccessNode node : projectLogic.getUserAccessForSite("/site/" + siteId).values()){ Map<String,Object> accessMap = new HashMap<String, Object>(); accessMap.put("realm", ""); accessMap.put("role", ""); if(node.getAccess() != null && node.getAccess().length == 2){ accessMap.put("realm", node.getAccess()[0]); accessMap.put("role", node.getAccess()[1]); } accessMap.put("deniedTools", node.getDeniedAuthTools()); accessMap.put("userId", node.getUserId()); User user = sakaiProxy.getUser(node.getUserId()); accessMap.put("userEid", ""); accessMap.put("userDisplayName", ""); if(user != null){ accessMap.put("userEid", user.getEid()); accessMap.put("userDisplayName", user.getDisplayName()); } String deniedToolsNames = ""; if(node.getDeniedAuthTools() != null){ for(String toolId : node.getDeniedAuthTools()){ Tool tool = sakaiProxy.getTool(toolId); if(tool != null){ if(!deniedToolsNames.equals("")){ deniedToolsNames += ", "; } deniedToolsNames += tool.getTitle(); } } } accessMap.put("deniedToolsNames", deniedToolsNames); returnList.add(accessMap); } //filter hidden roles: String[] hiddenRoles = sakaiProxy.getHideRolesForInstructorViewAccess(); if(hiddenRoles != null){ for (Iterator iterator = returnList.iterator(); iterator.hasNext();) { Map<String, Object> map = (Map<String, Object>) iterator.next(); for(String role : hiddenRoles){ if(map.containsKey("role") && role.equals(map.get("role"))){ iterator.remove(); break; } } } } return returnList; }else{ throw new IllegalArgumentException("Expected url path is /access/site/{id}"); } }else{ throw new IllegalArgumentException("Expected url path is /access/site/{id}"); } } public void deleteEntity(EntityReference ref, Map<String, Object> params) { // TODO Auto-generated method stub } public List<?> getEntities(EntityReference ref, Search search) { // TODO Auto-generated method stub return null; } public String[] getHandledOutputFormats() { return new String[] { Formats.HTML, Formats.XML, Formats.JSON }; } public String[] getHandledInputFormats() { // TODO Auto-generated method stub return null; } public void setRequestGetter(RequestGetter requestGetter) { // TODO Auto-generated method stub } public void setRequestStorage(RequestStorage requestStorage) { // TODO Auto-generated method stub } public class GenericOutputSerialized implements Serializable{ public String key; public String value; public GenericOutputSerialized(String key, String value){ this.key = key; this.value = value; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } }