package org.apache.archiva.webdav; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.archiva.admin.model.RepositoryAdminException; import org.apache.archiva.admin.model.beans.ManagedRepository; import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; import org.apache.archiva.configuration.ArchivaConfiguration; import org.apache.archiva.configuration.ConfigurationEvent; import org.apache.archiva.configuration.ConfigurationListener; import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator; import org.apache.archiva.security.ServletAuthenticator; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.DavLocatorFactory; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavResource; import org.apache.jackrabbit.webdav.DavResourceFactory; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.DavSessionProvider; import org.apache.jackrabbit.webdav.WebdavRequest; import org.apache.jackrabbit.webdav.WebdavRequestImpl; import org.apache.jackrabbit.webdav.WebdavResponse; import org.apache.jackrabbit.webdav.WebdavResponseImpl; import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.Map; /** * RepositoryServlet */ public class RepositoryServlet extends AbstractWebdavServlet implements ConfigurationListener { private Logger log = LoggerFactory.getLogger( RepositoryServlet.class ); private ArchivaConfiguration configuration; private ManagedRepositoryAdmin managedRepositoryAdmin; private Map<String, ManagedRepository> repositoryMap; private DavLocatorFactory locatorFactory; private DavResourceFactory resourceFactory; private DavSessionProvider sessionProvider; private final Object reloadLock = new Object(); @Override public void init( ServletConfig servletConfig ) throws ServletException { super.init( servletConfig ); try { initServers( servletConfig ); } catch ( RepositoryAdminException e ) { log.error( e.getMessage(), e ); throw new ServletException( e.getMessage(), e ); } } /** * Service the given request. This method has been overridden and copy/pasted to allow better exception handling and * to support different realms * * @param request * @param response * @throws ServletException * @throws java.io.IOException */ @Override protected void service( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { WebdavRequest webdavRequest = new WebdavRequestImpl( request, getLocatorFactory() ); // DeltaV requires 'Cache-Control' header for all methods except 'VERSION-CONTROL' and 'REPORT'. int methodCode = DavMethods.getMethodCode( request.getMethod() ); boolean noCache = DavMethods.isDeltaVMethod( webdavRequest ) && !( DavMethods.DAV_VERSION_CONTROL == methodCode || DavMethods.DAV_REPORT == methodCode ); WebdavResponse webdavResponse = new WebdavResponseImpl( response, noCache ); DavResource resource = null; try { // make sure there is a authenticated user if ( !getDavSessionProvider().attachSession( webdavRequest ) ) { return; } // check matching if=header for lock-token relevant operations resource = getResourceFactory().createResource( webdavRequest.getRequestLocator(), webdavRequest, webdavResponse ); if ( !isPreconditionValid( webdavRequest, resource ) ) { webdavResponse.sendError( DavServletResponse.SC_PRECONDITION_FAILED ); return; } if ( !execute( webdavRequest, webdavResponse, methodCode, resource ) ) { super.service( request, response ); } } catch ( UnauthorizedDavException e ) { webdavResponse.setHeader( "WWW-Authenticate", getAuthenticateHeaderValue( e.getRepositoryName() ) ); webdavResponse.sendError( e.getErrorCode(), e.getStatusPhrase() ); } catch ( BrowserRedirectException e ) { response.sendRedirect( e.getLocation() ); } catch ( DavException e ) { if ( e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED ) { final String msg = "Should throw " + UnauthorizedDavException.class.getName(); log.error( msg ); webdavResponse.sendError( e.getErrorCode(), msg ); } else if ( e.getCause() != null ) { webdavResponse.sendError( e.getErrorCode(), e.getCause().getMessage() ); } else { webdavResponse.sendError( e.getErrorCode(), e.getMessage() ); } } finally { getDavSessionProvider().releaseSession( webdavRequest ); } } public synchronized void initServers( ServletConfig servletConfig ) throws RepositoryAdminException { long start = System.currentTimeMillis(); WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() ); configuration = wac.getBean( "archivaConfiguration#default", ArchivaConfiguration.class ); configuration.addListener( this ); managedRepositoryAdmin = wac.getBean( ManagedRepositoryAdmin.class ); repositoryMap = managedRepositoryAdmin.getManagedRepositoriesAsMap(); for ( ManagedRepository repo : repositoryMap.values() ) { File repoDir = new File( repo.getLocation() ); if ( !repoDir.exists() ) { if ( !repoDir.mkdirs() ) { // Skip invalid directories. log.info( "Unable to create missing directory for {}", repo.getLocation() ); continue; } } } resourceFactory = wac.getBean( "davResourceFactory#archiva", DavResourceFactory.class ); locatorFactory = new ArchivaDavLocatorFactory(); ServletAuthenticator servletAuth = wac.getBean( ServletAuthenticator.class ); HttpAuthenticator httpAuth = wac.getBean( "httpAuthenticator#basic", HttpAuthenticator.class ); sessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth ); long end = System.currentTimeMillis(); log.info( "initServers done in {} ms", (end - start) ); } @Override public void configurationEvent( ConfigurationEvent event ) { if ( event.getType() == ConfigurationEvent.SAVED ) { try { initRepositories(); } catch ( RepositoryAdminException e ) { log.error( e.getMessage(), e ); throw new RuntimeException( e.getMessage(), e ); } } } private void initRepositories() throws RepositoryAdminException { synchronized ( repositoryMap ) { repositoryMap.clear(); repositoryMap.putAll( managedRepositoryAdmin.getManagedRepositoriesAsMap() ); } synchronized ( reloadLock ) { initServers( getServletConfig() ); } } public synchronized ManagedRepository getRepository( String prefix ) throws RepositoryAdminException { if ( repositoryMap.isEmpty() ) { repositoryMap.putAll( managedRepositoryAdmin.getManagedRepositoriesAsMap() ); } return repositoryMap.get( prefix ); } ArchivaConfiguration getConfiguration() { return configuration; } @Override protected boolean isPreconditionValid( final WebdavRequest request, final DavResource davResource ) { // check for read or write access to the resource when resource-based permission is implemented return true; } @Override public DavSessionProvider getDavSessionProvider() { return sessionProvider; } @Override public void setDavSessionProvider( final DavSessionProvider davSessionProvider ) { this.sessionProvider = davSessionProvider; } @Override public DavLocatorFactory getLocatorFactory() { return locatorFactory; } @Override public void setLocatorFactory( final DavLocatorFactory davLocatorFactory ) { locatorFactory = davLocatorFactory; } @Override public DavResourceFactory getResourceFactory() { return resourceFactory; } @Override public void setResourceFactory( final DavResourceFactory davResourceFactory ) { resourceFactory = davResourceFactory; } @Override public String getAuthenticateHeaderValue() { throw new UnsupportedOperationException(); } public String getAuthenticateHeaderValue( String repository ) { return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\""; } @Override public void destroy() { configuration.removeListener( this ); resourceFactory = null; configuration = null; locatorFactory = null; sessionProvider = null; repositoryMap.clear(); repositoryMap = null; WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( getServletContext() ); if ( wac instanceof ConfigurableApplicationContext ) { ( (ConfigurableApplicationContext) wac ).close(); } super.destroy(); } }