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.plugins.workspaces.documents;
017
018import java.util.Collections;
019import java.util.HashSet;
020import java.util.Set;
021
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.commons.lang.StringUtils;
025
026import org.ametys.cms.transformation.URIResolver;
027import org.ametys.core.authentication.token.AuthenticationTokenManager;
028import org.ametys.core.user.CurrentUserProvider;
029import org.ametys.core.user.UserIdentity;
030import org.ametys.core.util.URIUtils;
031import org.ametys.plugins.explorer.resources.Resource;
032import org.ametys.plugins.explorer.resources.ResourceCollection;
033import org.ametys.plugins.repository.AmetysObject;
034import org.ametys.plugins.repository.UnknownAmetysObjectException;
035import org.ametys.plugins.repository.version.VersionableAmetysObject;
036import org.ametys.plugins.workspaces.project.objects.Project;
037
038/**
039 * {@link URIResolver} for webdav "project-resource". <br>
040 * These links point to a file from the resources of a project through webdav
041 */
042public class WebdavProjectResourceURIResolver extends ProjectResourceURIResolver
043{
044    /** The authentication token manager */
045    protected AuthenticationTokenManager _authenticationTokenManager;
046    
047    /** Current User Provider */
048    protected CurrentUserProvider _currentUserProvider;
049    
050    @Override
051    public void service(ServiceManager manager) throws ServiceException
052    {
053        super.service(manager);
054        _authenticationTokenManager = (AuthenticationTokenManager) manager.lookup(AuthenticationTokenManager.ROLE);
055        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
056    }
057    
058    @Override
059    public String getType()
060    {
061        return "webdav-project-resource";
062    }
063    
064    // Override to single encoded instead of twice
065    @Override
066    protected String _resolve(String uri, boolean download, boolean absolute, boolean internal, String prefix, String suffix)
067    {
068        String version = null;
069        String path;
070        AmetysObject resource = null;
071        try
072        {
073            String resourceId = uri;
074            int i = uri.indexOf(";");
075            if (i != -1)
076            {
077                resourceId = uri.substring(0, i);
078                version = uri.substring(i + 1);
079            }
080            
081            resource = _resolver.resolveById(resourceId);
082            path = getPath(resource);
083            if (StringUtils.isBlank(path))
084            {
085                return "";
086            }
087        }
088        catch (UnknownAmetysObjectException e)
089        {
090            getLogger().warn("Link to unexisting resource " + uri);
091            return "";
092        }
093        
094        int i = path.lastIndexOf(".");
095        String extension = i != -1 ? path.substring(i) : null;
096        path = i != -1 ? path.substring(0, i) : path; 
097        
098        StringBuilder result = new StringBuilder();
099        
100        result.append(getUriPrefix(resource, download, absolute, internal));
101
102        String realPrefix = null;
103        if (resource instanceof Resource)
104        {
105            realPrefix = getRealPrefix((Resource) resource, prefix);
106        }
107
108        if (StringUtils.isNotEmpty(realPrefix))
109        {
110            result.append("/_").append(realPrefix);
111        }
112        
113        if (StringUtils.isNotEmpty(version) && resource instanceof VersionableAmetysObject)
114        {
115            result.append("/__version").append(version);
116        }
117        
118        result.append(path);
119        
120        if (suffix != null)
121        {
122            result.append(suffix);
123        }
124        
125        if (extension != null)
126        {
127            result.append(extension);
128        }
129        
130        String fullUri = result.toString();
131        
132        // the ';' char is handled by Tomcat as an URI path parameter
133        // Replace it by its encoded value, which may look weird in Office, but at least it won't fail
134        fullUri = fullUri.replaceAll(";", "%3B");
135        
136        return URIUtils.encodeURI(fullUri, download ? Collections.singletonMap("download", "true") : null);
137    }
138    
139    @Override
140    protected String getUriPrefix(AmetysObject object, boolean download, boolean absolute, boolean internal)
141    {
142        Project project = null;
143        String projectName = null;
144        String siteName = null;
145
146        UserIdentity user = _currentUserProvider.getUser();
147        long duration = 12 * 60 * 60; // 12h
148        boolean autoRenew = true; // Auto Renew the token to be able to continue to edit it
149        
150        Set<String> contexts = new HashSet<>();
151        String path = getPath(object); // Token only usable on this resource
152        path = StringUtils.removeStart(path, "/"); // / at the beginning is removed because it is used in a uri path
153        contexts.add(path);
154        
155        if (object instanceof Resource)
156        {
157            String parentPath = getPath(object.getParent());
158            parentPath = StringUtils.removeStart(parentPath, "/").concat("/"); // / at the beginning is removed because it is used in a uri path, / at the end to avoid identical folders
159            contexts.add(parentPath);
160        }
161        String type = "Workspaces-Webdav";
162        String comment = "Token created to modify content : '" + object.getId() + "'";
163        String token = _authenticationTokenManager.generateToken(user, duration, autoRenew, null, contexts, type, comment);
164        
165        project = _getProject(object);
166        projectName = project.getName();
167
168        siteName = project.getSites().iterator().next().getName();
169        
170        if (internal)
171        {
172            return "cocoon://_workspaces/dav/" + projectName + "/" + token;
173        }
174        else if (absolute)
175        {
176            return _webPrefixHandler.getAbsoluteUriPrefix(siteName) + "/_workspaces/dav/" + projectName + "/" + token;
177        }
178        else
179        {
180            return _webPrefixHandler.getUriPrefix(siteName) + "/_workspaces/dav/" + projectName + "/" + token;
181        }
182    }
183    
184    /**
185     * Get the path of a {@link Resource} or a {@link ResourceCollection}
186     * @param resource the resource to work with
187     * @return the path
188     */
189    protected String getPath(AmetysObject resource)
190    {
191        String path = null;
192        if (resource instanceof Resource)
193        {
194            path = getResourcePath((Resource) resource);
195        }
196        else if (resource instanceof ResourceCollection)
197        {
198            path = ((ResourceCollection) resource).getExplorerPath();
199        }
200        else
201        {
202            getLogger().error("Trying to access to something that is not a Resource or a ResourceCollection : " + resource.getId());
203        }
204        return path;
205    }
206    
207    @Override
208    protected String getRealPrefix(Resource resource, String prefix)
209    {
210        return null;
211    }
212}