/*******************************************************************************
* Copyright (c) 2015 IBH SYSTEMS GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBH SYSTEMS GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.packagedrone.repo.channel.servlet;
import static org.eclipse.packagedrone.web.util.BasicAuthentication.parseAuthorization;
import java.io.IOException;
import java.util.Collections;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.packagedrone.repo.channel.ChannelService;
import org.eclipse.packagedrone.repo.channel.ChannelService.By;
import org.eclipse.packagedrone.web.util.BasicAuthentication;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is an abstract implementation for implementing servlets which require
* the {@link StorageService}.
* <p>
* The servlet ensures that the service methods (GET, POST, ... ) only get
* called when there is a storage service present. The service can then be
* fetched using {@link #getService(HttpServletRequest)}.
* </p<
*/
public abstract class AbstractChannelServiceServlet extends HttpServlet
{
private final static Logger logger = LoggerFactory.getLogger ( AbstractChannelServiceServlet.class );
private static final long serialVersionUID = 1L;
private static final String ATTR_CHANNEL_SERVICE = AbstractChannelServiceServlet.class.getName () + ".channelService";
private ServiceTracker<ChannelService, ChannelService> tracker;
public AbstractChannelServiceServlet ()
{
super ();
}
@Override
public void init () throws ServletException
{
super.init ();
final BundleContext context = FrameworkUtil.getBundle ( getClass () ).getBundleContext ();
this.tracker = new ServiceTracker<> ( context, ChannelService.class, null );
this.tracker.open ();
}
protected ChannelService getService ( final HttpServletRequest request )
{
return (ChannelService)request.getAttribute ( ATTR_CHANNEL_SERVICE );
}
@Override
protected void service ( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException, IOException
{
logger.trace ( "Request: {} / {}", request.getMethod (), request.getPathInfo () );
final ChannelService service = this.tracker.getService ();
if ( service == null )
{
handleNoService ( request, response );
}
else
{
request.setAttribute ( ATTR_CHANNEL_SERVICE, service );
super.service ( request, response );
}
}
protected void handleNoService ( final HttpServletRequest request, final HttpServletResponse response ) throws IOException
{
response.setStatus ( HttpServletResponse.SC_SERVICE_UNAVAILABLE );
response.setContentType ( "text/plain" );
response.getWriter ().write ( "Channel service unavailable" );
}
@Override
public void destroy ()
{
this.tracker.close ();
super.destroy ();
}
/**
* Authenticate the request is authenticated against the deploy keys
* <p>
* If the request could not be authenticated a basic authentication request
* is sent back and the {@link HttpServletResponse} will be committed.
* </p>
*
* @param by
* the channel locator
* @param request
* the request
* @param response
* the response
* @return <code>true</code> if the request was not authenticated and the
* response got committed
* @throws IOException
* in case on a IO error
*/
protected boolean authenticate ( final By by, final HttpServletRequest request, final HttpServletResponse response ) throws IOException
{
if ( isAuthenticated ( by, request ) )
{
return true;
}
BasicAuthentication.request ( response, "channel", "Please authenticate" );
return false;
}
/**
* Simply test if the request is authenticated against the channels deploy
* keys
*
* @param by
* the channel locator
* @param request
* the request
* @return <code>true</code> if the request could be authenticated against
* the channels deploy keys, <code>false</code> otherwise
*/
protected boolean isAuthenticated ( final By by, final HttpServletRequest request )
{
final String[] authToks = parseAuthorization ( request );
if ( authToks == null )
{
return false;
}
if ( !authToks[0].equals ( "deploy" ) )
{
return false;
}
final String deployKey = authToks[1];
logger.debug ( "Deploy key: '{}'", deployKey );
final ChannelService service = getService ( request );
if ( service == null )
{
logger.info ( "Called 'isAuthenticated' without service" );
return false;
}
return service.getChannelDeployKeyStrings ( by ).orElse ( Collections.emptySet () ).contains ( deployKey );
}
}