001 package org.apache.archiva.webdav; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.archiva.admin.model.RepositoryAdminException; 023 import org.apache.archiva.admin.model.beans.ManagedRepository; 024 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; 025 import org.apache.archiva.configuration.ArchivaConfiguration; 026 import org.apache.archiva.configuration.ConfigurationEvent; 027 import org.apache.archiva.configuration.ConfigurationListener; 028 import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator; 029 import org.apache.archiva.security.ServletAuthenticator; 030 import org.apache.jackrabbit.webdav.DavException; 031 import org.apache.jackrabbit.webdav.DavLocatorFactory; 032 import org.apache.jackrabbit.webdav.DavMethods; 033 import org.apache.jackrabbit.webdav.DavResource; 034 import org.apache.jackrabbit.webdav.DavResourceFactory; 035 import org.apache.jackrabbit.webdav.DavServletResponse; 036 import org.apache.jackrabbit.webdav.DavSessionProvider; 037 import org.apache.jackrabbit.webdav.WebdavRequest; 038 import org.apache.jackrabbit.webdav.WebdavRequestImpl; 039 import org.apache.jackrabbit.webdav.WebdavResponse; 040 import org.apache.jackrabbit.webdav.WebdavResponseImpl; 041 import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet; 042 import org.slf4j.Logger; 043 import org.slf4j.LoggerFactory; 044 import org.springframework.context.ConfigurableApplicationContext; 045 import org.springframework.web.context.WebApplicationContext; 046 import org.springframework.web.context.support.WebApplicationContextUtils; 047 048 import javax.servlet.ServletConfig; 049 import javax.servlet.ServletException; 050 import javax.servlet.http.HttpServletRequest; 051 import javax.servlet.http.HttpServletResponse; 052 import java.io.File; 053 import java.io.IOException; 054 import java.util.Map; 055 056 /** 057 * RepositoryServlet 058 */ 059 public class RepositoryServlet 060 extends AbstractWebdavServlet 061 implements ConfigurationListener 062 { 063 private Logger log = LoggerFactory.getLogger( RepositoryServlet.class ); 064 065 private ArchivaConfiguration configuration; 066 067 private ManagedRepositoryAdmin managedRepositoryAdmin; 068 069 private Map<String, ManagedRepository> repositoryMap; 070 071 private DavLocatorFactory locatorFactory; 072 073 private DavResourceFactory resourceFactory; 074 075 private DavSessionProvider sessionProvider; 076 077 private final Object reloadLock = new Object(); 078 079 public void init( ServletConfig servletConfig ) 080 throws ServletException 081 { 082 super.init( servletConfig ); 083 try 084 { 085 initServers( servletConfig ); 086 } 087 catch ( RepositoryAdminException e ) 088 { 089 log.error( e.getMessage(), e ); 090 throw new ServletException( e.getMessage(), e ); 091 } 092 } 093 094 /** 095 * Service the given request. This method has been overridden and copy/pasted to allow better exception handling and 096 * to support different realms 097 * 098 * @param request 099 * @param response 100 * @throws ServletException 101 * @throws java.io.IOException 102 */ 103 @Override 104 protected void service( HttpServletRequest request, HttpServletResponse response ) 105 throws ServletException, IOException 106 { 107 WebdavRequest webdavRequest = new WebdavRequestImpl( request, getLocatorFactory() ); 108 // DeltaV requires 'Cache-Control' header for all methods except 'VERSION-CONTROL' and 'REPORT'. 109 int methodCode = DavMethods.getMethodCode( request.getMethod() ); 110 boolean noCache = DavMethods.isDeltaVMethod( webdavRequest ) && !( DavMethods.DAV_VERSION_CONTROL == methodCode 111 || DavMethods.DAV_REPORT == methodCode ); 112 WebdavResponse webdavResponse = new WebdavResponseImpl( response, noCache ); 113 DavResource resource = null; 114 115 try 116 { 117 // make sure there is a authenticated user 118 if ( !getDavSessionProvider().attachSession( webdavRequest ) ) 119 { 120 return; 121 } 122 123 // check matching if=header for lock-token relevant operations 124 resource = 125 getResourceFactory().createResource( webdavRequest.getRequestLocator(), webdavRequest, webdavResponse ); 126 127 if ( !isPreconditionValid( webdavRequest, resource ) ) 128 { 129 webdavResponse.sendError( DavServletResponse.SC_PRECONDITION_FAILED ); 130 return; 131 } 132 if ( !execute( webdavRequest, webdavResponse, methodCode, resource ) ) 133 { 134 super.service( request, response ); 135 } 136 137 } 138 catch ( UnauthorizedDavException e ) 139 { 140 webdavResponse.setHeader( "WWW-Authenticate", getAuthenticateHeaderValue( e.getRepositoryName() ) ); 141 webdavResponse.sendError( e.getErrorCode(), e.getStatusPhrase() ); 142 } 143 catch ( BrowserRedirectException e ) 144 { 145 response.sendRedirect( e.getLocation() ); 146 } 147 catch ( DavException e ) 148 { 149 if ( e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED ) 150 { 151 final String msg = "Should throw " + UnauthorizedDavException.class.getName(); 152 log.error( msg ); 153 webdavResponse.sendError( e.getErrorCode(), msg ); 154 } 155 else if ( e.getCause() != null ) 156 { 157 webdavResponse.sendError( e.getErrorCode(), e.getCause().getMessage() ); 158 } 159 else 160 { 161 webdavResponse.sendError( e.getErrorCode(), e.getMessage() ); 162 } 163 } 164 finally 165 { 166 getDavSessionProvider().releaseSession( webdavRequest ); 167 } 168 } 169 170 public synchronized void initServers( ServletConfig servletConfig ) 171 throws RepositoryAdminException 172 { 173 174 long start = System.currentTimeMillis(); 175 176 WebApplicationContext wac = 177 WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() ); 178 179 configuration = wac.getBean( "archivaConfiguration#default", ArchivaConfiguration.class ); 180 configuration.addListener( this ); 181 182 managedRepositoryAdmin = wac.getBean( ManagedRepositoryAdmin.class ); 183 184 repositoryMap = managedRepositoryAdmin.getManagedRepositoriesAsMap(); 185 186 for ( ManagedRepository repo : repositoryMap.values() ) 187 { 188 File repoDir = new File( repo.getLocation() ); 189 190 if ( !repoDir.exists() ) 191 { 192 if ( !repoDir.mkdirs() ) 193 { 194 // Skip invalid directories. 195 log.info( "Unable to create missing directory for {}", repo.getLocation() ); 196 continue; 197 } 198 } 199 } 200 201 resourceFactory = wac.getBean( "davResourceFactory#archiva", DavResourceFactory.class ); 202 locatorFactory = new ArchivaDavLocatorFactory(); 203 204 ServletAuthenticator servletAuth = wac.getBean( ServletAuthenticator.class ); 205 HttpAuthenticator httpAuth = wac.getBean( "httpAuthenticator#basic", HttpAuthenticator.class ); 206 207 sessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth ); 208 209 long end = System.currentTimeMillis(); 210 211 log.info( "initServers done in {} ms", (end - start) ); 212 } 213 214 public void configurationEvent( ConfigurationEvent event ) 215 { 216 if ( event.getType() == ConfigurationEvent.SAVED ) 217 { 218 try 219 { 220 initRepositories(); 221 } 222 catch ( RepositoryAdminException e ) 223 { 224 log.error( e.getMessage(), e ); 225 throw new RuntimeException( e.getMessage(), e ); 226 } 227 } 228 } 229 230 private void initRepositories() 231 throws RepositoryAdminException 232 { 233 synchronized ( repositoryMap ) 234 { 235 repositoryMap.clear(); 236 repositoryMap.putAll( managedRepositoryAdmin.getManagedRepositoriesAsMap() ); 237 } 238 239 synchronized ( reloadLock ) 240 { 241 initServers( getServletConfig() ); 242 } 243 } 244 245 public synchronized ManagedRepository getRepository( String prefix ) 246 throws RepositoryAdminException 247 { 248 if ( repositoryMap.isEmpty() ) 249 { 250 repositoryMap.putAll( managedRepositoryAdmin.getManagedRepositoriesAsMap() ); 251 } 252 return repositoryMap.get( prefix ); 253 } 254 255 ArchivaConfiguration getConfiguration() 256 { 257 return configuration; 258 } 259 260 protected boolean isPreconditionValid( final WebdavRequest request, final DavResource davResource ) 261 { 262 // check for read or write access to the resource when resource-based permission is implemented 263 264 return true; 265 } 266 267 public DavSessionProvider getDavSessionProvider() 268 { 269 return sessionProvider; 270 } 271 272 public void setDavSessionProvider( final DavSessionProvider davSessionProvider ) 273 { 274 this.sessionProvider = davSessionProvider; 275 } 276 277 public DavLocatorFactory getLocatorFactory() 278 { 279 return locatorFactory; 280 } 281 282 public void setLocatorFactory( final DavLocatorFactory davLocatorFactory ) 283 { 284 locatorFactory = davLocatorFactory; 285 } 286 287 public DavResourceFactory getResourceFactory() 288 { 289 return resourceFactory; 290 } 291 292 public void setResourceFactory( final DavResourceFactory davResourceFactory ) 293 { 294 resourceFactory = davResourceFactory; 295 } 296 297 public String getAuthenticateHeaderValue() 298 { 299 throw new UnsupportedOperationException(); 300 } 301 302 public String getAuthenticateHeaderValue( String repository ) 303 { 304 return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\""; 305 } 306 307 @Override 308 public void destroy() 309 { 310 configuration.removeListener( this ); 311 312 resourceFactory = null; 313 configuration = null; 314 locatorFactory = null; 315 sessionProvider = null; 316 repositoryMap.clear(); 317 repositoryMap = null; 318 319 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( getServletContext() ); 320 321 if ( wac instanceof ConfigurableApplicationContext ) 322 { 323 ( (ConfigurableApplicationContext) wac ).close(); 324 } 325 super.destroy(); 326 } 327 }