org.jolokia.jvmagent.security.DelegatingAuthenticator Maven / Gradle / Ivy
package org.jolokia.jvmagent.security;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Stack;
import javax.net.ssl.*;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.*;
import org.jolokia.util.EscapeUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
* Authenticator using JAAS for logging in with user and password for the given realm.
*
* @author roland
* @since 26.05.14
*/
public class DelegatingAuthenticator extends Authenticator {
private final URL delegateURL;
private final PrincipalExtractor principalExtractor;
private final String realm;
public DelegatingAuthenticator(String pRealm, String pUrl, String pPrincipalSpec, boolean pDisableCertCheck) {
this.realm = pRealm;
try {
this.delegateURL = new URL(pUrl);
this.principalExtractor = createPrincipalExtractor(pPrincipalSpec);
// REMARK : This might be done on a per-connection basis not globally for everyone
if (pDisableCertCheck) {
disableSSLCertificateChecking();
}
} catch (MalformedURLException exp) {
throw new IllegalArgumentException("Invalid delegation url '" + pUrl + "' given: " + exp,exp);
}
}
@Override
public Result authenticate(HttpExchange pHttpExchange) {
try {
URLConnection connection = delegateURL.openConnection();
connection.addRequestProperty("Authorization",
pHttpExchange.getRequestHeaders().getFirst("Authorization"));
connection.setConnectTimeout(2000);
connection.connect();
if (connection instanceof HttpURLConnection) {
HttpURLConnection httpConnection = (HttpURLConnection) connection;
return httpConnection.getResponseCode() == 200 ?
new Success(principalExtractor.extract(connection)) :
new Failure(401);
} else {
return new Failure(401);
}
} catch (final IOException e) {
return prepareFailure(pHttpExchange, "Cannot call delegate url " + delegateURL + ": " + e, 503);
} catch (final IllegalArgumentException e) {
return prepareFailure(pHttpExchange, "Illegal Argument: " + e, 400);
} catch (ParseException e) {
return prepareFailure(pHttpExchange, "Invalid JSON response: " + e, 422);
}
}
private Result prepareFailure(HttpExchange pHttpExchange, String pErrorDetails, int pCode) {
pHttpExchange.getResponseHeaders().add("X-Error-Details", pErrorDetails);
return new Failure(pCode);
}
private PrincipalExtractor createPrincipalExtractor(String pPrincipalExtractorSpec) {
if (pPrincipalExtractorSpec == null || pPrincipalExtractorSpec.startsWith("empty:")) {
return new EmptyPrincipalExtractor();
} else if (pPrincipalExtractorSpec.startsWith("json:")) {
return new JsonPathExtractor(pPrincipalExtractorSpec.substring("json:".length()));
} else {
throw new IllegalArgumentException("No principal extractor found for spec '" + pPrincipalExtractorSpec + "'");
}
}
// =======================================================================================
private interface PrincipalExtractor {
HttpPrincipal extract(URLConnection connection) throws IOException, ParseException;
}
// Extract principal from a JSON object
private class JsonPathExtractor implements PrincipalExtractor {
private String path;
public JsonPathExtractor(String pPath) {
path = pPath;
}
@Override
public HttpPrincipal extract(URLConnection connection) throws IOException, ParseException {
Object payload = new JSONParser().parse(new InputStreamReader(connection.getInputStream()));
Stack pathElements = EscapeUtil.extractElementsFromPath(path);
Object result = payload;
while (!pathElements.isEmpty()) {
if (result == null) {
throw new IllegalArgumentException("No path '" + path + "' found in " + payload.toString());
}
String key = pathElements.pop();
result = extractValue(result, key);
}
return new HttpPrincipal(result.toString(),realm);
}
private Object extractValue(Object payload, String key) {
if (payload instanceof JSONObject) {
return ((JSONObject) payload).get(key);
} else if (payload instanceof JSONArray) {
return ((JSONArray) payload).get(Integer.parseInt(key));
} else {
return null;
}
}
}
private class EmptyPrincipalExtractor implements PrincipalExtractor {
public HttpPrincipal extract(URLConnection connection) {
return new HttpPrincipal("",realm);
}
}
// ============================================================================================
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (KeyManagementException e) {
throw new IllegalArgumentException("Disabling SSL certificate failed: " + e,e);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Disabling SSL certificate failed: " + e,e);
}
}
}