blob: 320f661c5fc68aeaa2b162ac58ce4082054b6f61 [file] [log] [blame]
/*
* 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.
*/
package org.apache.solr.security;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Timer;
import org.apache.http.HttpRequest;
import org.apache.http.protocol.HttpContext;
import org.apache.solr.core.SolrInfoBean;
import org.apache.solr.metrics.SolrMetricProducer;
import org.apache.solr.metrics.SolrMetricsContext;
import org.eclipse.jetty.client.api.Request;
/**
*
* @lucene.experimental
*/
public abstract class AuthenticationPlugin implements SolrInfoBean, SolrMetricProducer {
final public static String AUTHENTICATION_PLUGIN_PROP = "authenticationPlugin";
final public static String HTTP_HEADER_X_SOLR_AUTHDATA = "X-Solr-AuthData";
// Metrics
private Set<String> metricNames = ConcurrentHashMap.newKeySet();
protected SolrMetricsContext solrMetricsContext;
protected Meter numErrors = new Meter();
protected Counter requests = new Counter();
protected Timer requestTimes = new Timer();
protected Counter totalTime = new Counter();
protected Counter numAuthenticated = new Counter();
protected Counter numPassThrough = new Counter();
protected Counter numWrongCredentials = new Counter();
protected Counter numMissingCredentials = new Counter();
/**
* This is called upon loading up of a plugin, used for setting it up.
* @param pluginConfig Config parameters, possibly from a ZK source
*/
public abstract void init(Map<String, Object> pluginConfig);
/**
* This method attempts to authenticate the request. Upon a successful authentication, this
* must call the next filter in the filter chain and set the user principal of the request,
* or else, upon an error or an authentication failure, throw an exception.
*
* @param request the http request
* @param response the http response
* @param filterChain the servlet filter chain
* @return false if the request not be processed by Solr (not continue), i.e.
* the response and status code have already been sent.
* @throws Exception any exception thrown during the authentication, e.g. PrivilegedActionException
*/
//TODO redeclare params as HttpServletRequest & HttpServletResponse
public abstract boolean doAuthenticate(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws Exception;
/**
* This method is called by SolrDispatchFilter in order to initiate authentication.
* It does some standard metrics counting.
*/
public final boolean authenticate(ServletRequest request, ServletResponse response, FilterChain filterChain) throws Exception {
Timer.Context timer = requestTimes.time();
requests.inc();
try {
return doAuthenticate(request, response, filterChain);
} catch(Exception e) {
numErrors.mark();
throw e;
} finally {
long elapsed = timer.stop();
totalTime.inc(elapsed);
}
}
/**
* Override this method to intercept internode requests. This allows your authentication
* plugin to decide on per-request basis whether it should handle inter-node requests or
* delegate to {@link PKIAuthenticationPlugin}. Return true to indicate that your plugin
* did handle the request, or false to signal that PKI plugin should handle it. This method
* will be called by {@link PKIAuthenticationPlugin}'s interceptor.
*
* <p>
* If not overridden, this method will return true for plugins implementing {@link HttpClientBuilderPlugin}.
* This method can be overridden by subclasses e.g. to set HTTP headers, even if you don't use a clientBuilder.
* </p>
* @param httpRequest the httpRequest that is about to be sent to another internal Solr node
* @param httpContext the context of that request.
* @return true if this plugin handled authentication for the request, else false
*/
protected boolean interceptInternodeRequest(HttpRequest httpRequest, HttpContext httpContext) {
return this instanceof HttpClientBuilderPlugin;
}
/**
* Override this method to intercept internode requests. This allows your authentication
* plugin to decide on per-request basis whether it should handle inter-node requests or
* delegate to {@link PKIAuthenticationPlugin}. Return true to indicate that your plugin
* did handle the request, or false to signal that PKI plugin should handle it. This method
* will be called by {@link PKIAuthenticationPlugin}'s interceptor.
*
* <p>
* If not overridden, this method will return true for plugins implementing {@link HttpClientBuilderPlugin}.
* This method can be overridden by subclasses e.g. to set HTTP headers, even if you don't use a clientBuilder.
* </p>
* @param request the httpRequest that is about to be sent to another internal Solr node
* @return true if this plugin handled authentication for the request, else false
*/
protected boolean interceptInternodeRequest(Request request) {
return this instanceof HttpClientBuilderPlugin;
}
/**
* Cleanup any per request data
*/
public void closeRequest() {
}
@Override
public SolrMetricsContext getSolrMetricsContext() {
return solrMetricsContext;
}
@Override
public void initializeMetrics(SolrMetricsContext parentContext, String scope) {
this.solrMetricsContext = parentContext.getChildContext(this);
// Metrics
numErrors = this.solrMetricsContext.meter(this, "errors", getCategory().toString(), scope);
requests = this.solrMetricsContext.counter(this, "requests", getCategory().toString(), scope);
numAuthenticated = this.solrMetricsContext.counter(this, "authenticated",getCategory().toString(), scope);
numPassThrough = this.solrMetricsContext.counter(this, "passThrough", getCategory().toString(), scope);
numWrongCredentials = this.solrMetricsContext.counter(this, "failWrongCredentials",getCategory().toString(), scope);
numMissingCredentials = this.solrMetricsContext.counter(this, "failMissingCredentials",getCategory().toString(), scope);
requestTimes = this.solrMetricsContext.timer(this,"requestTimes", getCategory().toString(), scope);
totalTime = this.solrMetricsContext.counter(this,"totalTime", getCategory().toString(), scope);
}
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public String getDescription() {
return "Authentication Plugin " + this.getClass().getName();
}
@Override
public Category getCategory() {
return Category.SECURITY;
}
@Override
public Set<String> getMetricNames() {
return metricNames;
}
}