Merge pull request #2752 from lachlan-roberts/jetty-9.4.x-2739-AuthenticationProtocolHandler
Issue #2739 - AuthenticationProtocolHandler Multiple Challenge Pattern
This commit is contained in:
commit
9dd2369a53
|
@ -38,7 +38,6 @@ import org.eclipse.jetty.client.util.BufferingResponseListener;
|
|||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.QuotedCSV;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -46,16 +45,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
{
|
||||
public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
|
||||
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
|
||||
|
||||
private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)");
|
||||
private static final Pattern TYPE_PATTERN = Pattern.compile("([^\\s]+)(\\s+(.*))?");
|
||||
private static final Pattern MULTIPLE_CHALLENGE_PATTERN = Pattern.compile("(.*?)\\s*,\\s*([^=\\s,]+(\\s+[^=\\s].*)?)");
|
||||
private static final Pattern BASE64_PATTERN = Pattern.compile("[\\+\\-\\.\\/\\dA-Z_a-z~]+=*");
|
||||
|
||||
private final HttpClient client;
|
||||
private final int maxContentLength;
|
||||
private final ResponseNotifier notifier;
|
||||
|
||||
private static final Pattern CHALLENGE_PATTERN = Pattern.compile("(?<schemeOnly>[!#$%&'*+\\-.^_`|~0-9A-Za-z]+)|(?:(?<scheme>[!#$%&'*+\\-.^_`|~0-9A-Za-z]+)\\s+)?(?:(?<token68>[a-zA-Z0-9\\-._~+\\/]+=*)|(?<paramName>[!#$%&'*+\\-.^_`|~0-9A-Za-z]+)\\s*=\\s*(?:(?<paramValue>.*)))");
|
||||
|
||||
protected AuthenticationProtocolHandler(HttpClient client, int maxContentLength)
|
||||
{
|
||||
this.client = client;
|
||||
|
@ -82,80 +77,51 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
// Return new instances every time to keep track of the response content
|
||||
return new AuthenticationListener();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected List<HeaderInfo> getHeaderInfo(String value) throws IllegalArgumentException
|
||||
|
||||
protected List<HeaderInfo> getHeaderInfo(String header) throws IllegalArgumentException
|
||||
{
|
||||
String header = value;
|
||||
List<HeaderInfo> headerInfos = new ArrayList<>();
|
||||
|
||||
while(true)
|
||||
Matcher m;
|
||||
|
||||
for(String value : new QuotedCSV(true, header))
|
||||
{
|
||||
Matcher m = MULTIPLE_CHALLENGE_PATTERN.matcher(header);
|
||||
m = CHALLENGE_PATTERN.matcher(value);
|
||||
if (m.matches())
|
||||
{
|
||||
headerInfos.add(newHeaderInfo(m.group(1)));
|
||||
header = m.group(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerInfos.add(newHeaderInfo(header));
|
||||
break;
|
||||
if(m.group("schemeOnly") != null)
|
||||
{
|
||||
headerInfos.add(new HeaderInfo(getAuthorizationHeader(), m.group(1), new HashMap<>()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m.group("scheme") != null)
|
||||
{
|
||||
headerInfos.add(new HeaderInfo(getAuthorizationHeader(), m.group("scheme"), new HashMap<>()));
|
||||
}
|
||||
|
||||
if (headerInfos.isEmpty())
|
||||
throw new IllegalArgumentException("Parameters without auth-scheme");
|
||||
|
||||
Map<String, String> authParams = headerInfos.get(headerInfos.size() - 1).getParameters();
|
||||
if (m.group("paramName") != null)
|
||||
{
|
||||
String paramVal = QuotedCSV.unquote(m.group("paramValue"));
|
||||
authParams.put(m.group("paramName"), paramVal);
|
||||
}
|
||||
else if (m.group("token68") != null)
|
||||
{
|
||||
if (!authParams.isEmpty())
|
||||
throw new IllegalArgumentException("token68 after auth-params");
|
||||
|
||||
authParams.put("base64", m.group("token68"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return headerInfos;
|
||||
}
|
||||
|
||||
protected HeaderInfo newHeaderInfo(String value) throws IllegalArgumentException
|
||||
{
|
||||
String type;
|
||||
Map<String,String> params = new HashMap<>();
|
||||
|
||||
Matcher m = TYPE_PATTERN.matcher(value);
|
||||
if (m.matches())
|
||||
{
|
||||
type = m.group(1);
|
||||
if (m.group(2) != null)
|
||||
params = parseParameters(m.group(3));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid Authentication Format");
|
||||
}
|
||||
|
||||
return new HeaderInfo(getAuthorizationHeader(), type, params);
|
||||
}
|
||||
|
||||
protected Map<String, String> parseParameters(String wwwAuthenticate) throws IllegalArgumentException
|
||||
{
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
Matcher b64 = BASE64_PATTERN.matcher(wwwAuthenticate);
|
||||
if (b64.matches())
|
||||
{
|
||||
result.put("base64", wwwAuthenticate);
|
||||
return result;
|
||||
}
|
||||
|
||||
QuotedCSV parts = new QuotedCSV(false, wwwAuthenticate);
|
||||
for (String part : parts)
|
||||
{
|
||||
Matcher params = PARAM_PATTERN.matcher(part);
|
||||
if (params.matches())
|
||||
{
|
||||
String name = StringUtil.asciiToLowerCase(params.group(1));
|
||||
String value = (params.group(2)==null) ? "" : params.group(2);
|
||||
result.put(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid Authentication Format");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private class AuthenticationListener extends BufferingResponseListener
|
||||
{
|
||||
|
|
|
@ -31,12 +31,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.Authentication.HeaderInfo;
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
|
@ -44,7 +44,6 @@ import org.eclipse.jetty.client.api.Request;
|
|||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Response.Listener;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.api.Authentication.HeaderInfo;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.client.util.DigestAuthentication;
|
||||
|
@ -705,7 +704,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
|||
Assert.assertTrue(headerInfo.getParameter("name").equals("value"));
|
||||
Assert.assertTrue(headerInfo.getParameter("other").equals("value2"));
|
||||
|
||||
headerInfos = aph.getHeaderInfo("Scheme name=value, Scheme2 name=value2");
|
||||
headerInfos = aph.getHeaderInfo(", , , , ,,,Scheme name=value, ,,Scheme2 name=value2,, ,,");
|
||||
Assert.assertEquals(headerInfos.size(), 2);
|
||||
Assert.assertTrue(headerInfos.get(0).getType().equalsIgnoreCase("Scheme"));
|
||||
Assert.assertTrue(headerInfos.get(0).getParameter("nAmE").equals("value"));
|
||||
|
@ -768,4 +767,23 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
|||
Assert.assertTrue(headerInfoList.get(2).getParameter("realm").equals("thermostat3="));
|
||||
Assert.assertTrue(headerInfoList.get(2).getParameter("nonce").equals("9523570528="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleChallangeLooksLikeMultipleChallenge()
|
||||
{
|
||||
AuthenticationProtocolHandler aph = new WWWAuthenticationProtocolHandler(client);
|
||||
List<HeaderInfo> headerInfoList = aph.getHeaderInfo("Digest param=\",f \"");
|
||||
Assert.assertEquals(1, headerInfoList.size());
|
||||
|
||||
|
||||
|
||||
headerInfoList = aph.getHeaderInfo("Digest realm=\"thermostat\", qop=\",Digest realm=hello\", nonce=\"1523430383=\"");
|
||||
Assert.assertEquals(1, headerInfoList.size());
|
||||
|
||||
HeaderInfo headerInfo = headerInfoList.get(0);
|
||||
Assert.assertTrue(headerInfo.getType().equalsIgnoreCase("Digest"));
|
||||
Assert.assertTrue(headerInfo.getParameter("qop").equals(",Digest realm=hello"));
|
||||
Assert.assertTrue(headerInfo.getParameter("realm").equals("thermostat"));
|
||||
Assert.assertThat(headerInfo.getParameter("nonce"), Matchers.is("1523430383="));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue