001/*
002 *  Copyright 2012 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.web.pageaccess;
017
018import java.util.Map;
019
020import org.apache.avalon.framework.parameters.Parameters;
021import org.apache.avalon.framework.service.ServiceException;
022import org.apache.avalon.framework.service.ServiceManager;
023import org.apache.avalon.framework.thread.ThreadSafe;
024import org.apache.cocoon.acting.ServiceableAction;
025import org.apache.cocoon.environment.ObjectModelHelper;
026import org.apache.cocoon.environment.Redirector;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.environment.SourceResolver;
029import org.apache.commons.lang.StringUtils;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.core.user.CurrentUserProvider;
033import org.ametys.core.user.UserIdentity;
034import org.ametys.plugins.repository.AmetysObject;
035import org.ametys.plugins.repository.AmetysObjectResolver;
036import org.ametys.runtime.authentication.AccessDeniedException;
037import org.ametys.runtime.authentication.AuthorizationRequiredException;
038import org.ametys.web.pageaccess.ContentAccessManager.ContentAccess;
039import org.ametys.web.renderingcontext.RenderingContext;
040import org.ametys.web.renderingcontext.RenderingContextHandler;
041
042/**
043 * Test if a content is restricted or not, depending on the pages it's in.<br>
044 * <ul>
045 * <li>If at least one page is not restricted, returns EMPTY_MAP. The content or resource can be cached.</li>
046 * <li>If all its pages are restricted and the current user is allowed to access at least one, return null.
047 * The content or resource can be served but can't be cached.</li>
048 * <li>If all its pages are restricted but the current user is not allowed, an {@link AccessDeniedException} is thrown.</li>
049 * <li>If all its pages are restricted but no one is logged in, an {@link AuthorizationRequiredException} is thrown.</li>
050 * </ul>
051 */
052public class IsContentRestrictedAction extends ServiceableAction implements ThreadSafe
053{
054    
055    /** The content access manager. */
056    protected ContentAccessManager _contentAccessManager;
057    
058    /** The rendering context handler. */
059    protected RenderingContextHandler _renderingContextHandler;
060    
061    /** The Ametys object resolver. */
062    protected AmetysObjectResolver _resolver;
063    
064    /** The current user provider */
065    protected CurrentUserProvider _currentUserProvider;
066    
067    @Override
068    public void service(ServiceManager serviceManager) throws ServiceException
069    {
070        super.service(serviceManager);
071        _contentAccessManager = (ContentAccessManager) serviceManager.lookup(ContentAccessManager.ROLE);
072        _renderingContextHandler = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE);
073        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
074        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
075    }
076    
077    @Override
078    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
079    {
080        Request request = ObjectModelHelper.getRequest(objectModel);
081        
082        RenderingContext currentContext = _renderingContextHandler.getRenderingContext();
083        
084        if (currentContext == RenderingContext.BACK || currentContext == RenderingContext.PREVIEW)
085        {
086            // In back-office or in preview or live mode, access is always granted.
087            return EMPTY_MAP;
088        }
089            
090        UserIdentity user = _currentUserProvider.getUser();
091        
092        // Get the content from either the parameters or the request attributes.
093        AmetysObject ao = getAmetysObject(parameters, request);
094        if (ao == null)
095        {
096            throw new IllegalArgumentException("A valid ametys object must be provided.");
097        }
098        
099        // Objects other than contents cannot be restricted.
100        if (!(ao instanceof Content))
101        {
102            return EMPTY_MAP;
103        }
104        
105        ContentAccess access = _contentAccessManager.getAccess((Content) ao, user);
106        
107        if (access == ContentAccess.UNRESTRICTED)
108        {
109            return EMPTY_MAP;
110        }
111        else if (access == ContentAccess.ALLOWED)
112        {
113            return null;
114        }
115        else if (user == null)
116        {
117            // user not yet authenticated
118            throw new AuthorizationRequiredException();
119        }
120        
121        // In case of 'ContentAccess.FORBIDDEN'
122        throw new AccessDeniedException("Access to content " + ao.getId() + " is not allowed for user " + user);
123    }
124
125    /**
126     * Get the Ametys object from either the parameters or the request attributes.
127     * @param parameters the action parameters.
128     * @param request the request.
129     * @return the Ametys object or null if not found.
130     */
131    protected AmetysObject getAmetysObject(Parameters parameters, Request request)
132    {
133        AmetysObject ao = null;
134        
135        String contentId = parameters.getParameter("contentId", "");
136        String contentPath = parameters.getParameter("contentPath", "");
137        if (StringUtils.isNotEmpty(contentId))
138        {
139            ao = _resolver.resolveById(contentId);
140        }
141        else if (StringUtils.isNotEmpty(contentPath))
142        {
143            String decodedPath = contentPath.replaceAll("%3A", ":");
144            ao = _resolver.resolveByPath(decodedPath);
145        }
146        else
147        {
148            ao = (Content) request.getAttribute(Content.class.getName());
149        }
150        
151        return ao;
152    }
153}