summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java162
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java55
2 files changed, 200 insertions, 17 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
new file mode 100644
index 0000000000..c142bc23aa
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013, Microsoft Corporation
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class HttpAuthTest {
+ private static String digestHeader = "WWW-Authenticate: Digest qop=\"auth\",algorithm=MD5-sess,nonce=\"+Upgraded+v1b9...ba\",charset=utf-8,realm=\"Digest\"";
+
+ private static String basicHeader = "WWW-Authenticate: Basic realm=\"everyones.loves.git\"";
+
+ private static String ntlmHeader = "WWW-Authenticate: NTLM";
+
+ private static String bearerHeader = "WWW-Authenticate: Bearer";
+
+ private static String URL_SAMPLE = "http://everyones.loves.git/u/2";
+
+ private static String BASIC = "Basic";
+
+ private static String DIGEST = "Digest";
+
+ @Test
+ public void testHttpAuthScanResponse() throws MalformedURLException {
+ checkResponse(new String[] { basicHeader }, BASIC);
+ checkResponse(new String[] { digestHeader }, DIGEST);
+ checkResponse(new String[] { basicHeader, digestHeader }, DIGEST);
+ checkResponse(new String[] { digestHeader, basicHeader }, DIGEST);
+ checkResponse(new String[] { ntlmHeader, basicHeader, digestHeader,
+ bearerHeader }, DIGEST);
+ checkResponse(new String[] { ntlmHeader, basicHeader, bearerHeader },
+ BASIC);
+ }
+
+ private static void checkResponse(String[] headers,
+ String expectedAuthMethod) throws MalformedURLException {
+
+ AuthHeadersResponse responce = new AuthHeadersResponse(headers);
+ HttpAuthMethod authMethod = HttpAuthMethod.scanResponse(responce);
+
+ if (!expectedAuthMethod.equals(getAuthMethodName(authMethod))) {
+ fail("Wrong authentication method: expected " + expectedAuthMethod
+ + ", but received " + getAuthMethodName(authMethod));
+ }
+ }
+
+ private static String getAuthMethodName(HttpAuthMethod authMethod) {
+ return authMethod.getClass().getSimpleName();
+ }
+
+ private static class AuthHeadersResponse extends HttpURLConnection {
+ Map<String, List<String>> headerFields = new HashMap<String, List<String>>();
+
+ public AuthHeadersResponse(String[] authHeaders)
+ throws MalformedURLException {
+ super(new URL(URL_SAMPLE));
+ parseHeaders(authHeaders);
+ }
+
+ @Override
+ public void disconnect() {
+ fail("The disconnect method shouldn't be invoked");
+ }
+
+ @Override
+ public boolean usingProxy() {
+ return false;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ fail("The connect method shouldn't be invoked");
+ }
+
+ @Override
+ public String getHeaderField(String name) {
+ if (!headerFields.containsKey(name))
+ return null;
+ else {
+ int n = headerFields.get(name).size();
+
+ if (n > 0)
+ return headerFields.get(name).get(n - 1);
+ else
+ return null;
+ }
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaderFields() {
+ return headerFields;
+ }
+
+ private void parseHeaders(String[] headers) {
+ for (String header : headers) {
+ int i = header.indexOf(':');
+
+ if (i < 0)
+ continue;
+
+ String key = header.substring(0, i);
+ String value = header.substring(i + 1).trim();
+
+ if (!headerFields.containsKey(key))
+ headerFields.put(key, new ArrayList<String>());
+
+ List<String> values = headerFields.get(key);
+ values.add(value);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
index 4ab7998f5c..8acba21bd0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2010, 2013, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -55,7 +55,9 @@ import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Random;
import org.eclipse.jgit.util.Base64;
@@ -69,6 +71,8 @@ import org.eclipse.jgit.util.Base64;
abstract class HttpAuthMethod {
/** No authentication is configured. */
static final HttpAuthMethod NONE = new None();
+ static final String EMPTY_STRING = ""; //$NON-NLS-1$
+ static final String SCHEMA_NAME_SEPARATOR = " "; //$NON-NLS-1$
/**
* Handle an authentication failure and possibly return a new response.
@@ -77,22 +81,39 @@ abstract class HttpAuthMethod {
* the connection that failed.
* @return new authentication method to try.
*/
- static HttpAuthMethod scanResponse(HttpURLConnection conn) {
- String hdr = conn.getHeaderField(HDR_WWW_AUTHENTICATE);
- if (hdr == null || hdr.length() == 0)
- return NONE;
-
- int sp = hdr.indexOf(' ');
- if (sp < 0)
- return NONE;
-
- String type = hdr.substring(0, sp);
- if (Basic.NAME.equalsIgnoreCase(type))
- return new Basic();
- else if (Digest.NAME.equalsIgnoreCase(type))
- return new Digest(hdr.substring(sp + 1));
- else
- return NONE;
+ static HttpAuthMethod scanResponse(final HttpURLConnection conn) {
+ final Map<String, List<String>> headers = conn.getHeaderFields();
+ HttpAuthMethod authentication = NONE;
+
+ for (final Entry<String, List<String>> entry : headers.entrySet()) {
+ if (HDR_WWW_AUTHENTICATE.equalsIgnoreCase(entry.getKey())) {
+ if (entry.getValue() != null) {
+ for (final String value : entry.getValue()) {
+ if (value != null && value.length() != 0) {
+ final String[] valuePart = value.split(
+ SCHEMA_NAME_SEPARATOR, 2);
+
+ if (Digest.NAME.equalsIgnoreCase(valuePart[0])) {
+ final String param;
+ if (valuePart.length == 1)
+ param = EMPTY_STRING;
+ else
+ param = valuePart[1];
+
+ authentication = new Digest(param);
+ break;
+ }
+
+ if (Basic.NAME.equalsIgnoreCase(valuePart[0]))
+ authentication = new Basic();
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return authentication;
}
/**