/* * eXist Open Source Native XML Database * Copyright (C) 2010 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.webdav; import org.apache.log4j.Logger; import com.bradmcevoy.http.Auth; import com.bradmcevoy.http.LockInfo; import com.bradmcevoy.http.LockTimeout; import com.bradmcevoy.http.LockToken; import com.bradmcevoy.http.Request; import com.bradmcevoy.http.Request.Method; import com.bradmcevoy.http.Resource; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.GregorianCalendar; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.XMLGregorianCalendar; import org.exist.security.User; import org.exist.storage.BrokerPool; import org.exist.xmldb.XmldbURI; import org.exist.security.SecurityManager; /** * Generic class representing a Milton Resource. * * @author Dannes Wessels <dannes@exist-db.org> */ public class MiltonResource implements Resource { protected final static Logger LOG = Logger.getLogger(MiltonResource.class); protected XmldbURI resourceXmldbUri; protected BrokerPool brokerPool; protected String host; protected User user; protected final static String AUTHENTICATED = "AUTHENTICATED"; protected String REALM = "exist"; protected ExistResource existResource; // Used for Long to DateTime conversion private DatatypeFactory datatypeFactory; public MiltonResource() { if(datatypeFactory==null){ try { datatypeFactory = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException ex) { LOG.error(ex); } } } protected XmldbURI getXmldbUri() { return resourceXmldbUri; } protected String getHost() { return host; } private User getUserAsSubject() { return user; } /** * Convert date to dateTime XML format. * s * @param date Representation of data * @return ISO8601 like formatted representation of date.s */ protected String getXmlDateTime(Long date){ // Convert to Calendar GregorianCalendar gc = new GregorianCalendar(); gc.setTime( new Date(date) ); // COnvert to XML dateTimes XMLGregorianCalendar xgc = datatypeFactory.newXMLGregorianCalendar(gc); return xgc.toXMLFormat(); } /** * Converts an org.exist.dom.LockToken into com.bradmcevoy.http.LockToken. * * @param existLT Exist-db representation of a webdav token. * @return Milton representation of a webdav token. */ protected LockToken convertToken(org.exist.dom.LockToken existLT) { // LockInfo : construct scope LockInfo.LockScope scope = null; switch (existLT.getScope()) { case org.exist.dom.LockToken.LOCK_SCOPE_SHARED: scope = LockInfo.LockScope.SHARED; break; case org.exist.dom.LockToken.LOCK_SCOPE_EXCLUSIVE: scope = LockInfo.LockScope.EXCLUSIVE; break; default: scope = LockInfo.LockScope.NONE; break; } // LockInfo : construct type LockInfo.LockType type = null; switch (existLT.getType()) { case org.exist.dom.LockToken.LOCK_TYPE_WRITE: type = LockInfo.LockType.WRITE; break; default: // DWES: if not WRITE then READ. typical :-) type = LockInfo.LockType.READ; break; } // LockInfo : get owner String owner = existLT.getOwner(); // LockInfo : construct depth LockInfo.LockDepth depth = null; switch (existLT.getDepth()) { case org.exist.dom.LockToken.LOCK_DEPTH_INFINIY: depth = LockInfo.LockDepth.INFINITY; break; default: // TODO either zero or infinity? depth = LockInfo.LockDepth.ZERO; break; } // LockInfo LockInfo li = new LockInfo(scope, type, owner, depth); // Lock Timeout Long timeout = existLT.getTimeOut(); // Special treatment when no LOCK was present if(timeout == org.exist.dom.LockToken.NO_LOCK_TIMEOUT){ timeout=null; // Special treatment infinite lock } else if(timeout == org.exist.dom.LockToken.LOCK_TIMEOUT_INFINITE){ timeout=Long.MAX_VALUE; } LockTimeout lt = new LockTimeout(timeout); // Token Id String id = existLT.getOpaqueLockToken(); // Return values in Milton object return new LockToken(id, li, lt); } /** * Converts an org.exist.dom.LockToken into com.bradmcevoy.http.LockToken. */ protected org.exist.dom.LockToken convertToken(LockTimeout timeout, LockInfo lockInfo) { org.exist.dom.LockToken existToken = new org.exist.dom.LockToken(); // Set lock depth switch (lockInfo.depth) { case ZERO: existToken.setDepth(org.exist.dom.LockToken.LOCK_DEPTH_0); break; case INFINITY: existToken.setDepth(org.exist.dom.LockToken.LOCK_DEPTH_INFINIY); break; } // Set lock scope switch (lockInfo.scope) { case EXCLUSIVE: existToken.setScope(org.exist.dom.LockToken.LOCK_SCOPE_EXCLUSIVE); break; case SHARED: existToken.setScope(org.exist.dom.LockToken.LOCK_SCOPE_SHARED); break; case NONE: existToken.setScope(org.exist.dom.LockToken.LOCK_SCOPE_NONE); break; } // Set lock type (read,write) switch (lockInfo.type) { case READ: existToken.setScope(org.exist.dom.LockToken.LOCK_TYPE_NONE); break; case WRITE: existToken.setScope(org.exist.dom.LockToken.LOCK_TYPE_WRITE); break; } // Set timeouts if (timeout == null || timeout.getSeconds() == null) { existToken.setTimeOut(existToken.NO_LOCK_TIMEOUT); } else if (timeout.getSeconds() == Long.MAX_VALUE ) { existToken.setTimeOut(org.exist.dom.LockToken.LOCK_TIMEOUT_INFINITE); } else { Long futureDate = (new Date().getTime())/1000 + timeout.getSeconds(); existToken.setTimeOut( futureDate ); } // Copy username if existent String user = lockInfo.lockedByUser; if(user != null){ existToken.setOwner(user); } return existToken; } /** * Convert % encoded string back to text */ protected XmldbURI decodePath(XmldbURI uri){ XmldbURI retval =null; try { String path = new URI(uri.toString()).getPath(); retval = XmldbURI.xmldbUriFor(""+path, false); } catch (URISyntaxException ex){ // oops LOG.error(ex.getMessage()); } return retval; } /** * Convert % encoded string back to text */ protected String decodePath(String uri){ String path =null; try { path = new URI(uri).getPath(); } catch (URISyntaxException ex) { // oops LOG.error(ex.getMessage()); } return path; } /* ======== * Resource * ======== */ //@Override public String getUniqueId() { return null; // disables the ETag field } //@Override public String getName() { return decodePath(""+resourceXmldbUri.lastSegment()); } //@Override public Object authenticate(String username, String password) { if(LOG.isDebugEnabled()) LOG.debug("Authenticating user " + username + " for " + resourceXmldbUri); // Check if username is provided. if (username == null) { return null; } // Check is user was already authenticated. if (user != null) { if(LOG.isDebugEnabled()) LOG.debug("User was already authenticated."); return AUTHENTICATED; } // Authenticate user with password user = existResource.authenticate(username, password); // Quick return if no user object was returned if (user == null) { if(LOG.isDebugEnabled()) LOG.debug("User could not be authenticated."); return null; } // Guest is not allowed to access. if (username.equals(SecurityManager.GUEST_USER)) { LOG.error("The user " + username + " is prohibited from logging in through WebDAV."); return null; } // Note: If User object is returned, authentication was OK // Collect data for this resource existResource.initMetadata(); if(LOG.isDebugEnabled()) LOG.debug("User '" + user.getName() + "' has been authenticated."); return AUTHENTICATED; } //@Override public boolean authorise(Request request, Method method, Auth auth) { LOG.info(method.toString() + " " + resourceXmldbUri + " (write="+ method.isWrite+")"); /* * First perform checks on Milton authentication */ if (auth == null) { if(LOG.isDebugEnabled()) LOG.debug("User hasn't been authenticated."); return false; } // Get effective username String userName = auth.getUser(); // Get authentication object Object tag = auth.getTag(); // Get URI. no idea why value is null. String authURI = auth.getUri(); // If object does not exist, there was no successfull authentication if (tag == null) { if(LOG.isDebugEnabled()) LOG.debug("No tag, user " + userName + " not authenticated"); return false; } else if (tag instanceof String) { String value = (String) tag; if (AUTHENTICATED.equals(value)) { // The correct TAG is returned! } else { if(LOG.isDebugEnabled()) LOG.debug("Authentication tag contains wrong value, user " + userName + " is not authenticated"); return false; } } /* * Second perform checks on actual exist-db permissions */ if (method.isWrite) { if (!existResource.writeAllowed) { if(LOG.isDebugEnabled()) LOG.debug("User " + userName + " is NOT authorized to write resource, abort."); return false; } } else { if (!existResource.readAllowed) { if(LOG.isDebugEnabled()) LOG.debug("User " + userName + " is NOT authorized to read resource, abort."); return false; } } if(auth.getUri()==null){ if(LOG.isTraceEnabled()) LOG.trace("URI is null"); // not sure why the null value can be there } String action = method.isWrite ? "write" : "read"; if(LOG.isDebugEnabled()) LOG.debug("User " + userName + " is authorized to " + action + " resource " + resourceXmldbUri.toString()); return true; } //@Override public String getRealm() { return REALM; } //@Override public Date getModifiedDate() { Date modifiedDate = null; Long time = existResource.getLastModified(); if (time != null) { modifiedDate = new Date(time); } // if(LOG.isDebugEnabled()) // LOG.debug("Modified date=" + modifiedDate); return modifiedDate; } //@Override public String checkRedirect(Request request) { return null; } }