blob: 47e8fe214d170e99680a75dc9fe95cb0e11e26b9 [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed 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 adaptorlib;
import com.google.enterprise.secmgr.http.HttpClientInterface;
import com.google.enterprise.secmgr.modules.SamlClient;
import com.sun.net.httpserver.HttpExchange;
import org.opensaml.DefaultBootstrap;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.credential.Credential;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
/**
* A credentials gatherer that implements authentication by communicating with
* the GSA's security manager via SAML. This class only sends the initial
* request; the response is handled in {@link SamlAssertionConsumerHandler}.
*/
class AuthnHandler extends AbstractHandler {
/** Manager that handles keeping track of users attempting to authenticate. */
private final SessionManager<HttpExchange> sessionManager;
/**
* Http client implementation that {@code SamlClient} will use to send
* requests directly to the GSA, for resolving SAML artifacts.
*/
private final HttpClientInterface httpClient = new HttpClientAdapter();
/** Credentials to use to sign messages. */
private final Credential cred;
private final SamlMetadata metadata;
AuthnHandler(String fallbackHostname, Charset defaultEncoding,
SessionManager<HttpExchange> sessionManager,
String gsaHostname, String keyAlias, int localPort)
throws IOException {
super(fallbackHostname, defaultEncoding);
this.sessionManager = sessionManager;
cred = getCredential(keyAlias);
try {
DefaultBootstrap.bootstrap();
} catch (ConfigurationException ex) {
throw new RuntimeException(ex);
}
this.metadata = new SamlMetadata(fallbackHostname, localPort, gsaHostname);
}
@Override
public void meteredHandle(HttpExchange ex) throws IOException {
String requestMethod = ex.getRequestMethod();
if (!"GET".equals(requestMethod) && !"HEAD".equals(requestMethod)) {
cannedRespond(ex, HttpURLConnection.HTTP_BAD_METHOD, "text/plain",
"Unsupported request method");
return;
}
Session session = sessionManager.getSession(ex);
AuthnState authnState = (AuthnState) session.getAttribute(
AuthnState.SESSION_ATTR_NAME);
if (authnState == null) {
authnState = new AuthnState();
session.setAttribute(AuthnState.SESSION_ATTR_NAME, authnState);
}
SamlClient client =
new SamlClient(
metadata.getLocalEntity(),
metadata.getPeerEntity(),
"GSA Adaptor",
cred,
httpClient);
authnState.startAttempt(client, getRequestUri(ex));
client.sendAuthnRequest(
new adaptorlib.HttpExchangeOutTransportAdapter(ex, true));
}
/**
* Create a {@code Credential} usable by OpenSAML by accessing the default
* keystore. The key should have the same password as the keystore.
*/
private static Credential getCredential(String alias) throws IOException {
String keystoreFile = System.getProperty("javax.net.ssl.keyStore");
if (keystoreFile == null) {
throw new RuntimeException("You must provide a default keystore");
}
String keystoreType = System.getProperty("javax.net.ssl.trustStoreType",
KeyStore.getDefaultType());
PrivateKey privateKey;
PublicKey publicKey;
try {
KeyStore ks = KeyStore.getInstance(keystoreType);
InputStream ksis = new FileInputStream(keystoreFile);
String keystorePasswordStr = System.getProperty(
"javax.net.ssl.keyStorePassword");
char[] keystorePassword = keystorePasswordStr == null ? null
: keystorePasswordStr.toCharArray();
try {
ks.load(ksis, keystorePassword);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
} catch (CertificateException ex) {
throw new RuntimeException(ex);
} finally {
ksis.close();
}
Key key = null;
try {
key = ks.getKey(alias, keystorePassword);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
} catch (UnrecoverableKeyException ex) {
throw new RuntimeException(ex);
}
if (key == null) {
throw new NullPointerException();
}
privateKey = (PrivateKey) key;
publicKey = ks.getCertificate("adaptor").getPublicKey();
} catch (KeyStoreException ex) {
throw new RuntimeException(ex);
}
return SecurityHelper.getSimpleCredential(publicKey, privateKey);
}
}