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.Set;
026import java.util.stream.Collectors;
027
028import org.apache.avalon.framework.activity.Initializable;
029import org.apache.avalon.framework.parameters.Parameters;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.cocoon.acting.ServiceableAction;
033import org.apache.cocoon.environment.ObjectModelHelper;
034import org.apache.cocoon.environment.Redirector;
035import org.apache.cocoon.environment.Request;
036import org.apache.cocoon.environment.SourceResolver;
037import org.apache.commons.lang3.StringUtils;
038import org.slf4j.Logger;
039
040import org.ametys.core.cocoon.JSonReader;
041import org.ametys.core.group.GroupIdentity;
042import org.ametys.core.right.AllowedUsers;
043import org.ametys.core.user.UserIdentity;
044import org.ametys.core.util.AvalonLoggerAdapter;
045import org.ametys.plugins.repository.AmetysObject;
046import org.ametys.plugins.repository.AmetysObjectResolver;
047import org.ametys.plugins.repository.AmetysRepositoryException;
048import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
049import org.ametys.runtime.config.Config;
050
051/**
052 * Action called to know allowed users for each {@link AmetysObject} in a list.
053 */
054public class AllowedUsersByObjectAction extends ServiceableAction implements Initializable
055{
056    /** The Ametys object resolver */
057    protected AmetysObjectResolver _resolver;
058    
059    /** The helper for read access */
060    protected ReadAccessHelper _readAccessHelper;
061    
062    private Logger _logger;
063    
064    @Override
065    public void service(ServiceManager smanager) throws ServiceException
066    {
067        super.service(smanager);
068        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
069        _readAccessHelper = (ReadAccessHelper) smanager.lookup(ReadAccessHelper.ROLE);
070    }
071    
072    @Override
073    public void initialize() throws Exception
074    {
075        // will be called after #enableLogging, so getLogger() will not return null
076        _logger = new AvalonLoggerAdapter(getLogger());
077    }
078    
079    @Override
080    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
081    {
082        Request request = ObjectModelHelper.getRequest(objectModel);
083        
084        Map<String, Object> result = new HashMap<>();
085        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
086        
087        String coreName = parameters.getParameter("coreName");
088        String objectIdsAsString = request.getParameter("ids");
089        
090        Map<String, Map<String, Object>> responseMap = new LinkedHashMap<>();
091        result.put("response", responseMap);
092        List<String> unresolvedIds = new ArrayList<>();
093        result.put("unresolvedIds", unresolvedIds);
094
095
096        // Retrieve the current workspace.
097        String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
098        
099        try
100        {
101            // Force the workspace.
102            String solrCorePrefix = Config.getInstance().getValue("cms.solr.core.prefix");
103            if (coreName.startsWith(solrCorePrefix))
104            {
105                String workspaceName = coreName.substring(solrCorePrefix.length());
106                RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName);
107                _act(objectIdsAsString, responseMap, unresolvedIds);
108            }
109            else
110            {
111                throw new IllegalArgumentException("The core name '" + coreName + "' does not start with the prefix '" + solrCorePrefix + "'");
112            }
113        }
114        finally
115        {
116            // Restore context
117            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
118        }
119        
120        return EMPTY_MAP;
121    }
122    
123    private void _act(String objectIdsAsString, Map<String, Map<String, Object>> responseMap, List<String> unresolvedIds)
124    {
125        if (objectIdsAsString == null)
126        {
127            throw new IllegalArgumentException("{} was called with no parameter 'ids'. It is very likely that this request has exceeded the Tomcat maximum POST size. ");
128        }
129        String[] objectIds = StringUtils.split(objectIdsAsString, ',');
130        _logger.info("Start retrieving allowed users for {} ametys object id(s)", objectIds.length);
131        for (String objectId : objectIds)
132        {
133            AmetysObject ametysObject;
134            try
135            {
136                ametysObject = _resolver.resolveById(objectId);
137            }
138            catch (AmetysRepositoryException e)
139            {
140                unresolvedIds.add(objectId);
141                _logger.info("The object with id '{}' cannot be retrieved.", objectId, e);
142                continue;
143            }
144            
145            try
146            {
147                AllowedUsers allowedUsersObj = _readAccessHelper.allowedUsers(ametysObject);
148                
149                Boolean anonymous = allowedUsersObj.isAnonymousAllowed();
150                Boolean anyConnected = allowedUsersObj.isAnyConnectedUserAllowed();
151                Set<GroupIdentity> allowedGroups = allowedUsersObj.getAllowedGroups();
152                Set<GroupIdentity> deniedGroups = allowedUsersObj.getDeniedGroups();
153                
154                List<String> allowedGroupsStr = _groupsAsStrings(allowedGroups);
155                List<String> deniedGroupsStr = _groupsAsStrings(deniedGroups);
156                List<String> allowedUsers = _usersAsStrings(allowedUsersObj.getAllowedUsers());
157                List<String> deniedUsers = _usersAsStrings(allowedUsersObj.getDeniedUsers());
158                
159                Map<String, Object> unresolvedAllowedUsers = new LinkedHashMap<>();
160                unresolvedAllowedUsers.put("anonymous", anonymous);
161                unresolvedAllowedUsers.put("anyConnected", anyConnected);
162                unresolvedAllowedUsers.put("allowedGroups", allowedGroupsStr);
163                unresolvedAllowedUsers.put("deniedGroups", deniedGroupsStr);
164                unresolvedAllowedUsers.put("allowedUsers", allowedUsers);
165                unresolvedAllowedUsers.put("deniedUsers", deniedUsers);
166                responseMap.put(objectId, unresolvedAllowedUsers);
167            }
168            catch (Exception e)
169            {
170                unresolvedIds.add(objectId);
171                _logger.error("An unexpected exception occured when trying to get read access of object with id '{}'.", objectId, e);
172            }
173        }
174        
175        _logger.info("End retrieving allowed users for {} ametys objects ids. {} id(s) could not be resolved.", objectIds.length, unresolvedIds.size());
176    }
177    
178    private List<String> _groupsAsStrings(Collection<GroupIdentity> groups)
179    {
180        if (groups == null)
181        {
182            return Collections.EMPTY_LIST;
183        }
184        
185        return groups.stream()
186                    .map(GroupIdentity::groupIdentityToString)
187                    .collect(Collectors.toList());
188    }
189    
190    private List<String> _usersAsStrings(Collection<UserIdentity> users)
191    {
192        if (users == null)
193        {
194            return Collections.EMPTY_LIST;
195        }
196        
197        return users.stream()
198                    .map(UserIdentity::userIdentityToString)
199                    .collect(Collectors.toList());
200    }
201}