/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/providers/trunk/cm-authz-provider/src/java/org/sakaiproject/coursemanagement/impl/provider/CourseManagementGroupProvider.java $ * $Id: CourseManagementGroupProvider.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.coursemanagement.impl.provider; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.authz.api.GroupProvider; import org.sakaiproject.coursemanagement.api.CourseManagementService; import org.sakaiproject.coursemanagement.api.Section; import org.sakaiproject.coursemanagement.api.exception.IdNotFoundException; /** * A Sakai GroupProvider that utilizes the CourseManagementService and the * CmMappingService to supply authz data to Sakai. This implementation uses * a list of RoleResolvers, which can be used to resolve a user's role in a section * based on memberships in parent objects such as CourseSets. */ public class CourseManagementGroupProvider implements GroupProvider { private static final Log log = LogFactory.getLog(CourseManagementGroupProvider.class); // Configuration keys. public static final String SITE_ROLE_RESOLUTION_ORDER = "siteRoleResolutionOrder"; // Handle packing and unpacking safely. public static String EID_SEPARATOR = "+"; public static String QUOTED_SEPARATOR = "/+"; public static Pattern EID_SEPARATOR_PATTERN = Pattern.compile("(?<!/)\\+"); /** The course management service */ CourseManagementService cmService; /** The role resolvers to use when looking for CM roles in the hierarchy*/ List<RoleResolver> roleResolvers; /** The ordered list of role preferences. Roles earlier in the list are preferred to those later in the list. */ List<String> rolePreferences; /** Map to support external service configuration */ Map<String, Object> configuration; // GroupProvider methods /** * This method is not longer in use in Sakai. It should be removed from the * GroupProvider interface. */ public String getRole(String id, String user) { log.error("\n------------------------------------------------------------------\n"); log.error("THIS METHOD IS NEVER CALLED IN SAKAI. WHAT HAPPENED???"); log.error("\n------------------------------------------------------------------\n"); return null; } /** * Provides a Map of user IDs to (Sakai) roles for the Course Section EIDs specified * in the input AuthzGroup provider string. */ public Map<String, String> getUserRolesForGroup(String id) { if(log.isDebugEnabled()) log.debug("------------------CMGP.getUserRolesForGroup(" + id + ")"); Map<String, String> userRoleMap = new HashMap<String, String>(); String[] sectionEids = unpackId(id); if(log.isDebugEnabled()) log.debug(id + " is mapped to " + sectionEids.length + " sections"); for (RoleResolver rr : roleResolvers) { for(int i=0; i < sectionEids.length; i++) { String sectionEid = sectionEids[i]; Section section; try { section = cmService.getSection(sectionEid); } catch (IdNotFoundException e) { if (log.isWarnEnabled()) log.warn("Unable to find CM section " + sectionEid); continue; } if(log.isDebugEnabled()) log.debug("Looking for roles in section " + sectionEid); Map<String, String> rrUserRoleMap = rr.getUserRoles(cmService, section); for(Iterator<Entry<String, String>> rrRoleIter = rrUserRoleMap.entrySet().iterator(); rrRoleIter.hasNext();) { Entry<String, String> entry = rrRoleIter.next(); String userEid = entry.getKey(); String existingRole = entry.getValue(); String rrRole = rrUserRoleMap.get(userEid); // The Role Resolver has found no role for this user if(rrRole == null) { continue; } // Add or replace the role in the map if this is a more preferred role than the previous role if(existingRole == null) { if(log.isDebugEnabled()) log.debug("Adding "+ userEid + " to userRoleMap with role=" + rrRole); userRoleMap.put(userEid, rrRole); } else if(preferredRole(existingRole, rrRole).equals(rrRole)){ if(log.isDebugEnabled()) log.debug("Changing "+ userEid + "'s role in userRoleMap from " + existingRole + " to " + rrRole + " for section " + sectionEid); userRoleMap.put(userEid, rrRole); } } } } if(log.isDebugEnabled()) log.debug("_____________getUserRolesForGroup=" + userRoleMap); return userRoleMap; } /** * Provides a map of Course Section EIDs (which can be used as AuthzGroup provider IDs) * to Sakai roles for a given user. */ public Map<String, String> getGroupRolesForUser(String userEid) { if(log.isDebugEnabled()) log.debug("------------------CMGP.getGroupRolesForUser(" + userEid + ")"); Map<String, String> groupRoleMap = new HashMap<String, String>(); for(RoleResolver rr : roleResolvers) { Map<String, String> rrGroupRoleMap = rr.getGroupRoles(cmService, userEid); if(log.isDebugEnabled()) log.debug("Found " + rrGroupRoleMap.size() + " groups for " + userEid + " from resolver " + rr.getClass().getName()); // Only add the section eids if they aren't already in the map or if the new role has a higher preference. for(Iterator<Entry<String, String>> rrRoleIter = rrGroupRoleMap.entrySet().iterator(); rrRoleIter.hasNext();) { Entry<String, String> entry = rrRoleIter.next(); String sectionEid = entry.getKey(); String existingRole = groupRoleMap.get(sectionEid); String rrRole = entry.getValue(); // The Role Resolver has found no role for this section if(rrRole == null) { continue; } if(existingRole == null) { if(log.isDebugEnabled()) log.debug("Adding " + sectionEid + " to groupRoleMap with sakai role" + rrRole + " for user " + userEid); groupRoleMap.put(sectionEid, rrRole); } else if(preferredRole(existingRole, rrRole).equals(rrRole)){ if(log.isDebugEnabled()) log.debug("Changing "+ userEid + "'s role in groupRoleMap from " + existingRole + " to " + rrRole + " for section " + sectionEid); groupRoleMap.put(sectionEid, rrRole); } } } if(log.isDebugEnabled()) log.debug("______________getGroupRolesForUser=" + groupRoleMap); return groupRoleMap; } /** * {@inheritDoc} */ public String packId(String[] ids) { if(ids == null || ids.length == 0) { return null; } if(ids.length == 1) { return ids[0]; } StringBuilder sb = new StringBuilder(); for(int i=0; i<ids.length; i++) { // First, escape any embedded separator characters. String eid = (ids[i]).replace(EID_SEPARATOR, QUOTED_SEPARATOR); sb.append(eid); if(i < ids.length - 1) { sb.append(EID_SEPARATOR); } } return sb.toString(); } public String[] unpackId(String id) { if(id == null) { return new String[0]; } String[] ids = EID_SEPARATOR_PATTERN.split(id); // Unescape any embedded separator characters. for(int i=0; i<ids.length; i++) { String eid = (ids[i]).replace(QUOTED_SEPARATOR, EID_SEPARATOR); ids[i] = eid; } return ids; } // Utility methods public void init() { if(log.isInfoEnabled()) log.info("initializing " + this.getClass().getName()); /** * Use the externally supplied configuration map, if any. */ if (configuration != null) { if (rolePreferences != null) { log.warn("Both a provider configuration object and direct role mappings have been defined. " + "The configuration object will take precedence."); } setRolePreferences((List<String>)configuration.get(SITE_ROLE_RESOLUTION_ORDER)); } } public void destroy() { if(log.isInfoEnabled()) log.info("destroying " + this.getClass().getName()); } // Dependency injection public void setCmService(CourseManagementService cmService) { this.cmService = cmService; } public void setRoleResolvers(List<RoleResolver> roleResolvers) { this.roleResolvers = roleResolvers; } public String preferredRole(String one, String other) { int oneIndex = rolePreferences.indexOf(one); int otherIndex = rolePreferences.indexOf(other); if(otherIndex == -1) { return one; } if(oneIndex == -1) { return other; } return oneIndex < otherIndex ? one : other; } public void setRolePreferences(List<String> rolePreferences) { this.rolePreferences = rolePreferences; } public void setConfiguration(Map<String, Object> configuration) { this.configuration = configuration; } public boolean groupExists(String groupId) { if (cmService.isSectionDefined(groupId)) return true; return false; } }