Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/remote/HttpAuthMethod.java @ 699:a483b2b68a2e
Provisional APIs and respective implementation for http, https and ssh remote repositories
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> | 
|---|---|
| date | Thu, 08 Aug 2013 19:18:50 +0200 | 
| parents | |
| children | 
   comparison
  equal
  deleted
  inserted
  replaced
| 698:822f3a83ff57 | 699:a483b2b68a2e | 
|---|---|
| 1 /* | |
| 2 * Copyright (c) 2013 TMate Software Ltd | |
| 3 * | |
| 4 * This program is free software; you can redistribute it and/or modify | |
| 5 * it under the terms of the GNU General Public License as published by | |
| 6 * the Free Software Foundation; version 2 of the License. | |
| 7 * | |
| 8 * This program is distributed in the hope that it will be useful, | |
| 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 * GNU General Public License for more details. | |
| 12 * | |
| 13 * For information on how to redistribute this software under | |
| 14 * the terms of a license other than GNU General Public License | |
| 15 * contact TMate Software at support@hg4j.com | |
| 16 */ | |
| 17 package org.tmatesoft.hg.internal.remote; | |
| 18 | |
| 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Info; | |
| 20 | |
| 21 import java.io.IOException; | |
| 22 import java.io.InputStream; | |
| 23 import java.net.HttpURLConnection; | |
| 24 import java.net.URL; | |
| 25 import java.security.cert.CertificateException; | |
| 26 import java.security.cert.X509Certificate; | |
| 27 import java.util.prefs.BackingStoreException; | |
| 28 import java.util.prefs.Preferences; | |
| 29 | |
| 30 import javax.net.ssl.HttpsURLConnection; | |
| 31 import javax.net.ssl.SSLContext; | |
| 32 import javax.net.ssl.TrustManager; | |
| 33 import javax.net.ssl.X509TrustManager; | |
| 34 | |
| 35 import org.tmatesoft.hg.auth.HgAuthFailedException; | |
| 36 import org.tmatesoft.hg.auth.HgAuthMethod; | |
| 37 import org.tmatesoft.hg.core.HgRemoteConnectionException; | |
| 38 import org.tmatesoft.hg.core.SessionContext; | |
| 39 import org.tmatesoft.hg.repo.HgInvalidStateException; | |
| 40 | |
| 41 /** | |
| 42 * | |
| 43 * @author Artem Tikhomirov | |
| 44 * @author TMate Software Ltd. | |
| 45 */ | |
| 46 public class HttpAuthMethod implements HgAuthMethod { | |
| 47 | |
| 48 private final SessionContext ctx; | |
| 49 private final URL url; | |
| 50 private String authInfo; | |
| 51 private SSLContext sslContext; | |
| 52 | |
| 53 /** | |
| 54 * @param sessionContext | |
| 55 * @param url location fully ready to attempt connection to perform authentication check, e.g. hello command (anything with *small* output will do) | |
| 56 * @throws HgRemoteConnectionException | |
| 57 */ | |
| 58 HttpAuthMethod(SessionContext sessionContext, URL url) throws HgRemoteConnectionException { | |
| 59 ctx = sessionContext; | |
| 60 if (!"http".equals(url.getProtocol()) && !"https".equals(url.getProtocol())) { | |
| 61 throw new HgInvalidStateException(String.format("http protocol expected: %s", url.toString())); | |
| 62 } | |
| 63 this.url = url; | |
| 64 if ("https".equals(url.getProtocol())) { | |
| 65 try { | |
| 66 sslContext = SSLContext.getInstance("SSL"); | |
| 67 class TrustEveryone implements X509TrustManager { | |
| 68 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { | |
| 69 } | |
| 70 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { | |
| 71 } | |
| 72 public X509Certificate[] getAcceptedIssuers() { | |
| 73 return new X509Certificate[0]; | |
| 74 } | |
| 75 }; | |
| 76 sslContext.init(null, new TrustManager[] { new TrustEveryone() }, null); | |
| 77 } catch (Exception ex) { | |
| 78 throw new HgRemoteConnectionException("Can't initialize secure connection", ex); | |
| 79 } | |
| 80 } else { | |
| 81 sslContext = null; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 public void tryWithUserInfo(String uriUserInfo) throws HgAuthFailedException { | |
| 86 int colon = uriUserInfo.indexOf(':'); | |
| 87 if (colon == -1) { | |
| 88 withPassword(uriUserInfo, null); | |
| 89 } else { | |
| 90 withPassword(uriUserInfo.substring(0, colon), uriUserInfo.substring(colon+1)); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 public void noCredentials() throws HgAuthFailedException { | |
| 95 // TODO Auto-generated method stub | |
| 96 checkConnection(); | |
| 97 } | |
| 98 | |
| 99 public boolean supportsPassword() { | |
| 100 return true; | |
| 101 } | |
| 102 | |
| 103 public void withPassword(String username, String password) throws HgAuthFailedException { | |
| 104 authInfo = buildAuthValue(username, password == null ? "" : password); | |
| 105 checkConnection(); | |
| 106 } | |
| 107 | |
| 108 public boolean supportsPublicKey() { | |
| 109 return false; | |
| 110 } | |
| 111 | |
| 112 public void withPublicKey(String username, InputStream privateKey, String passphrase) throws HgAuthFailedException { | |
| 113 } | |
| 114 | |
| 115 public boolean supportsCertificate() { | |
| 116 return "https".equals(url.getProtocol()); | |
| 117 } | |
| 118 | |
| 119 public void withCertificate(X509Certificate[] clientCert) throws HgAuthFailedException { | |
| 120 // TODO Auto-generated method stub | |
| 121 checkConnection(); | |
| 122 } | |
| 123 | |
| 124 private void checkConnection() throws HgAuthFailedException { | |
| 125 // we've checked the protocol to be http(s) | |
| 126 HttpURLConnection c = null; | |
| 127 try { | |
| 128 c = (HttpURLConnection) url.openConnection(); | |
| 129 c = setupConnection(c); | |
| 130 c.connect(); | |
| 131 InputStream is = c.getInputStream(); | |
| 132 while (is.read() != -1) { | |
| 133 } | |
| 134 is.close(); | |
| 135 final int HTTP_UNAUTHORIZED = 401; | |
| 136 if (c.getResponseCode() == HTTP_UNAUTHORIZED) { | |
| 137 throw new HgAuthFailedException(c.getResponseMessage(), null); | |
| 138 } | |
| 139 } catch (IOException ex) { | |
| 140 throw new HgAuthFailedException("Communication failure while authenticating", ex); | |
| 141 } finally { | |
| 142 if (c != null) { | |
| 143 c.disconnect(); | |
| 144 } | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 HttpURLConnection setupConnection(HttpURLConnection urlConnection) { | |
| 149 if (authInfo != null) { | |
| 150 urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); | |
| 151 } | |
| 152 if (sslContext != null) { | |
| 153 ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext.getSocketFactory()); | |
| 154 } | |
| 155 return urlConnection; | |
| 156 } | |
| 157 | |
| 158 private String buildAuthValue(String username, String password) { | |
| 159 String ai = null; | |
| 160 try { | |
| 161 // Hack to get Base64-encoded credentials | |
| 162 Preferences tempNode = Preferences.userRoot().node("xxx"); | |
| 163 tempNode.putByteArray("xxx", String.format("%s:%s", username, password).getBytes()); | |
| 164 ai = tempNode.get("xxx", null); | |
| 165 tempNode.removeNode(); | |
| 166 } catch (BackingStoreException ex) { | |
| 167 ctx.getLog().dump(getClass(), Info, ex, null); | |
| 168 // IGNORE | |
| 169 } | |
| 170 return ai; | |
| 171 } | |
| 172 } | 
