/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-impl/src/main/java/org/sakaiproject/authz/impl/BaseAuthzGroupService.java $ * $Id: BaseAuthzGroupService.java 133031 2014-01-06 20:27:07Z matthew@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 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.authz.impl; import java.util.Collection; import java.util.Collections; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.authz.api.AuthzGroup; import org.sakaiproject.authz.api.AuthzGroupAdvisor; import org.sakaiproject.authz.api.AuthzGroupService; import org.sakaiproject.authz.api.AuthzPermissionException; import org.sakaiproject.authz.api.FunctionManager; import org.sakaiproject.authz.api.GroupAlreadyDefinedException; import org.sakaiproject.authz.api.GroupFullException; import org.sakaiproject.authz.api.GroupIdInvalidException; import org.sakaiproject.authz.api.GroupNotDefinedException; import org.sakaiproject.authz.api.GroupProvider; import org.sakaiproject.authz.api.Role; import org.sakaiproject.authz.api.RoleAlreadyDefinedException; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.entity.api.Edit; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.HttpAccess; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.event.api.EventTrackingService; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.javax.PagingPosition; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.time.api.Time; import org.sakaiproject.time.api.TimeService; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.user.api.UserNotDefinedException; import org.sakaiproject.util.StorageUser; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * <p> * BaseAuthzGroupService is a Sakai azGroup service implementation. * </p> * <p> * To support the public view feature, an AuthzGroup named TEMPLATE_PUBVIEW must exist, with a role named ROLE_PUBVIEW - all the abilities in this role become the public view abilities for any resource. * </p> */ public abstract class BaseAuthzGroupService implements AuthzGroupService { /** Our logger. */ private static Log M_log = LogFactory.getLog(BaseAuthzGroupService.class); /** Storage manager for this service. */ protected Storage m_storage = null; /** The initial portion of a relative access point URL. */ protected String m_relativeAccessPoint = null; /** A provider of additional Abilities for a userId. */ protected GroupProvider m_provider = null; /********************************************************************************************************************************************************************************************************************************************************** * Abstractions, etc. *********************************************************************************************************************************************************************************************************************************************************/ /** * Construct storage for this service. */ protected abstract Storage newStorage(); /** * Access the partial URL that forms the root of resource URLs. * * @param relative * if true, form within the access path only (i.e. starting with /content) * @return the partial URL that forms the root of resource URLs. */ protected String getAccessPoint(boolean relative) { return (relative ? "" : serverConfigurationService().getAccessUrl()) + m_relativeAccessPoint; } /** * Access the azGroup id extracted from an AuthzGroup reference. * * @param ref * The azGroup reference string. * @return The the azGroup id extracted from an AuthzGroup reference. */ protected String authzGroupId(String ref) { String start = getAccessPoint(true) + Entity.SEPARATOR; int i = ref.indexOf(start); if (i == -1) return ref; String id = ref.substring(i + start.length()); return id; } /** * Check security permission. * * @param lock * The lock id string. * @param resource * The resource reference string, or null if no resource is involved. * @return true if allowd, false if not */ protected boolean unlockCheck(String lock, String resource) { if (!securityService().unlock(lock, resource)) { return false; } return true; } /** * Check security permission. * * @param lock * The lock id string. * @param resource * The resource reference string, or null if no resource is involved. * @exception PermissionException * Thrown if the azGroup does not have access */ protected void unlock(String lock, String resource) throws AuthzPermissionException { if (!unlockCheck(lock, resource)) { throw new AuthzPermissionException(sessionManager().getCurrentSessionUserId(), lock, resource); } } /** * Create the live properties for the azGroup. */ protected void addLiveProperties(BaseAuthzGroup azGroup) { String current = sessionManager().getCurrentSessionUserId(); azGroup.m_createdUserId = current; azGroup.m_lastModifiedUserId = current; Time now = timeService().newTime(); azGroup.m_createdTime = now; azGroup.m_lastModifiedTime = (Time) now.clone(); } /** * Update the live properties for an AuthzGroup for when modified. */ protected void addLiveUpdateProperties(BaseAuthzGroup azGroup) { String current = sessionManager().getCurrentSessionUserId(); azGroup.m_lastModifiedUserId = current; azGroup.m_lastModifiedTime = timeService().newTime(); } /********************************************************************************************************************************************************************************************************************************************************** * Provider configuration *********************************************************************************************************************************************************************************************************************************************************/ /** * Configuration: set the azGroup provider helper service. * * @param provider * the azGroup provider helper service. */ public void setProvider(GroupProvider provider) { m_provider = provider; } /********************************************************************************************************************************************************************************************************************************************************** * Dependencies *********************************************************************************************************************************************************************************************************************************************************/ /** * @return the ServerConfigurationService collaborator. */ protected abstract ServerConfigurationService serverConfigurationService(); /** * @return the EntityManager collaborator. */ protected abstract EntityManager entityManager(); /** * @return the FunctionManager collaborator. */ protected abstract FunctionManager functionManager(); /** * @return the SecurityService collaborator. */ protected abstract SecurityService securityService(); /** * @return the TimeService collaborator. */ protected abstract TimeService timeService(); /** * @return the SessionManager collaborator. */ protected abstract SessionManager sessionManager(); /** * @return the EventTrackingService collaborator. */ protected abstract EventTrackingService eventTrackingService(); /** * @return the ServerConfigurationService collaborator. */ protected abstract UserDirectoryService userDirectoryService(); protected List<AuthzGroupAdvisor> authzGroupAdvisors; protected SiteService siteService; public void setSiteService(SiteService siteService) { this.siteService = siteService; } /********************************************************************************************************************************************************************************************************************************************************** * Init and Destroy *********************************************************************************************************************************************************************************************************************************************************/ /** * Final initialization, once all dependencies are set. */ public void init() { authzGroupAdvisors = new ArrayList<AuthzGroupAdvisor>(); try { m_relativeAccessPoint = REFERENCE_ROOT; // construct storage and read m_storage = newStorage(); m_storage.open(); // register as an entity producer entityManager().registerEntityProducer(this, REFERENCE_ROOT); // register functions functionManager().registerFunction(SECURE_ADD_AUTHZ_GROUP); functionManager().registerFunction(SECURE_REMOVE_AUTHZ_GROUP); functionManager().registerFunction(SECURE_UPDATE_AUTHZ_GROUP); functionManager().registerFunction(SECURE_UPDATE_OWN_AUTHZ_GROUP); functionManager().registerFunction(SECURE_VIEW_ALL_AUTHZ_GROUPS); // if no provider was set, see if we can find one if (m_provider == null) { m_provider = (GroupProvider) ComponentManager.get(GroupProvider.class.getName()); } M_log.info("init(): provider: " + ((m_provider == null) ? "none" : m_provider.getClass().getName())); } catch (Exception t) { M_log.warn("init(); ", t); } } /** * Returns to uninitialized state. */ public void destroy() { m_storage.close(); m_storage = null; M_log.info("destroy()"); } /********************************************************************************************************************************************************************************************************************************************************** * AuthzGroupService implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * {@inheritDoc} */ public List getAuthzGroups(String criteria, PagingPosition page) { return m_storage.getAuthzGroups(criteria, page); } /** * {@inheritDoc} */ public List getAuthzUserGroupIds(ArrayList authzGroupIds, String userid) { return m_storage.getAuthzUserGroupIds(authzGroupIds, userid); } /** * {@inheritDoc} */ public Collection<String> getAuthzUsersInGroups(Set<String> groupIds) { return m_storage.getAuthzUsersInGroups(groupIds); } /** * {@inheritDoc} */ public int countAuthzGroups(String criteria) { return m_storage.countAuthzGroups(criteria); } /** * {@inheritDoc} */ public Set getAuthzGroupIds(String providerId) { return m_storage.getAuthzGroupIds(providerId); } /** * {@inheritDoc} */ public Set getProviderIds(String authzGroupId) { return m_storage.getProviderIds(authzGroupId); } /** * {@inheritDoc} */ public AuthzGroup getAuthzGroup(String id) throws GroupNotDefinedException { // Note: since this is a "read" operations, we do NOT refresh (i.e. write) the provider info. if (id == null) throw new GroupNotDefinedException("<null>"); AuthzGroup azGroup = m_storage.get(id); // if not found if (azGroup == null) { throw new GroupNotDefinedException(id); } return azGroup; } /** * {@inheritDoc} */ public void joinGroup(String authzGroupId, String roleId) throws GroupNotDefinedException, AuthzPermissionException { joinGroup(authzGroupId, roleId, 0); } /** * {@inheritDoc} */ public void joinGroup(String authzGroupId, String roleId, int maxSize) throws GroupNotDefinedException, AuthzPermissionException, GroupFullException { String user = sessionManager().getCurrentSessionUserId(); if (user == null) { throw new AuthzPermissionException(null, SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); } // check security (throws if not permitted) unlock(SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); // get the AuthzGroup AuthzGroup azGroup = m_storage.get(authzGroupId); if (azGroup == null) { throw new GroupNotDefinedException(authzGroupId); } // check that the role exists Role role = azGroup.getRole(roleId); if (role == null) { throw new GroupNotDefinedException(roleId); } // KNL-523 separate join and unjoin events //((BaseAuthzGroup) azGroup).setEvent(SECURE_UPDATE_OWN_AUTHZ_GROUP); // see if already a member BaseMember grant = (BaseMember) azGroup.getMember(user); if (grant == null) addMemberToGroup(azGroup, user, roleId, maxSize); else // if inactive, deny permission to join if (!grant.active) throw new AuthzPermissionException(user, SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); // If the user is already in the group and active, or is already in the group and active but // with a different role, no action will be taken } /** * {@inheritDoc} */ public void unjoinGroup(String authzGroupId) throws GroupNotDefinedException, AuthzPermissionException { String user = sessionManager().getCurrentSessionUserId(); if (user == null) { throw new AuthzPermissionException(null, SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); } // check security (throws if not permitted) unlock(SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); // get the AuthzGroup AuthzGroup azGroup = m_storage.get(authzGroupId); if (azGroup == null) { throw new GroupNotDefinedException(authzGroupId); } // if not joined (no grant), we are done BaseMember grant = (BaseMember) azGroup.getMember(user); if (grant == null) { return; } // if the user currently is the only maintain role user, disallow the unjoin if (grant.getRole().getId().equals(azGroup.getMaintainRole())) { Set maintainers = azGroup.getUsersHasRole(azGroup.getMaintainRole()); if (maintainers.size() <= 1) { throw new AuthzPermissionException(user, SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); } } // if the grant is provided, disallow the unjoin. There would be no point in // allowing the user to unjoin, since the user will rejoin the realm the next // time it is updated or he/she logs in. if (grant.isProvided()) { throw new AuthzPermissionException(user, SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId); } // KNL-523 separate join and unjoin events //((BaseAuthzGroup) azGroup).setEvent(SECURE_UPDATE_OWN_AUTHZ_GROUP); removeMemberFromGroup(azGroup, user); } /** * {@inheritDoc} */ public boolean allowJoinGroup(String authzGroupId) { String user = sessionManager().getCurrentSessionUserId(); if (user == null) return false; // check security (throws if not permitted) if (!unlockCheck(SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId)) return false; // get the AuthzGroup AuthzGroup azGroup = m_storage.get(authzGroupId); if (azGroup == null) return false; // If already a member and inactive, disallow join BaseMember grant = (BaseMember) azGroup.getMember(user); if ((grant != null) && (!grant.active)) return false; return true; } /** * {@inheritDoc} */ public boolean allowUnjoinGroup(String authzGroupId) { String user = sessionManager().getCurrentSessionUserId(); if (user == null) { return false; } // check security (throws if not permitted) if (!unlockCheck(SECURE_UPDATE_OWN_AUTHZ_GROUP, authzGroupId)) return false; // get the azGroup AuthzGroup azGroup = m_storage.get(authzGroupId); if (azGroup == null) { return false; } // if not joined (no grant), unable to unjoin BaseMember grant = (BaseMember) azGroup.getMember(user); if (grant == null) { return false; } // if the grant is provider, unable to unjoin else if (grant.isProvided()) { return false; } // if the user currently is the only maintain role user, disallow the unjoin if (grant.getRole().getId().equals(azGroup.getMaintainRole())) { Set maintainers = azGroup.getUsersHasRole(azGroup.getMaintainRole()); if (maintainers.size() <= 1) { return false; } } return true; } /** * {@inheritDoc} */ public boolean allowUpdate(String id) { return unlockCheck(SECURE_UPDATE_AUTHZ_GROUP, authzGroupReference(id)); } /** * {@inheritDoc} */ public void save(AuthzGroup azGroup) throws GroupNotDefinedException, AuthzPermissionException { if (azGroup.getId() == null) throw new GroupNotDefinedException("<null>"); Reference ref = entityManager().newReference(azGroup.getId()); if (!siteService.allowUpdateSiteMembership(ref.getId())) { // check security (throws if not permitted) unlock(SECURE_UPDATE_AUTHZ_GROUP, authzGroupReference(azGroup.getId())); } // make sure it's in storage if (!m_storage.check(azGroup.getId())) { // if this was new, create it in storage if (((BaseAuthzGroup) azGroup).m_isNew) { // reserve an AuthzGroup with this id from the info store - if it's in use, this will return null AuthzGroup newAzg = m_storage.put(azGroup.getId()); if (newAzg == null) { M_log.warn("saveUsingSecurity, storage.put for a new returns null"); } } else { throw new GroupNotDefinedException(azGroup.getId()); } } // complete the save completeSave(azGroup); } /** * Complete the saving of the group, once id and security checks have been cleared. * * @param azGroup */ protected void completeSave(AuthzGroup azGroup) { // update the properties addLiveUpdateProperties((BaseAuthzGroup) azGroup); // allow any advisors to make last minute changes for (AuthzGroupAdvisor authzGroupAdvisor : authzGroupAdvisors) { authzGroupAdvisor.update(azGroup); } // complete the azGroup m_storage.save(azGroup); // track it String event = ((BaseAuthzGroup) azGroup).getEvent(); if (event == null) event = SECURE_UPDATE_AUTHZ_GROUP; eventTrackingService().post(eventTrackingService().newEvent(event, azGroup.getReference(), true)); // close the azGroup object ((BaseAuthzGroup) azGroup).closeEdit(); // update the db with latest provider, and site security with the latest changes, using the updated azGroup BaseAuthzGroup updatedRealm = (BaseAuthzGroup) m_storage.get(azGroup.getId()); updateSiteSecurity(updatedRealm); // clear the event for next time ((BaseAuthzGroup) azGroup).setEvent(null); } /** * Add member to a group, once id and security checks have been cleared. * * @param azGroup */ protected void addMemberToGroup(AuthzGroup azGroup, String userId, String roleId, int maxSize) throws GroupFullException { // update the properties (sets last modified time and modified-by) addLiveUpdateProperties((BaseAuthzGroup) azGroup); // allow any advisors to make last minute changes for (AuthzGroupAdvisor authzGroupAdvisor : authzGroupAdvisors) { authzGroupAdvisor.groupUpdate(azGroup, userId, roleId); } // add user to the azGroup m_storage.addNewUser(azGroup, userId, roleId, maxSize); // track it // KNL-523 set the event //String event = ((BaseAuthzGroup) azGroup).getEvent(); //if (event == null) event = SECURE_JOIN_AUTHZ_GROUP; eventTrackingService().post(eventTrackingService().newEvent(SECURE_JOIN_AUTHZ_GROUP, azGroup.getReference(), true)); // close the azGroup object ((BaseAuthzGroup) azGroup).closeEdit(); // update the db with latest provider, and site security with the latest changes, using the updated azGroup BaseAuthzGroup updatedRealm = (BaseAuthzGroup) m_storage.get(azGroup.getId()); updateSiteSecurity(updatedRealm); // clear the event for next time ((BaseAuthzGroup) azGroup).setEvent(null); } /** * Add member to a group, once id and security checks have been cleared. * * @param azGroup */ protected void removeMemberFromGroup(AuthzGroup azGroup, String userId) { // update the properties (sets last modified time and modified-by) addLiveUpdateProperties((BaseAuthzGroup) azGroup); // allow any advisors to make last minute changes for (AuthzGroupAdvisor authzGroupAdvisor : authzGroupAdvisors) { authzGroupAdvisor.groupUpdate(azGroup, userId, azGroup.getMember(userId).getRole().getId()); } // remove user from the azGroup m_storage.removeUser(azGroup, userId); // track it // KNL-523 set the event //String event = ((BaseAuthzGroup) azGroup).getEvent(); //if (event == null) event = SECURE_UNJOIN_AUTHZ_GROUP; eventTrackingService().post(eventTrackingService().newEvent(SECURE_UNJOIN_AUTHZ_GROUP, azGroup.getReference(), true)); // close the azGroup object ((BaseAuthzGroup) azGroup).closeEdit(); // update the db with latest provider, and site security with the latest changes, using the updated azGroup BaseAuthzGroup updatedRealm = (BaseAuthzGroup) m_storage.get(azGroup.getId()); updateSiteSecurity(updatedRealm); // clear the event for next time ((BaseAuthzGroup) azGroup).setEvent(null); } /** * {@inheritDoc} */ public boolean allowAdd(String id) { return unlockCheck(SECURE_ADD_AUTHZ_GROUP, authzGroupReference(id)); } /** * {@inheritDoc} */ public AuthzGroup addAuthzGroup(String id) throws GroupIdInvalidException, GroupAlreadyDefinedException, AuthzPermissionException { // check security (throws if not permitted) unlock(SECURE_ADD_AUTHZ_GROUP, authzGroupReference(id)); // reserve an AuthzGroup with this id from the info store - if it's in use, this will return null AuthzGroup azGroup = m_storage.put(id); if (azGroup == null) { throw new GroupAlreadyDefinedException(id); } ((BaseAuthzGroup) azGroup).setEvent(SECURE_ADD_AUTHZ_GROUP); // update the properties addLiveProperties((BaseAuthzGroup) azGroup); // save completeSave(azGroup); return azGroup; } /** * {@inheritDoc} */ public AuthzGroup addAuthzGroup(String id, AuthzGroup other, String userId) throws GroupIdInvalidException, GroupAlreadyDefinedException, AuthzPermissionException { // make the new AuthzGroup AuthzGroup azGroup = addAuthzGroup(id); // move in the values from the old AuthzGroup (this includes the id, which we restore ((BaseAuthzGroup) azGroup).set(other); ((BaseAuthzGroup) azGroup).m_id = id; // give the user the "maintain" role String roleName = azGroup.getMaintainRole(); if ((roleName != null) && (userId != null)) { if (azGroup.getRole(roleName) == null) { // add the "maintain" role to the azGroup try { azGroup.addRole(roleName); } catch (RoleAlreadyDefinedException e) { M_log.warn("addAuthzGroup: ", e); } } azGroup.addMember(userId, roleName, true, false); } // update the properties addLiveProperties((BaseAuthzGroup) azGroup); // save completeSave(azGroup); return azGroup; } /** * {@inheritDoc} */ public AuthzGroup newAuthzGroup(String id, AuthzGroup other, String userId) throws GroupAlreadyDefinedException { // make the new AuthzGroup BaseAuthzGroup azGroup = new BaseAuthzGroup(this,id); azGroup.m_isNew = true; // move in the values from the old AuthzGroup (this includes the id, which we restore) if (other != null) { azGroup.set(other); azGroup.m_id = id; } // give the user the "maintain" role String roleName = azGroup.getMaintainRole(); if ((roleName != null) && (userId != null)) { azGroup.addMember(userId, roleName, true, false); } return azGroup; } /** * {@inheritDoc} */ public boolean allowRemove(String id) { return unlockCheck(SECURE_REMOVE_AUTHZ_GROUP, authzGroupReference(id)); } /** * {@inheritDoc} */ public void removeAuthzGroup(AuthzGroup azGroup) throws AuthzPermissionException { // check security (throws if not permitted) unlock(SECURE_REMOVE_AUTHZ_GROUP, azGroup.getReference()); // allow any advisors to make last minute changes for (AuthzGroupAdvisor authzGroupAdvisor : authzGroupAdvisors) { authzGroupAdvisor.remove(azGroup); } // complete the azGroup m_storage.remove(azGroup); // track it eventTrackingService().post(eventTrackingService().newEvent(SECURE_REMOVE_AUTHZ_GROUP, azGroup.getReference(), true)); // close the azGroup object ((BaseAuthzGroup) azGroup).closeEdit(); // clear any site security based on this (if a site) azGroup removeSiteSecurity(azGroup); } /** * {@inheritDoc} */ public void removeAuthzGroup(String azGroupId) throws AuthzPermissionException { if (azGroupId == null) return; // check for existance AuthzGroup azGroup = m_storage.get(azGroupId); if (azGroup == null) { return; } // check security (throws if not permitted) unlock(SECURE_REMOVE_AUTHZ_GROUP, authzGroupReference(azGroupId)); // allow any advisors to make last minute changes for (AuthzGroupAdvisor authzGroupAdvisor : authzGroupAdvisors) { authzGroupAdvisor.remove(azGroup); } // complete the azGroup m_storage.remove(azGroup); // track it eventTrackingService().post(eventTrackingService().newEvent(SECURE_REMOVE_AUTHZ_GROUP, azGroup.getReference(), true)); // close the azGroup object ((BaseAuthzGroup) azGroup).closeEdit(); // clear any site security based on this (if a site) azGroup removeSiteSecurity(azGroup); } /** * {@inheritDoc} */ public String authzGroupReference(String id) { return getAccessPoint(true) + Entity.SEPARATOR + id; } /** * {@inheritDoc} */ public boolean isAllowed(String user, String function, String azGroupId) { return m_storage.isAllowed(user, function, azGroupId); } /** * {@inheritDoc} */ public boolean isAllowed(String user, String function, Collection azGroups) { return m_storage.isAllowed(user, function, azGroups); } /** * {@inheritDoc} */ public Set getUsersIsAllowed(String function, Collection azGroups) { return m_storage.getUsersIsAllowed(function, azGroups); } /** * {@inheritDoc} */ public Set<String[]> getUsersIsAllowedByGroup(String function, Collection<String> azGroups) { return m_storage.getUsersIsAllowedByGroup(function, azGroups); } /** * {@inheritDoc} */ public Map<String,Integer> getUserCountIsAllowed(String function, Collection<String> azGroups) { return m_storage.getUserCountIsAllowed(function, azGroups); } /** * {@inheritDoc} */ public Set getAllowedFunctions(String role, Collection azGroups) { return m_storage.getAllowedFunctions(role, azGroups); } /** * {@inheritDoc} */ public Set getAuthzGroupsIsAllowed(String userId, String function, Collection azGroups) { return m_storage.getAuthzGroupsIsAllowed(userId, function, azGroups); } /** * {@inheritDoc} */ public String getUserRole(String userId, String azGroupId) { return m_storage.getUserRole(userId, azGroupId); } /** * {@inheritDoc} */ public Map<String, String> getUserRoles(String userId, Collection<String> azGroupIds) { return m_storage.getUserRoles(userId, azGroupIds); } /** * {@inheritDoc} */ public Map getUsersRole(Collection userIds, String azGroupId) { return m_storage.getUsersRole(userIds, azGroupId); } /** * {@inheritDoc} */ public void refreshUser(String userId) { if ((m_provider == null) || (userId == null)) return; try { String eid = userDirectoryService().getUserEid(userId); // wrap the provided map in our special map that will deal with compound provider ids // suppressed using a properties setting and per // http://article.gmane.org/gmane.comp.cms.sakai.devel/36245 // DRS / Univ of VA SAK-1590 if (!serverConfigurationService().getBoolean("suppressCMRefresh", false)) { Map providerGrants = new ProviderMap(m_provider, m_provider.getGroupRolesForUser(eid)); m_storage.refreshUser(userId, providerGrants); } // update site security for this user - get the user's realms for the three site locks Set updAuthzGroups = getAuthzGroupsIsAllowed(userId, SiteService.SECURE_UPDATE_SITE, null); Set unpAuthzGroups = getAuthzGroupsIsAllowed(userId, SiteService.SITE_VISIT_UNPUBLISHED, null); Set visitAuthzGroups = getAuthzGroupsIsAllowed(userId, SiteService.SITE_VISIT, null); // convert from azGroup ids (potential site references) to site ids for those that are site, // skipping special and user sites other than our user's Set updSites = new HashSet(); for (Iterator i = updAuthzGroups.iterator(); i.hasNext();) { String azGroupId = (String) i.next(); Reference ref = entityManager().newReference(azGroupId); if ((SiteService.APPLICATION_ID.equals(ref.getType())) && SiteService.SITE_SUBTYPE.equals(ref.getSubType()) && !siteService.isSpecialSite(ref.getId()) && (!siteService.isUserSite(ref.getId()) || userId.equals(siteService.getSiteUserId(ref.getId())))) { updSites.add(ref.getId()); } } Set unpSites = new HashSet(); for (Iterator i = unpAuthzGroups.iterator(); i.hasNext();) { String azGroupId = (String) i.next(); Reference ref = entityManager().newReference(azGroupId); if ((SiteService.APPLICATION_ID.equals(ref.getType())) && SiteService.SITE_SUBTYPE.equals(ref.getSubType()) && !siteService.isSpecialSite(ref.getId()) && (!siteService.isUserSite(ref.getId()) || userId.equals(siteService.getSiteUserId(ref.getId())))) { unpSites.add(ref.getId()); } } Set visitSites = new HashSet(); for (Iterator i = visitAuthzGroups.iterator(); i.hasNext();) { String azGroupId = (String) i.next(); Reference ref = entityManager().newReference(azGroupId); if ((SiteService.APPLICATION_ID.equals(ref.getType())) && SiteService.SITE_SUBTYPE.equals(ref.getSubType()) && !siteService.isSpecialSite(ref.getId()) && (!siteService.isUserSite(ref.getId()) || userId.equals(siteService.getSiteUserId(ref.getId())))) { visitSites.add(ref.getId()); } } siteService.setUserSecurity(userId, updSites, unpSites, visitSites); } catch (UserNotDefinedException e) { M_log.warn("refreshUser: cannot find eid for user: " + userId); } } /** * Update the site security based on the values in the AuthzGroup, if it is a site AuthzGroup. * * @param azGroup * The AuthzGroup. */ protected void updateSiteSecurity(AuthzGroup azGroup) { // Special code for the site service Reference ref = entityManager().newReference(azGroup.getId()); if (SiteService.APPLICATION_ID.equals(ref.getType()) && SiteService.SITE_SUBTYPE.equals(ref.getSubType())) { // collect the users Set updUsers = azGroup.getUsersIsAllowed(SiteService.SECURE_UPDATE_SITE); Set unpUsers = azGroup.getUsersIsAllowed(SiteService.SITE_VISIT_UNPUBLISHED); Set visitUsers = azGroup.getUsersIsAllowed(SiteService.SITE_VISIT); siteService.setSiteSecurity(ref.getId(), updUsers, unpUsers, visitUsers); } } /** * Update the site security when an AuthzGroup is deleted, if it is a site AuthzGroup. * * @param azGroup * The AuthzGroup. */ protected void removeSiteSecurity(AuthzGroup azGroup) { // Special code for the site service Reference ref = entityManager().newReference(azGroup.getId()); if (SiteService.APPLICATION_ID.equals(ref.getType()) && SiteService.SITE_SUBTYPE.equals(ref.getSubType())) { // no azGroup, no users Set empty = new HashSet(); siteService.setSiteSecurity(ref.getId(), empty, empty, empty); } } /********************************************************************************************************************************************************************************************************************************************************** * EntityProducer implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * {@inheritDoc} */ public String getLabel() { return "authzGroup"; } /** * {@inheritDoc} */ public boolean willArchiveMerge() { return false; } /** * {@inheritDoc} */ public HttpAccess getHttpAccess() { return null; } /** * {@inheritDoc} */ public boolean parseEntityReference(String reference, Reference ref) { // for azGroup access String id = extractEntityId(reference); if (id != null) { ref.set(APPLICATION_ID, null, id, null, null); return true; } return false; } /** * Get the realm ID from a reference string. * @param reference The reference to a realm. eg<code>/realm//site/mercury</code> * @return The ID of the realm or <code>null</code> if it's not a realm reference. */ protected String extractEntityId(String reference) { if (reference.startsWith(REFERENCE_ROOT) && REFERENCE_ROOT.length() + 1 <= reference.length()) { // the azGroup id may have separators - we use everything after "/realm/" String id = reference.substring(REFERENCE_ROOT.length() + 1, reference.length()); return id; } return null; } /** * {@inheritDoc} */ public String getEntityDescription(Reference ref) { return null; } /** * {@inheritDoc} */ public ResourceProperties getEntityResourceProperties(Reference ref) { return null; } /** * {@inheritDoc} */ public Entity getEntity(Reference ref) { return null; } /** * {@inheritDoc} */ public Collection getEntityAuthzGroups(Reference ref, String userId) { // double check that it's mine if (!APPLICATION_ID.equals(ref.getType())) return null; Collection rv = new Vector(); // if the reference is an AuthzGroup, and not a special one // get the list of realms for the azGroup-referenced resource if ((ref.getId() != null) && (ref.getId().length() > 0) && (!ref.getId().startsWith("!"))) { // add the current user's azGroup (for what azGroup stuff everyone can do, i.e. add) ref.addUserAuthzGroup(rv, sessionManager().getCurrentSessionUserId()); // make a new reference on the azGroup's id Reference refnew = entityManager().newReference(ref.getId()); rv.addAll(refnew.getAuthzGroups(userId)); } return rv; } /** * {@inheritDoc} */ public String getEntityUrl(Reference ref) { return null; } /** * {@inheritDoc} */ public String archive(String siteId, Document doc, Stack stack, String archivePath, List attachments) { return ""; } /** * {@inheritDoc} */ public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames, Map userIdTrans, Set userListAllowImport) { return ""; } /********************************************************************************************************************************************************************************************************************************************************** * Storage *********************************************************************************************************************************************************************************************************************************************************/ protected interface Storage { /** * Open. */ void open(); /** * Close. */ void close(); /** * Check if an AuthzGroup by this id exists. * * @param id * The AuthzGroup id. * @return true if an AuthzGroup by this id exists, false if not. */ boolean check(String id); /** * Get the AuthzGroup with this id, or null if not found. * * @param id * The AuthzGroup id. * @return The AuthzGroup with this id, or null if not found. */ AuthzGroup get(String id); /** * Add a new AuthzGroup with this id. * * @param id * The AuthzGroup id. * @return The new AuthzGroup, or null if the id is in use. */ AuthzGroup put(String id); /** * Save the changes to the AuthzGroup * * @param azGroup * The AuthzGroup to save. */ void save(AuthzGroup azGroup); /** * Add a user to the AuthzGroup * * @param azGroup * The AuthzGroup to which the user is being added * @param userId * The user to add * @param roleId * The user's role * @param maxSize * The maximum size of the group. */ void addNewUser(AuthzGroup azGroup, String userId, String roleId, int maxSize) throws GroupFullException; /** * Remove a user from the AuthzGroup * * @param azGroup * The AuthzGroup to which the user is being added * @param userId * The user to remove */ void removeUser(AuthzGroup azGroup, String userId); /** * Remove this AuthzGroup. * * @param azGroup * The azGroup to remove. */ void remove(AuthzGroup azGroup); /** * Access a list of AuthzGroups that meet specified criteria, naturally sorted. * * @param criteria * Selection criteria: AuthzGroups returned will match this string somewhere in their id, or provider group id. * @param page * The PagePosition subset of items to return. * @return The List (AuthzGroup) of AuthzGroups that meet specified criteria. */ List getAuthzGroups(String criteria, PagingPosition page); /** * Access a list of AuthzGroups that meet specified criteria for a specified user_id * * @param authzGroupIds * AuthzGroup selection criteria (list of authz group ids) * @param user_id * Return only groups with user_id as a member * @return The List (AuthzGroup) that meet specified criteria. */ List getAuthzUserGroupIds(ArrayList authzGroupIds, String user_id); /** * Return a list of users in the specified group list * * @param groupIds set of authz group ids * @return collection of user ids */ Collection<String> getAuthzUsersInGroups(Set<String> groupIds); /** * Count the AuthzGroup objets that meet specified criteria. * * @param criteria * Selection criteria: realms returned will match this string somewhere in their id, or provider group id. * @return The count of AuthzGroups that meet specified criteria. */ int countAuthzGroups(String criteria); /** * Get the provider IDs for an AuthzGroup * * @param authzGroupId The ID of the AuthzGroup * @return The Set (String) of provider IDs */ public Set getProviderIds(String authzGroupId); /** * Get the AuthzGroup IDs associated with a provider ID. * * @param providerId The provider id * @return The Set (String) of AuthzGroup IDs */ public Set getAuthzGroupIds(String providerId); /** * Complete the read process once the basic AuthzGroup info has been read. * * @param azGroup * The AuthzGroup to complete. */ void completeGet(BaseAuthzGroup azGroup); /** * Test if this user is allowed to perform the function in the named AuthzGroup. * * @param userId * The user id. * @param function * The function to open. * @param azGroupId * The AuthzGroup id to consult, if it exists. * @return true if this user is allowed to perform the function in the named AuthzGroup, false if not. */ boolean isAllowed(String userId, String function, String azGroupId); /** * Test if this user is allowed to perform the function in the named AuthzGroups. * * @param userId * The user id. * @param function * The function to open. * @param azGroups * A collection of AuthzGroup ids to consult. * @return true if this user is allowed to perform the function in the named AuthzGroups, false if not. */ boolean isAllowed(String userId, String function, Collection<String> realms); /** * Get the set of user ids of users who are allowed to perform the function in the named AuthzGroups. * * @param function * The function to check. * @param azGroups * A collection of the ids of AuthzGroups to consult. * @return the Set (String) of user ids of users who are allowed to perform the function in the named AuthzGroups. */ Set getUsersIsAllowed(String function, Collection azGroups); /** * Get the set of user ids per group of users who are allowed to perform the function in the named AuthzGroups. * * @param function * The function to check. * @param azGroups * A collection of the ids of AuthzGroups to consult. * @return A Set of String arrays (userid, realm) with user ids per group who are allowed to perform the function in the named AuthzGroups. */ Set<String[]> getUsersIsAllowedByGroup(String function, Collection<String> azGroups); /** * Get the number of users per group who are allowed to perform the function in the given AuthzGroups. * * @param function * The function to check. * @param azGroups * A collection of the ids of AuthzGroups to consult. * @return A Map (authzgroupid (String) -> user count (Integer) ) of the number of users who are allowed to perform the function in the given AuthzGroups. */ Map<String,Integer> getUserCountIsAllowed(String function, Collection<String> azGroups); /** * Get the set of functions that users with this role in these AuthzGroups are allowed to perform. * * @param role * The role name. * @param azGroups * A collection of AuthzGroup ids to consult. * @return the Set (String) of functions that users with this role in these AuthzGroups are allowed to perform */ Set getAllowedFunctions(String role, Collection azGroups); /** * Get the set of AuthzGroup ids in which this user is allowed to perform this function. * * @param userId * The user id. * @param function * The function to check. * @param azGroups * The Collection of AuthzGroup ids to search; if null, search them all. * @return the Set (String) of AuthzGroup ids in which this user is allowed to perform this function. */ Set getAuthzGroupsIsAllowed(String userId, String function, Collection azGroups); /** * Get the role name for this user in this AuthzGroup. * * @param userId * The user id. * @param function * The function to open. * @param azGroupId * The AuthzGroup id to consult, if it exists. * @return the role name for this user in this AuthzGroup, if the user has active membership, or null if not. */ String getUserRole(String userId, String azGroupId); /** * Get all role names for a given user in a set of AuthzGroups. * * @param userId * The user ID of the person to search for. * @param azGroupIds * A collection of AuthzGroup IDs to narrow the search (may be empty or null to search all). * @return A Map<String, String> (AuthzGroup ID -> role name) for every AuthzGroup where the user is a member, filtered to the set of AuthzGroups in azGroupIds (if non-null and non-empty). * */ Map<String, String> getUserRoles(String userId, Collection<String> azGroupIds); /** * Get the role name for each user in the userIds Collection in this AuthzGroup. * * @param userId * The user id. * @param function * The function to open. * @param azGroupId * The AuthzGroup id to consult, if it exists. * @return A Map (userId -> role name) of role names for each user who have active membership; if the user does not, it will not be in the Map. */ Map getUsersRole(Collection userIds, String azGroupId); /** * Refresh this user's roles in any AuthzGroup that has an entry in the map; the user's new role is in the map. * * @param userId * The user id * @param providerMembership * The Map of external group id -> role id. */ void refreshUser(String userId, Map providerMembership); /** * Refresh the external user - role membership for this AuthzGroup * * @param azGroup * The azGroup to refresh. */ void refreshAuthzGroup(BaseAuthzGroup azGroup); /** * Retrieve all maintain roles * * @return a String Set of all maintain roles */ public Set<String> getMaintainRoles(); } public class ProviderMap implements Map { protected Map m_wrapper = null; protected GroupProvider m_provider = null; public ProviderMap(GroupProvider provider, Map wrapper) { m_provider = provider; m_wrapper = wrapper; } public void clear() { m_wrapper.clear(); } public boolean containsKey(Object key) { return m_wrapper.containsKey(key); } public boolean containsValue(Object value) { return m_wrapper.containsValue(value); } public Set entrySet() { return m_wrapper.entrySet(); } public Object get(Object key) { // if we have this key exactly, use it Object value = m_wrapper.get(key); if (value != null) return value; // otherwise break up key as a compound id and find what values we have for these // the values are roles, and we prefer "maintain" to "access" String rv = null; String[] ids = m_provider.unpackId((String) key); for (int i = 0; i < ids.length; i++) { // try this one value = m_wrapper.get(ids[i]); // if we found one already, ask the provider which to keep if (value != null) { rv = m_provider.preferredRole((String)value, rv); } } return rv; } public boolean isEmpty() { return m_wrapper.isEmpty(); } public Set keySet() { return m_wrapper.keySet(); } public Object put(Object key, Object value) { return m_wrapper.put(key, value); } public void putAll(Map t) { m_wrapper.putAll(t); } public Object remove(Object key) { return m_wrapper.remove(key); } public int size() { return m_wrapper.size(); } public Collection values() { return m_wrapper.values(); } } @Override public void addAuthzGroupAdvisor(AuthzGroupAdvisor advisor) { if (advisor != null) { authzGroupAdvisors.add(advisor); } } @Override public boolean removeAuthzGroupAdvisor(AuthzGroupAdvisor advisor) { if (advisor != null) { return authzGroupAdvisors.remove(advisor); } return false; } @Override public List<AuthzGroupAdvisor> getAuthzGroupAdvisors() { return Collections.unmodifiableList(authzGroupAdvisors); } public Set getMaintainRoles(){ return m_storage.getMaintainRoles(); } }