tikhomirov@699: /* tikhomirov@699: * Copyright (c) 2013 TMate Software Ltd tikhomirov@699: * tikhomirov@699: * This program is free software; you can redistribute it and/or modify tikhomirov@699: * it under the terms of the GNU General Public License as published by tikhomirov@699: * the Free Software Foundation; version 2 of the License. tikhomirov@699: * tikhomirov@699: * This program is distributed in the hope that it will be useful, tikhomirov@699: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@699: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@699: * GNU General Public License for more details. tikhomirov@699: * tikhomirov@699: * For information on how to redistribute this software under tikhomirov@699: * the terms of a license other than GNU General Public License tikhomirov@699: * contact TMate Software at support@hg4j.com tikhomirov@699: */ tikhomirov@699: package org.tmatesoft.hg.internal.remote; tikhomirov@699: tikhomirov@699: import static org.tmatesoft.hg.util.LogFacility.Severity.Info; tikhomirov@699: tikhomirov@699: import java.io.IOException; tikhomirov@699: import java.io.InputStream; tikhomirov@699: import java.net.HttpURLConnection; tikhomirov@699: import java.net.URL; tikhomirov@699: import java.security.cert.CertificateException; tikhomirov@699: import java.security.cert.X509Certificate; tikhomirov@699: import java.util.prefs.BackingStoreException; tikhomirov@699: import java.util.prefs.Preferences; tikhomirov@699: tikhomirov@699: import javax.net.ssl.HttpsURLConnection; tikhomirov@699: import javax.net.ssl.SSLContext; tikhomirov@699: import javax.net.ssl.TrustManager; tikhomirov@699: import javax.net.ssl.X509TrustManager; tikhomirov@699: tikhomirov@699: import org.tmatesoft.hg.auth.HgAuthFailedException; tikhomirov@699: import org.tmatesoft.hg.auth.HgAuthMethod; tikhomirov@699: import org.tmatesoft.hg.core.HgRemoteConnectionException; tikhomirov@699: import org.tmatesoft.hg.core.SessionContext; tikhomirov@699: import org.tmatesoft.hg.repo.HgInvalidStateException; tikhomirov@699: tikhomirov@699: /** tikhomirov@699: * tikhomirov@699: * @author Artem Tikhomirov tikhomirov@699: * @author TMate Software Ltd. tikhomirov@699: */ tikhomirov@699: public class HttpAuthMethod implements HgAuthMethod { tikhomirov@699: tikhomirov@699: private final SessionContext ctx; tikhomirov@699: private final URL url; tikhomirov@699: private String authInfo; tikhomirov@699: private SSLContext sslContext; tikhomirov@699: tikhomirov@699: /** tikhomirov@699: * @param sessionContext tikhomirov@699: * @param url location fully ready to attempt connection to perform authentication check, e.g. hello command (anything with *small* output will do) tikhomirov@699: * @throws HgRemoteConnectionException tikhomirov@699: */ tikhomirov@699: HttpAuthMethod(SessionContext sessionContext, URL url) throws HgRemoteConnectionException { tikhomirov@699: ctx = sessionContext; tikhomirov@699: if (!"http".equals(url.getProtocol()) && !"https".equals(url.getProtocol())) { tikhomirov@699: throw new HgInvalidStateException(String.format("http protocol expected: %s", url.toString())); tikhomirov@699: } tikhomirov@699: this.url = url; tikhomirov@699: if ("https".equals(url.getProtocol())) { tikhomirov@699: try { tikhomirov@699: sslContext = SSLContext.getInstance("SSL"); tikhomirov@699: class TrustEveryone implements X509TrustManager { tikhomirov@699: public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { tikhomirov@699: } tikhomirov@699: public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { tikhomirov@699: } tikhomirov@699: public X509Certificate[] getAcceptedIssuers() { tikhomirov@699: return new X509Certificate[0]; tikhomirov@699: } tikhomirov@699: }; tikhomirov@699: sslContext.init(null, new TrustManager[] { new TrustEveryone() }, null); tikhomirov@699: } catch (Exception ex) { tikhomirov@699: throw new HgRemoteConnectionException("Can't initialize secure connection", ex); tikhomirov@699: } tikhomirov@699: } else { tikhomirov@699: sslContext = null; tikhomirov@699: } tikhomirov@699: } tikhomirov@699: tikhomirov@699: public void tryWithUserInfo(String uriUserInfo) throws HgAuthFailedException { tikhomirov@699: int colon = uriUserInfo.indexOf(':'); tikhomirov@699: if (colon == -1) { tikhomirov@699: withPassword(uriUserInfo, null); tikhomirov@699: } else { tikhomirov@699: withPassword(uriUserInfo.substring(0, colon), uriUserInfo.substring(colon+1)); tikhomirov@699: } tikhomirov@699: } tikhomirov@699: tikhomirov@699: public void noCredentials() throws HgAuthFailedException { tikhomirov@699: // TODO Auto-generated method stub tikhomirov@699: checkConnection(); tikhomirov@699: } tikhomirov@699: tikhomirov@699: public boolean supportsPassword() { tikhomirov@699: return true; tikhomirov@699: } tikhomirov@699: tikhomirov@699: public void withPassword(String username, String password) throws HgAuthFailedException { tikhomirov@699: authInfo = buildAuthValue(username, password == null ? "" : password); tikhomirov@699: checkConnection(); tikhomirov@699: } tikhomirov@699: tikhomirov@699: public boolean supportsPublicKey() { tikhomirov@699: return false; tikhomirov@699: } tikhomirov@699: tikhomirov@699: public void withPublicKey(String username, InputStream privateKey, String passphrase) throws HgAuthFailedException { tikhomirov@699: } tikhomirov@699: tikhomirov@699: public boolean supportsCertificate() { tikhomirov@699: return "https".equals(url.getProtocol()); tikhomirov@699: } tikhomirov@699: tikhomirov@699: public void withCertificate(X509Certificate[] clientCert) throws HgAuthFailedException { tikhomirov@699: // TODO Auto-generated method stub tikhomirov@699: checkConnection(); tikhomirov@699: } tikhomirov@699: tikhomirov@699: private void checkConnection() throws HgAuthFailedException { tikhomirov@699: // we've checked the protocol to be http(s) tikhomirov@699: HttpURLConnection c = null; tikhomirov@699: try { tikhomirov@699: c = (HttpURLConnection) url.openConnection(); tikhomirov@699: c = setupConnection(c); tikhomirov@699: c.connect(); tikhomirov@699: InputStream is = c.getInputStream(); tikhomirov@699: while (is.read() != -1) { tikhomirov@699: } tikhomirov@699: is.close(); tikhomirov@699: final int HTTP_UNAUTHORIZED = 401; tikhomirov@699: if (c.getResponseCode() == HTTP_UNAUTHORIZED) { tikhomirov@699: throw new HgAuthFailedException(c.getResponseMessage(), null); tikhomirov@699: } tikhomirov@699: } catch (IOException ex) { tikhomirov@699: throw new HgAuthFailedException("Communication failure while authenticating", ex); tikhomirov@699: } finally { tikhomirov@699: if (c != null) { tikhomirov@699: c.disconnect(); tikhomirov@699: } tikhomirov@699: } tikhomirov@699: } tikhomirov@699: tikhomirov@699: HttpURLConnection setupConnection(HttpURLConnection urlConnection) { tikhomirov@699: if (authInfo != null) { tikhomirov@699: urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); tikhomirov@699: } tikhomirov@699: if (sslContext != null) { tikhomirov@699: ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext.getSocketFactory()); tikhomirov@699: } tikhomirov@699: return urlConnection; tikhomirov@699: } tikhomirov@699: tikhomirov@699: private String buildAuthValue(String username, String password) { tikhomirov@699: String ai = null; tikhomirov@699: try { tikhomirov@699: // Hack to get Base64-encoded credentials tikhomirov@699: Preferences tempNode = Preferences.userRoot().node("xxx"); tikhomirov@699: tempNode.putByteArray("xxx", String.format("%s:%s", username, password).getBytes()); tikhomirov@699: ai = tempNode.get("xxx", null); tikhomirov@699: tempNode.removeNode(); tikhomirov@699: } catch (BackingStoreException ex) { tikhomirov@699: ctx.getLog().dump(getClass(), Info, ex, null); tikhomirov@699: // IGNORE tikhomirov@699: } tikhomirov@699: return ai; tikhomirov@699: } tikhomirov@699: }