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.io.UnsupportedEncodingException;
019import java.net.URISyntaxException;
020import java.net.URLDecoder;
021import java.util.Base64;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Set;
025
026import org.apache.avalon.framework.parameters.Parameters;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.acting.ServiceableAction;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Redirector;
032import org.apache.cocoon.environment.Request;
033import org.apache.cocoon.environment.SourceResolver;
034import org.apache.commons.lang.StringUtils;
035import org.apache.excalibur.source.Source;
036
037import org.ametys.core.right.RightManager;
038import org.ametys.core.user.CurrentUserProvider;
039import org.ametys.core.user.UserIdentity;
040import org.ametys.plugins.repository.AmetysObjectResolver;
041import org.ametys.plugins.repository.AmetysRepositoryException;
042import org.ametys.runtime.authentication.AccessDeniedException;
043import org.ametys.runtime.authentication.AuthorizationRequiredException;
044import org.ametys.web.repository.page.Page;
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
074        if (StringUtils.isBlank(path))
075        {
076            throw new IllegalArgumentException("Missing server directory's path");
077        }
078        
079        path = ServerDirectoryHelper.normalize(_decodePath(path));
080        
081        String zoneItemEncoded = parameters.getParameter("zoneItem");
082        String zoneItemId = new String(Base64.getUrlDecoder().decode(zoneItemEncoded.getBytes("UTF-8")));
083        ZoneItem zoneItem = (ZoneItem) _resolver.resolveById(zoneItemId);
084        
085        // Check page to page
086        _checkPageAccess(zoneItem);
087        
088        boolean enableDynamicPaths = zoneItem.getServiceParameters().getBoolean("enableDynamicPaths", false);
089        
090        String folder = ServerDirectoryHelper.normalize(zoneItem.getServiceParameters().getString("folder", null));
091        if (enableDynamicPaths)
092        {
093            String site = (String) request.getAttribute("site");
094            String language = (String) request.getAttribute("sitemapLanguage");
095            
096            folder = ServerDirectoryHelper.evaluateDynamicPath(folder, site, language, _currentUserProvider.getUser());
097            
098            if (!folder.startsWith("file:/"))
099            {
100                folder = "file:/" + folder;
101            }
102        }
103        
104        Set<Source> rootSources = ServerDirectoryHelper.getRootServerSources(_srcResolver);
105        if (!ServerDirectoryHelper.isValidPath(path, rootSources))
106        {
107            throw new AccessDeniedException("You are not allowed to access to server directory file " + path);
108        }
109        
110        if (!path.startsWith(folder))
111        {
112            throw new IllegalStateException("The server directory file '" + path + "' is not part of the current service : " + folder);
113        }
114        
115        result.put("path", path);
116        
117        return result;
118    }
119    
120    private void _checkPageAccess(ZoneItem zoneItem) throws AuthorizationRequiredException, AmetysRepositoryException, AccessDeniedException
121    {
122        Page page = zoneItem.getZone().getPage();
123        
124        if (!_rightManager.hasAnonymousReadAccess(page))
125        {
126            UserIdentity user = _currentUserProvider.getUser();
127            if (user == null)
128            {
129                throw new AuthorizationRequiredException(null);
130            }
131            else if (!_rightManager.hasReadAccess(user, page))
132            {
133                throw new AccessDeniedException("Access to page " + page.getPathInSitemap() + " is not allowed for user " + user);
134            }
135        }
136    }
137    
138    /**
139     * Decode the resource path
140     * @param path the resource path
141     * @return the decoded resource path
142     * @throws UnsupportedEncodingException if UTF-8 encoding is not supported
143     * @throws URISyntaxException if an error occurred
144     */
145    protected String _decodePath (String path) throws UnsupportedEncodingException, URISyntaxException
146    {
147        StringBuffer sb = new StringBuffer();
148        
149        String[] parts = path.split("/");
150        boolean first = true;
151        for (String part : parts)
152        {
153            if (first)
154            {
155                if (path.startsWith("/"))
156                {
157                    sb.append("/");
158                }
159                first = false;
160            }
161            else
162            {
163                sb.append("/");
164            }
165            
166            sb.append(URLDecoder.decode(part, "utf-8"));
167        }
168        
169        return sb.toString();
170    }
171}