/** * Copyright (c) 2011-2012, Thilo Planz. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package v7db.files.milton; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.substringAfter; import static org.apache.commons.lang3.StringUtils.substringAfterLast; import java.util.Properties; import org.slf4j.MDC; import v7db.auth.AuthenticationProvider; import v7db.auth.AuthenticationProviderFactory; import v7db.auth.AuthenticationToken; import v7db.auth.MongoAuthenticationProvider; import v7db.files.AuthorisationProvider; import v7db.files.AuthorisationProviderFactory; import v7db.files.Configuration; import v7db.files.mongodb.V7File; import v7db.files.mongodb.V7GridFS; import com.bradmcevoy.http.ApplicationConfig; import com.bradmcevoy.http.Auth; import com.bradmcevoy.http.HttpManager; import com.bradmcevoy.http.Initable; import com.bradmcevoy.http.MiltonServlet; import com.bradmcevoy.http.Request; import com.bradmcevoy.http.Resource; import com.bradmcevoy.http.Request.Method; import com.mongodb.Mongo; class ResourceFactory implements com.bradmcevoy.http.ResourceFactory, Initable { private Mongo mongo; private V7GridFS fs; private String ROOT; private String endpoint; private String endpointName; private Properties endpointProperties; private AuthenticationProvider authentication; private AuthorisationProvider authorisation; private boolean fakeLocking = false; private final String dbName; ResourceFactory(String dbName) { this.dbName = dbName; } public void init(ApplicationConfig config, HttpManager manager) { try { endpoint = config.getInitParameter("webdav.endpoint"); endpointName = defaultIfBlank(substringAfterLast(endpoint, "/"), "/"); mongo = Configuration.getMongo(); endpointProperties = new Properties(Configuration .getEndpointProperties(endpoint)); // need to adjust mongo.db in case of multi-tenant mode endpointProperties.put("mongo.db", dbName); fs = new V7GridFS(mongo.getDB(dbName)); ROOT = getProperty("root"); if (ROOT == null) ROOT = endpoint; authentication = getAuthenticationProvider(); authorisation = AuthorisationProviderFactory .getAuthorisationProvider(endpointProperties); fakeLocking = "fake".equals(getProperty("locking.provider")); } catch (Exception e) { throw new RuntimeException(e); } } private AuthenticationProvider getAuthenticationProvider() { String p = endpointProperties.getProperty("auth.provider"); if ("mongo".equals(p)) { return new MongoAuthenticationProvider(mongo, endpointProperties); } return AuthenticationProviderFactory .getAuthenticationProvider(endpointProperties); } public Resource getResource(String host, String _path) { String servletPath = MiltonServlet.request().getServletPath(); String path = _path.equals(servletPath) ? "/" : substringAfter(_path, servletPath); if (!path.startsWith("/")) throw new IllegalArgumentException("path: " + _path + " servletPath: " + servletPath); if ("/".equals(path)) { return fakeLocking ? new LockableFolderResource(endpointName, fs .getFile(ROOT), this) : new FolderResource(endpointName, fs .getFile(ROOT), this); } String[] p = path.split("/"); p[0] = ROOT; V7File f = fs.getFile(p); if (f == null) return null; if (f.hasContent()) return fakeLocking ? new LockableFileResource(f, this) : new FileResource(f, this); return fakeLocking ? new LockableFolderResource(f, this) : new FolderResource(f, this); } public void destroy(HttpManager manager) { if (mongo != null) mongo.close(); } String getProperty(String name) { return endpointProperties.getProperty(name); } String getRealm() { return getProperty("auth.realm"); } boolean authorise(V7File file, Request request, Method method, Auth auth) { AuthenticationToken tag = auth == null ? null : (AuthenticationToken) auth.getTag(); switch (method) { case GET: case PROPFIND: return authorisation.authoriseRead(file, tag); case POST: case PUT: case MKCOL: case DELETE: case LOCK: return authorisation.authoriseWrite(file, tag); case UNLOCK: // unlock only works if you have the lock token, so no extra // authorisation required return true; case MOVE: // TODO: also need to check write permissions in the target // directory return authorisation.authoriseWrite(file, tag); case COPY: // TODO: also need to check write permissions in the target // directory return authorisation.authoriseRead(file, tag); default: System.err.println("acl not implemented for " + method + " on " + file.getName()); return false; } } AuthenticationToken authenticate(String user, String password) { // Cyberduck does BasicAuth with "anonymous" // not sure if that is good, but here we go ... // we cannot return null because that would "fail" the anonymous login if ("anonymous".equals(user)) return AuthenticationToken.ANONYMOUS; if (authentication == null) return null; AuthenticationToken auth = authentication.authenticate(user, password); if (auth != null) MDC.put("user", auth.getUsername()); return auth; } }