001/*
002 *  Copyright 2015 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.plugins.serverdirectory;
017
018import java.util.Base64;
019import java.util.HashMap;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.avalon.framework.parameters.Parameters;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.acting.ServiceableAction;
027import org.apache.cocoon.environment.ObjectModelHelper;
028import org.apache.cocoon.environment.Redirector;
029import org.apache.cocoon.environment.Request;
030import org.apache.cocoon.environment.Response;
031import org.apache.cocoon.environment.SourceResolver;
032import org.apache.commons.lang.StringUtils;
033import org.apache.excalibur.source.Source;
034
035import org.ametys.core.right.RightManager;
036import org.ametys.core.user.CurrentUserProvider;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.core.util.URIUtils;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.AmetysRepositoryException;
041import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
042import org.ametys.runtime.authentication.AccessDeniedException;
043import org.ametys.runtime.authentication.AuthorizationRequiredException;
044import org.ametys.web.repository.page.SitemapElement;
045import org.ametys.web.repository.page.ZoneItem;
046
047/**
048 *  Check if we are authorized to access to the path
049 */
050public class CheckPathAccessAction extends ServiceableAction
051{
052    private org.apache.excalibur.source.SourceResolver _srcResolver;
053    private AmetysObjectResolver _resolver;
054    private CurrentUserProvider _currentUserProvider;
055    private RightManager _rightManager;
056
057    @Override
058    public void service(ServiceManager smanager) throws ServiceException
059    {
060        super.service(smanager);
061        _srcResolver = (org.apache.excalibur.source.SourceResolver) smanager.lookup(org.apache.excalibur.source.SourceResolver.ROLE);
062        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
063        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
064        _rightManager = (RightManager) smanager.lookup(RightManager.ROLE);
065    }
066    
067    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
068    {
069        Map<String, Object> result = new HashMap<>();
070
071        Request request = ObjectModelHelper.getRequest(objectModel);        
072        String path = request.getParameter("path");
073        String name = request.getParameter("name");
074
075        if (StringUtils.isBlank(path))
076        {
077            throw new IllegalArgumentException("Missing server directory's path");
078        }
079        if (StringUtils.isBlank(name))
080        {
081            throw new IllegalArgumentException("Missing server file's name");
082        }
083
084        String decodedPath = ServerDirectoryHelper.normalize(URIUtils.decode(path));
085        
086        String zoneItemEncoded = parameters.getParameter("zoneItem");
087        String zoneItemId = new String(Base64.getUrlDecoder().decode(zoneItemEncoded.getBytes("UTF-8")));
088        ZoneItem zoneItem = (ZoneItem) _resolver.resolveById(zoneItemId);
089        
090        // Check page to page
091        _checkPageAccess(zoneItem);
092        
093        ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
094        boolean enableDynamicPaths = serviceParameters.getValue("enableDynamicPaths", false, false);
095        
096        String folder = ServerDirectoryHelper.normalize(serviceParameters.getValue("folder"));
097        if (enableDynamicPaths)
098        {
099            String site = (String) request.getAttribute("site");
100            String language = (String) request.getAttribute("sitemapLanguage");
101            
102            folder = ServerDirectoryHelper.evaluateDynamicPath(folder, site, language, _currentUserProvider.getUser());
103            
104            if (!folder.startsWith("file:/"))
105            {
106                folder = "file:/" + folder;
107            }
108        }
109        
110        Set<Source> rootSources = ServerDirectoryHelper.getRootServerSources(_srcResolver);
111        if (!ServerDirectoryHelper.isValidPath(decodedPath, rootSources))
112        {
113            throw new AccessDeniedException("You are not allowed to access to server directory file " + decodedPath);
114        }
115        
116        if (!decodedPath.startsWith(folder))
117        {
118            throw new IllegalStateException("The server directory file '" + decodedPath + "' is not part of the current service : " + folder);
119        }
120        
121        result.put("path", path);
122        
123        String decodedName = ServerDirectoryHelper.normalize(URIUtils.decode(name));
124        Response response = ObjectModelHelper.getResponse(objectModel);
125        _setHeader(decodedName, response);
126        
127        return result;
128    }
129    
130    private void _checkPageAccess(ZoneItem zoneItem) throws AuthorizationRequiredException, AmetysRepositoryException, AccessDeniedException
131    {
132        SitemapElement sitemapElement = zoneItem.getZone().getSitemapElement();
133        
134        if (!_rightManager.hasAnonymousReadAccess(sitemapElement))
135        {
136            UserIdentity user = _currentUserProvider.getUser();
137            if (user == null)
138            {
139                throw new AuthorizationRequiredException(null);
140            }
141            else if (!_rightManager.hasReadAccess(user, sitemapElement))
142            {
143                throw new AccessDeniedException("Access to page " + sitemapElement.getSiteName() + "/" + sitemapElement.getSitemapName() + "/" + sitemapElement.getPathInSitemap() + " is not allowed for user " + user);
144            }
145        }
146    }
147    
148    /**
149     * Set Content-Disposition header at attachement, with the file name
150     * @param name file name to encode
151     * @param response request response where the header will be set
152     */
153    protected void _setHeader (String name, Response response)
154    {
155        String encodedName = URIUtils.encodeHeader(name);
156        response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedName + "\";filename*=UTF-8''" + encodedName);
157    }
158}