001/* 002 * Copyright 2017 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.rights.solrchecking; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Objects; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.activity.Initializable; 029import org.apache.avalon.framework.context.Context; 030import org.apache.avalon.framework.context.ContextException; 031import org.apache.avalon.framework.context.Contextualizable; 032import org.apache.avalon.framework.parameters.Parameters; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.cocoon.acting.ServiceableAction; 036import org.apache.cocoon.components.ContextHelper; 037import org.apache.cocoon.environment.ObjectModelHelper; 038import org.apache.cocoon.environment.Redirector; 039import org.apache.cocoon.environment.Request; 040import org.apache.cocoon.environment.SourceResolver; 041import org.apache.commons.lang3.StringUtils; 042import org.slf4j.Logger; 043 044import org.ametys.core.cocoon.JSonReader; 045import org.ametys.core.group.GroupIdentity; 046import org.ametys.core.group.GroupManager; 047import org.ametys.core.right.AllowedUsers; 048import org.ametys.core.right.RightManager; 049import org.ametys.core.user.UserIdentity; 050import org.ametys.core.util.AvalonLoggerAdapter; 051import org.ametys.plugins.repository.AmetysObject; 052import org.ametys.plugins.repository.AmetysObjectResolver; 053import org.ametys.plugins.repository.AmetysRepositoryException; 054import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 055import org.ametys.runtime.config.Config; 056 057/** 058 * Action called to know allowed users for each {@link AmetysObject} in a list. 059 */ 060public class AllowedUsersByObjectAction extends ServiceableAction implements Contextualizable, Initializable 061{ 062 /** The Ametys object resolver */ 063 protected AmetysObjectResolver _resolver; 064 /** The right manager */ 065 protected RightManager _rightManager; 066 /** The group manager */ 067 protected GroupManager _groupManager; 068 /** The Extension Point for doing additonal operations before and after calling {@link RightManager#getReadAccessAllowedUsers(Object)} in the iteration over objectIds in {@link #_act(String, Map, List)} */ 069 protected AllowedUsersActionAdditionalOperationsExtensionPoint _allowedUsersActionAdditionalOperationsEP; 070 071 private Context _context; 072 private Logger _logger; 073 074 @Override 075 public void service(ServiceManager smanager) throws ServiceException 076 { 077 super.service(smanager); 078 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 079 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 080 _groupManager = (GroupManager) smanager.lookup(GroupManager.ROLE); 081 _allowedUsersActionAdditionalOperationsEP = (AllowedUsersActionAdditionalOperationsExtensionPoint) smanager.lookup(AllowedUsersActionAdditionalOperationsExtensionPoint.ROLE); 082 } 083 084 @Override 085 public void contextualize(Context context) throws ContextException 086 { 087 _context = context; 088 } 089 090 @Override 091 public void initialize() throws Exception 092 { 093 // will be called after #enableLogging, so getLogger() will not return null 094 _logger = new AvalonLoggerAdapter(getLogger()); 095 } 096 097 @Override 098 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 099 { 100 Request request = ObjectModelHelper.getRequest(objectModel); 101 102 Map<String, Object> result = new HashMap<>(); 103 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 104 105 String coreName = parameters.getParameter("coreName"); 106 String objectIdsAsString = request.getParameter("ids"); 107 108 Map<String, Map<String, Object>> responseMap = new LinkedHashMap<>(); 109 result.put("response", responseMap); 110 List<String> unresolvedIds = new ArrayList<>(); 111 result.put("unresolvedIds", unresolvedIds); 112 113 114 // Retrieve the current workspace. 115 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 116 117 try 118 { 119 // Force the workspace. 120 String solrCorePrefix = Config.getInstance().getValueAsString("cms.solr.core.prefix"); 121 if (coreName.startsWith(solrCorePrefix)) 122 { 123 String workspaceName = coreName.substring(solrCorePrefix.length()); 124 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 125 _act(objectIdsAsString, responseMap, unresolvedIds); 126 } 127 else 128 { 129 throw new IllegalArgumentException("The core name '" + coreName + "' does not starts with the prefix '" + solrCorePrefix + "'"); 130 } 131 } 132 finally 133 { 134 // Restore context 135 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 136 } 137 138 return EMPTY_MAP; 139 } 140 141 private void _act(String objectIdsAsString, Map<String, Map<String, Object>> responseMap, List<String> unresolvedIds) 142 { 143 Request request = ContextHelper.getRequest(_context); 144 145 if (objectIdsAsString == null) 146 { 147 throw new IllegalArgumentException("{} was called with no parameter 'ids'. It is very likely that this request has exceeded the Tomcat maximum POST size. "); 148 } 149 String[] objectIds = StringUtils.split(objectIdsAsString, ','); 150 _logger.info("Start retrieving allowed users for {} ametys object id(s)", objectIds.length); 151 for (String objectId : objectIds) 152 { 153 AmetysObject ametysObject; 154 try 155 { 156 ametysObject = _resolver.resolveById(objectId); 157 } 158 catch (AmetysRepositoryException e) 159 { 160 unresolvedIds.add(objectId); 161 _logger.info("The object with id '{}' cannot be retrieved.", objectId, e); 162 continue; 163 } 164 165 try 166 { 167 _allowedUsersActionAdditionalOperationsEP.beforeGettingAllowedUsers(ametysObject, request); 168 AllowedUsers allowedUsersObj = _rightManager.getReadAccessAllowedUsers(ametysObject); 169 _allowedUsersActionAdditionalOperationsEP.afterGettingAllowedUsers(ametysObject, request); 170 171 Boolean anonymous = allowedUsersObj.isAnonymousAllowed(); 172 Boolean anyConnected = allowedUsersObj.isAnyConnectedUserAllowed(); 173 List<String> allowedGroupUsers = _usersFromGroupsAsStrings(allowedUsersObj.getAllowedGroups()); 174 List<String> deniedGroupUsers = _usersFromGroupsAsStrings(allowedUsersObj.getDeniedGroups()); 175 List<String> allowedUsers = _usersAsStrings(allowedUsersObj.getAllowedUsers()); 176 List<String> deniedUsers = _usersAsStrings(allowedUsersObj.getDeniedUsers()); 177 178 Map<String, Object> unresolvedAllowedUsers = new LinkedHashMap<>(); 179 unresolvedAllowedUsers.put("anonymous", anonymous); 180 unresolvedAllowedUsers.put("anyConnected", anyConnected); 181 unresolvedAllowedUsers.put("allowedGroupUsers", allowedGroupUsers); 182 unresolvedAllowedUsers.put("deniedGroupUsers", deniedGroupUsers); 183 unresolvedAllowedUsers.put("allowedUsers", allowedUsers); 184 unresolvedAllowedUsers.put("deniedUsers", deniedUsers); 185 responseMap.put(objectId, unresolvedAllowedUsers); 186 } 187 catch (Exception e) 188 { 189 unresolvedIds.add(objectId); 190 _logger.error("An unexpected exception occured when trying to get read access of object with id '{}'.", objectId, e); 191 } 192 } 193 194 _logger.info("End retrieving allowed users for {} ametys objects ids. {} id(s) could not be resolved.", objectIds.length, unresolvedIds.size()); 195 } 196 197 private List<String> _usersFromGroupsAsStrings(Collection<GroupIdentity> groups) 198 { 199 if (groups == null) 200 { 201 return Collections.EMPTY_LIST; 202 } 203 204 return groups.stream() 205 .map(_groupManager::getGroup) 206 .filter(Objects::nonNull) 207 .flatMap(group -> group.getUsers().stream()) 208 .distinct() 209 .map(UserIdentity::userIdentityToString) 210 .collect(Collectors.toList()); 211 } 212 213 private List<String> _usersAsStrings(Collection<UserIdentity> users) 214 { 215 if (users == null) 216 { 217 return Collections.EMPTY_LIST; 218 } 219 220 return users.stream() 221 .map(UserIdentity::userIdentityToString) 222 .collect(Collectors.toList()); 223 } 224}