/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.cas; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.URL; import java.security.KeyStore; import java.util.Properties; import java.util.logging.Logger; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.TrustManagerFactory; import javax.servlet.http.HttpServletResponse; import org.geoserver.data.test.LiveSystemTestData; import org.geotools.util.logging.Logging; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; /** * Extends LiveData to deal with a CAS server (central authentication server) * @author christian * */ public class LiveCasData extends LiveSystemTestData { private static final Logger LOGGER = Logging.getLogger(LiveCasData.class); private static final String CAS_SERVER_PROPERTY = "casserverurlprefix"; private static final String CAS_SERVICE_PROPERTY = "service"; private static final String CAS_PROXYCALLBACK_PROPERTY = "proxycallbackurlprefix"; /** * The property file containing the token -> value pairs used to get * a CAS server Url * * */ protected File fixture; protected URL serverURLPrefix, serviceURL,loginURL, proxyCallbackURLPrefix; protected File keyStoreFile; /** * List of file paths (relative to the source data directory) that will be * subjected to token filtering. By default only <code>catalog.xml</code> * will be filtered. */ public URL getServerURLPrefix() { return serverURLPrefix; } public URL getLoginURL() { return loginURL; } public void setLoginURL(URL loginURL) { this.loginURL = loginURL; } public URL getServiceURL() { return serviceURL; } public URL getProxyCallbackURLPrefix() { return proxyCallbackURLPrefix; } public void setProxyCallbackURLPrefix(URL proxyCallbackURLPrefix) { this.proxyCallbackURLPrefix = proxyCallbackURLPrefix; } /** * constant fixture id */ protected String fixtureId="cas"; public LiveCasData(File dataDirSourceDirectory ) throws IOException { super(dataDirSourceDirectory); this.fixture = lookupFixture(fixtureId); } public File getKeyStoreFile() { return keyStoreFile; } public void setKeyStoreFile(File keyStoreFile) { this.keyStoreFile = keyStoreFile; } /** * Looks up the fixture file in the home directory provided that the * @param fixtureId * */ private File lookupFixture(String fixtureId) { // first of all, make sure the fixture was not disabled using a system // variable final String property = System.getProperty("gs." + fixtureId); if (property != null && "false".equals(property.toLowerCase())) { return null; } // then look in the user home directory File base = new File(System.getProperty("user.home"), ".geoserver"); // create the hidden folder, this is handy especially on windows where // a user cannot create a directory starting with . from the UI // (works only from the command line) if(!base.exists()) base.mkdir(); File fixtureFile = new File(base, fixtureId + ".properties"); if (!fixtureFile.exists()) { final String warning = "Disabling test based on fixture " + fixtureId + " since the file " + fixtureFile + " could not be found"; disableTest(warning); return null; } Properties props = new Properties(); try { props.load(new FileInputStream(fixtureFile)); String tmp = props.getProperty(CAS_SERVER_PROPERTY); if (tmp==null) tmp=""; // avoid NPE serverURLPrefix=new URL(tmp); loginURL=new URL(tmp+"/login"); tmp = props.getProperty(CAS_SERVICE_PROPERTY); if (tmp==null) tmp=""; // avoid NPE serviceURL=new URL(tmp); tmp = props.getProperty(CAS_PROXYCALLBACK_PROPERTY); if (tmp==null) tmp=""; // avoid NPE proxyCallbackURLPrefix=new URL(tmp); } catch (Exception e) { disableTest("Error in fixture file: "+e.getMessage()); return null; } // check connection try { HttpURLConnection huc = (HttpURLConnection) loginURL.openConnection(); huc.setRequestMethod("GET"); huc.connect(); if (huc.getResponseCode()!=HttpServletResponse.SC_OK) { disableTest("Cannot connect to "+loginURL.toString()); return null; } } catch (Exception ex) { disableTest("problem with cas connection: "+ex.getMessage()); return null; } keyStoreFile = new File(base,"keystore.jks"); if (keyStoreFile.exists()==false) { disableTest("Keystore not found: "+ keyStoreFile.getAbsolutePath()); return null; } return fixtureFile; } public boolean isTestDataAvailable() { return fixture != null; } @Override public void setUp() throws Exception { // if the test was disabled we don't need to run the setup if (fixture == null) return; super.setUp(); } /** * Permanently disable this test logging the specificed warning message (the reason * why the test is being disabled) * @param warning */ private void disableTest(final String warning) { LOGGER.warning(warning); fixture = null; System.setProperty("gs." + fixtureId, "false"); } protected HttpsServer createSSLServer() throws Exception{ // keytool -genkey -alias alias -keypass simulator \ // -keystore lig.keystore -storepass simulator InetSocketAddress address = new InetSocketAddress ( getProxyCallbackURLPrefix().getPort() ); // initialise the HTTPS server HttpsServer httpsServer = HttpsServer.create ( address, 0 ); SSLContext sslContext = SSLContext.getInstance ( "TLS" ); // initialise the keystore char[] password = "changeit".toCharArray (); KeyStore ks = KeyStore.getInstance ( "JKS" ); File base = new File(System.getProperty("user.home"), ".geoserver"); File keystore = new File(base,"keystore.jks"); FileInputStream fis = new FileInputStream ( keystore ); ks.load ( fis, password ); // setup the key manager factory KeyManagerFactory kmf = KeyManagerFactory.getInstance ( KeyManagerFactory.getDefaultAlgorithm() ); kmf.init ( ks, password ); // setup the trust manager factory TrustManagerFactory tmf = TrustManagerFactory.getInstance ( TrustManagerFactory.getDefaultAlgorithm() ); tmf.init ( ks ); // setup the HTTPS context and parameters sslContext.init ( kmf.getKeyManagers (), tmf.getTrustManagers(), null ); httpsServer.setHttpsConfigurator( new HttpsConfigurator( sslContext ) { public void configure ( HttpsParameters params ) { try { // initialise the SSL context SSLContext c = SSLContext.getDefault (); SSLEngine engine = c.createSSLEngine (); params.setNeedClientAuth ( false ); params.setCipherSuites ( engine.getEnabledCipherSuites () ); params.setProtocols ( engine.getEnabledProtocols () ); // get the default parameters SSLParameters defaultSSLParameters = c.getDefaultSSLParameters (); params.setSSLParameters ( defaultSSLParameters ); } catch ( Exception ex ) { throw new RuntimeException(ex); } } } ); httpsServer.createContext("/test", new HttpHandler() { @Override public void handle(HttpExchange t) throws IOException { LOGGER.info("https server working"); t.getRequestBody().close(); t.sendResponseHeaders(200, 0); t.getResponseBody().close(); } }); httpsServer.setExecutor(null); // creates a default executor return httpsServer; } protected void checkSSLServer() throws Exception { URL testSSLURL = new URL(getProxyCallbackURLPrefix().getProtocol(), getProxyCallbackURLPrefix().getHost(),getProxyCallbackURLPrefix().getPort(),"/test"); HttpURLConnection con = (HttpURLConnection) testSSLURL.openConnection(); con.getInputStream().close(); } }