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