This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator;
import java.util.List;
import java.util.Map;
/**
* This class incorporates test response data
*/
class ResponseDescriptor {
private Map<String, List<String>> headers;
private String body;
private int responseCode;
public Map<String, List<String>> getHeaders() {
return headers;
}
public void setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
}

View File

@@ -0,0 +1,170 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.valves.RemoteIpValve;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
public class TestAuthInfoResponseHeaders extends TomcatBaseTest {
private static String USER = "user";
private static String PWD = "pwd";
private static String ROLE = "role";
private static String URI = "/protected";
private static String CONTEXT_PATH = "/foo";
private static String CLIENT_AUTH_HEADER = "authorization";
/*
* Encapsulate the logic to generate an HTTP header
* for BASIC Authentication.
* Note: only used internally, so no need to validate arguments.
*/
private static final class BasicCredentials {
private final String method;
private final String username;
private final String password;
private final String credentials;
private BasicCredentials(String aMethod,
String aUsername, String aPassword) {
method = aMethod;
username = aUsername;
password = aPassword;
String userCredentials = username + ":" + password;
byte[] credentialsBytes =
userCredentials.getBytes(StandardCharsets.ISO_8859_1);
String base64auth = Base64.encodeBase64String(credentialsBytes);
credentials= method + " " + base64auth;
}
private String getCredentials() {
return credentials;
}
}
@Test
public void testNoHeaders() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false);
}
@Test
public void testWithHeaders() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, true);
}
public void doTest(String user, String pwd, String uri, boolean expectResponseAuthHeaders)
throws Exception {
if (expectResponseAuthHeaders) {
BasicAuthenticator auth =
(BasicAuthenticator) getTomcatInstance().getHost().findChild(
CONTEXT_PATH).getPipeline().getFirst();
auth.setSendAuthInfoResponseHeaders(true);
}
getTomcatInstance().start();
Map<String,List<String>> reqHeaders = new HashMap<>();
List<String> auth = new ArrayList<>();
auth.add(new BasicCredentials("Basic", user, pwd).getCredentials());
reqHeaders.put(CLIENT_AUTH_HEADER, auth);
List<String> forwardedFor = new ArrayList<>();
forwardedFor.add("192.168.0.10");
List<String> forwardedHost = new ArrayList<>();
forwardedHost.add("localhost");
reqHeaders.put("X-Forwarded-For", forwardedFor);
reqHeaders.put("X-Forwarded-Host", forwardedHost);
Map<String,List<String>> respHeaders = new HashMap<>();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
respHeaders);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
if (expectResponseAuthHeaders) {
List<String> remoteUsers = respHeaders.get("remote-user");
Assert.assertNotNull(remoteUsers);
Assert.assertEquals(USER, remoteUsers.get(0));
List<String> authTypes = respHeaders.get("auth-type");
Assert.assertNotNull(authTypes);
Assert.assertEquals(HttpServletRequest.BASIC_AUTH, authTypes.get(0));
} else {
Assert.assertFalse(respHeaders.containsKey("remote-user"));
Assert.assertFalse(respHeaders.containsKey("auth-type"));
}
bc.recycle();
}
@Override
public void setUp() throws Exception {
super.setUp();
// Configure a context with digest auth and a single protected resource
Tomcat tomcat = getTomcatInstance();
tomcat.getHost().getPipeline().addValve(new RemoteIpValve());
// No file system docBase required
Context ctxt = tomcat.addContext(CONTEXT_PATH, null);
// Add protected servlet
Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet());
ctxt.addServletMappingDecoded(URI, "TesterServlet");
SecurityCollection collection = new SecurityCollection();
collection.addPatternDecoded(URI);
SecurityConstraint sc = new SecurityConstraint();
sc.addAuthRole(ROLE);
sc.addCollection(collection);
ctxt.addConstraint(sc);
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser(USER, PWD);
realm.addUserRole(USER, ROLE);
ctxt.setRealm(realm);
// Configure the authenticator
LoginConfig lc = new LoginConfig();
lc.setAuthMethod(HttpServletRequest.BASIC_AUTH);
ctxt.setLoginConfig(lc);
ctxt.getPipeline().addValve(new BasicAuthenticator());
}
}

View File

@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.Context;
import org.apache.catalina.Realm;
import org.apache.catalina.authenticator.AuthenticatorBase.AllowCorsPreflight;
import org.apache.catalina.filters.AddDefaultCharsetFilter;
import org.apache.catalina.filters.CorsFilter;
import org.apache.catalina.realm.NullRealm;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
@RunWith(Parameterized.class)
public class TestAuthenticatorBaseCorsPreflight extends TomcatBaseTest {
private static final String ALLOWED_ORIGIN = "http://example.com";
private static final String EMPTY_ORIGIN = "";
private static final String INVALID_ORIGIN = "http://%20";
private static final String SAME_ORIGIN = "http://localhost";
private static final String ALLOWED_METHOD = "GET";
private static final String BLOCKED_METHOD = "POST";
private static final String EMPTY_METHOD = "";
@Parameterized.Parameters(name = "{index}: input[{0}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
parameterSets.add(new Object[] { AllowCorsPreflight.NEVER, "/*", "OPTIONS", null, null, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", null, null, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.TRUE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", EMPTY_ORIGIN, ALLOWED_METHOD, Boolean.FALSE});
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", INVALID_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", SAME_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "GET", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, BLOCKED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, EMPTY_METHOD, Boolean.FALSE});
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, null, Boolean.FALSE});
parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/*", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.TRUE });
parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/x", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
return parameterSets;
}
@Parameter(0)
public AllowCorsPreflight allowCorsPreflight;
@Parameter(1)
public String filterMapping;
@Parameter(2)
public String method;
@Parameter(3)
public String origin;
@Parameter(4)
public String accessControl;
@Parameter(5)
public boolean allow;
@BeforeClass
public static void init() {
// So the test can set the origin header
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
}
@Test
public void test() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp");
Context ctx = tomcat.addContext("", appDir.getAbsolutePath());
Tomcat.addServlet(ctx, "default", new DefaultServlet());
ctx.addServletMappingDecoded("/", "default");
LoginConfig loginConfig = new LoginConfig();
loginConfig.setAuthMethod("BASIC");
ctx.setLoginConfig(loginConfig);
BasicAuthenticator basicAuth = new BasicAuthenticator();
basicAuth.setAllowCorsPreflight(allowCorsPreflight.toString());
ctx.getPipeline().addValve(basicAuth);
Realm realm = new NullRealm();
ctx.setRealm(realm);
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.addPattern("/*");
SecurityConstraint constraint = new SecurityConstraint();
constraint.setAuthConstraint(true);
constraint.addCollection(securityCollection);
ctx.addConstraint(constraint);
// For code coverage
FilterDef otherFilter = new FilterDef();
otherFilter.setFilterName("other");
otherFilter.setFilterClass(AddDefaultCharsetFilter.class.getName());
FilterMap otherMap = new FilterMap();
otherMap.setFilterName("other");
otherMap.addURLPatternDecoded("/other");
ctx.addFilterDef(otherFilter);
ctx.addFilterMap(otherMap);
FilterDef corsFilter = new FilterDef();
corsFilter.setFilterName("cors");
corsFilter.setFilterClass(CorsFilter.class.getName());
corsFilter.addInitParameter(CorsFilter.PARAM_CORS_ALLOWED_ORIGINS, ALLOWED_ORIGIN);
corsFilter.addInitParameter(CorsFilter.PARAM_CORS_ALLOWED_METHODS, ALLOWED_METHOD);
FilterMap corsFilterMap = new FilterMap();
corsFilterMap.setFilterName("cors");
corsFilterMap.addURLPatternDecoded(filterMapping);
ctx.addFilterDef(corsFilter);
ctx.addFilterMap(corsFilterMap);
tomcat.start();
Map<String,List<String>> reqHead = new HashMap<>();
if (origin != null) {
List<String> values = new ArrayList<>();
if (SAME_ORIGIN.equals(origin)) {
values.add(origin + ":" + getPort());
} else {
values.add(origin);
}
reqHead.put(CorsFilter.REQUEST_HEADER_ORIGIN, values);
}
if (accessControl != null) {
List<String> values = new ArrayList<>();
values.add(accessControl);
reqHead.put(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, values);
}
ByteChunk out = new ByteChunk();
int rc = methodUrl("http://localhost:" + getPort() + "/target", out, 300000, reqHead, null, method, false);
if (allow) {
Assert.assertEquals(200, rc);
} else {
Assert.assertEquals(403, rc);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,397 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.unittest.TesterContext;
import org.apache.tomcat.unittest.TesterServletContext;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.security.ConcurrentMessageDigest;
import org.apache.tomcat.util.security.MD5Encoder;
public class TestDigestAuthenticator extends TomcatBaseTest {
private static String USER = "user";
private static String PWD = "pwd";
private static String ROLE = "role";
private static String URI = "/protected";
private static String QUERY = "?foo=bar";
private static String CONTEXT_PATH = "/foo";
private static String CLIENT_AUTH_HEADER = "authorization";
private static String REALM = "TestRealm";
private static String CNONCE = "cnonce";
private static String NC1 = "00000001";
private static String NC2 = "00000002";
private static String QOP = "auth";
@Test
public void bug54521() throws LifecycleException {
DigestAuthenticator digestAuthenticator = new DigestAuthenticator();
TesterContext context = new TesterContext();
context.setServletContext(new TesterServletContext());
digestAuthenticator.setContainer(context);
digestAuthenticator.start();
Request request = new TesterRequest();
final int count = 1000;
Set<String> nonces = new HashSet<>();
for (int i = 0; i < count; i++) {
nonces.add(digestAuthenticator.generateNonce(request));
}
Assert.assertEquals(count, nonces.size());
}
@Test
public void testAllValid() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, CNONCE, QOP, true, true);
}
@Test
public void testValidNoQop() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, null, null, true, true);
}
@Test
public void testValidQuery() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI + QUERY, false, true, REALM, true,
true, NC1, NC2, CNONCE, QOP, true, true);
}
@Test
public void testInvalidUriFail() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, true, true, REALM, true, true,
NC1, NC2, CNONCE, QOP, false, false);
}
@Test
public void testInvalidUriPass() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, true, false, REALM, true, true,
NC1, NC2, CNONCE, QOP, true, true);
}
@Test
public void testInvalidRealm() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, "null", true, true,
NC1, NC2, CNONCE, QOP, false, false);
}
@Test
public void testInvalidNonce() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, false, true,
NC1, NC2, CNONCE, QOP, false, true);
}
@Test
public void testInvalidOpaque() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, false,
NC1, NC2, CNONCE, QOP, false, true);
}
@Test
public void testInvalidNc1() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
"null", null, CNONCE, QOP, false, false);
}
@Test
public void testInvalidQop() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, CNONCE, "null", false, false);
}
@Test
public void testInvalidQopCombo1() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, CNONCE, null, false, false);
}
@Test
public void testInvalidQopCombo2() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, null, QOP, false, false);
}
@Test
public void testInvalidQopCombo3() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, null, null, false, false);
}
@Test
public void testInvalidQopCombo4() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, CNONCE, QOP, false, false);
}
@Test
public void testInvalidQopCombo5() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, CNONCE, null, false, false);
}
@Test
public void testInvalidQopCombo6() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, null, QOP, false, false);
}
@Test
public void testReplay() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC1, CNONCE, QOP, true, false);
}
public void doTest(String user, String pwd, String uri, boolean breakUri,
boolean validateUri, String realm, boolean useServerNonce,
boolean useServerOpaque, String nc1, String nc2, String cnonce,
String qop, boolean req2expect200, boolean req3expect200)
throws Exception {
if (!validateUri) {
DigestAuthenticator auth =
(DigestAuthenticator) getTomcatInstance().getHost().findChild(
CONTEXT_PATH).getPipeline().getFirst();
auth.setValidateUri(false);
}
getTomcatInstance().start();
String digestUri;
if (breakUri) {
digestUri = "/broken" + uri;
} else {
digestUri = uri;
}
List<String> auth = new ArrayList<>();
auth.add(buildDigestResponse(user, pwd, digestUri, realm, "null",
"null", nc1, cnonce, qop));
Map<String,List<String>> reqHeaders = new HashMap<>();
reqHeaders.put(CLIENT_AUTH_HEADER, auth);
Map<String,List<String>> respHeaders = new HashMap<>();
// The first request will fail - but we need to extract the nonce
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
respHeaders);
Assert.assertEquals(401, rc);
Assert.assertTrue(bc.getLength() > 0);
bc.recycle();
// Second request should succeed (if we use the server nonce)
auth.clear();
if (useServerNonce) {
if (useServerOpaque) {
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), getOpaque(respHeaders), nc1,
cnonce, qop));
} else {
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), "null", nc1, cnonce, qop));
}
} else {
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
"null", getOpaque(respHeaders), nc1, cnonce, QOP));
}
rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
null);
if (req2expect200) {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
} else {
Assert.assertEquals(401, rc);
Assert.assertTrue(bc.getLength() > 0);
}
// Third request should succeed if we increment nc
auth.clear();
bc.recycle();
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), getOpaque(respHeaders), nc2, cnonce,
qop));
rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
null);
if (req3expect200) {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
} else {
Assert.assertEquals(401, rc);
Assert.assertTrue(bc.getLength() > 0);
}
}
@Override
public void setUp() throws Exception {
super.setUp();
// Configure a context with digest auth and a single protected resource
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctxt = tomcat.addContext(CONTEXT_PATH, null);
// Add protected servlet
Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet());
ctxt.addServletMappingDecoded(URI, "TesterServlet");
SecurityCollection collection = new SecurityCollection();
collection.addPatternDecoded(URI);
SecurityConstraint sc = new SecurityConstraint();
sc.addAuthRole(ROLE);
sc.addCollection(collection);
ctxt.addConstraint(sc);
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser(USER, PWD);
realm.addUserRole(USER, ROLE);
ctxt.setRealm(realm);
// Configure the authenticator
LoginConfig lc = new LoginConfig();
lc.setAuthMethod("DIGEST");
lc.setRealmName(REALM);
ctxt.setLoginConfig(lc);
ctxt.getPipeline().addValve(new DigestAuthenticator());
}
protected static String getNonce(Map<String,List<String>> respHeaders) {
List<String> authHeaders =
respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME);
// Assume there is only one
String authHeader = authHeaders.iterator().next();
int start = authHeader.indexOf("nonce=\"") + 7;
int end = authHeader.indexOf('\"', start);
return authHeader.substring(start, end);
}
protected static String getOpaque(Map<String,List<String>> respHeaders) {
List<String> authHeaders =
respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME);
// Assume there is only one
String authHeader = authHeaders.iterator().next();
int start = authHeader.indexOf("opaque=\"") + 8;
int end = authHeader.indexOf('\"', start);
return authHeader.substring(start, end);
}
/*
* Notes from RFC2617
* H(data) = MD5(data)
* KD(secret, data) = H(concat(secret, ":", data))
* A1 = unq(username-value) ":" unq(realm-value) ":" passwd
* A2 = Method ":" digest-uri-value
* request-digest = <"> < KD ( H(A1), unq(nonce-value)
":" nc-value
":" unq(cnonce-value)
":" unq(qop-value)
":" H(A2)
) <">
*/
private static String buildDigestResponse(String user, String pwd,
String uri, String realm, String nonce, String opaque, String nc,
String cnonce, String qop) {
String a1 = user + ":" + realm + ":" + pwd;
String a2 = "GET:" + uri;
String md5a1 = digest(a1);
String md5a2 = digest(a2);
String response;
if (qop == null) {
response = md5a1 + ":" + nonce + ":" + md5a2;
} else {
response = md5a1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" +
qop + ":" + md5a2;
}
String md5response = digest(response);
StringBuilder auth = new StringBuilder();
auth.append("Digest username=\"");
auth.append(user);
auth.append("\", realm=\"");
auth.append(realm);
auth.append("\", nonce=\"");
auth.append(nonce);
auth.append("\", uri=\"");
auth.append(uri);
auth.append("\", opaque=\"");
auth.append(opaque);
auth.append("\", response=\"");
auth.append(md5response);
auth.append("\"");
if (qop != null) {
auth.append(", qop=");
auth.append(qop);
auth.append("");
}
if (nc != null) {
auth.append(", nc=");
auth.append(nc);
}
if (cnonce != null) {
auth.append(", cnonce=\"");
auth.append(cnonce);
auth.append("\"");
}
return auth.toString();
}
private static String digest(String input) {
return MD5Encoder.encode(
ConcurrentMessageDigest.digestMD5(input.getBytes()));
}
private static class TesterRequest extends Request {
@Override
public String getRemoteAddr() {
return "127.0.0.1";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl;
import org.apache.catalina.connector.Request;
public class TestJaspicCallbackHandlerInAuthenticator {
@Test
public void testCustomCallbackHandlerCreation() throws Exception {
testCallbackHandlerCreation("org.apache.catalina.authenticator.TestCallbackHandlerImpl",
TestCallbackHandlerImpl.class);
}
@Test
public void testDefaultCallbackHandlerCreation() throws Exception {
testCallbackHandlerCreation(null, CallbackHandlerImpl.class);
}
private void testCallbackHandlerCreation(String callbackHandlerImplClassName,
Class<?> callbackHandlerImplClass)
throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
TestAuthenticator authenticator = new TestAuthenticator();
authenticator.setJaspicCallbackHandlerClass(callbackHandlerImplClassName);
Method createCallbackHandlerMethod =
AuthenticatorBase.class.getDeclaredMethod("createCallbackHandler");
createCallbackHandlerMethod.setAccessible(true);
CallbackHandler callbackHandler =
(CallbackHandler) createCallbackHandlerMethod.invoke(authenticator);
Assert.assertTrue(callbackHandlerImplClass.isInstance(callbackHandler));
}
private static class TestAuthenticator extends AuthenticatorBase {
@Override
protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
return false;
}
@Override
protected String getAuthMethod() {
return null;
}
}
}
class TestCallbackHandlerImpl implements CallbackHandler {
public TestCallbackHandlerImpl() {
// Default constructor required by reflection
}
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
// don't have to do anything; needed only for instantiation
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.filters.TesterHttpServletResponse;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.security.ConcurrentMessageDigest;
import org.apache.tomcat.util.security.MD5Encoder;
public class TesterDigestAuthenticatorPerformance {
private static String USER = "user";
private static String PWD = "pwd";
private static String ROLE = "role";
private static String METHOD = "GET";
private static String URI = "/protected";
private static String CONTEXT_PATH = "/foo";
private static String CLIENT_AUTH_HEADER = "authorization";
private static String REALM = "TestRealm";
private static String QOP = "auth";
private static final AtomicInteger nonceCount = new AtomicInteger(0);
private DigestAuthenticator authenticator = new DigestAuthenticator();
@Test
public void testSimple() throws Exception {
doTest(4, 1000000);
}
public void doTest(int threadCount, int requestCount) throws Exception {
TesterRunnable runnables[] = new TesterRunnable[threadCount];
Thread threads[] = new Thread[threadCount];
String nonce = authenticator.generateNonce(new TesterDigestRequest());
// Create the runnables & threads
for (int i = 0; i < threadCount; i++) {
runnables[i] =
new TesterRunnable(authenticator, nonce, requestCount);
threads[i] = new Thread(runnables[i]);
}
long start = System.currentTimeMillis();
// Start the threads
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
// Wait for the threads to finish
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
double wallTime = System.currentTimeMillis() - start;
// Gather the results...
double totalTime = 0;
int totalSuccess = 0;
for (int i = 0; i < threadCount; i++) {
System.out.println("Thread: " + i + " Success: " +
runnables[i].getSuccess());
totalSuccess = totalSuccess + runnables[i].getSuccess();
totalTime = totalTime + runnables[i].getTime();
}
System.out.println("Average time per request (user): " +
totalTime/(threadCount * requestCount));
System.out.println("Average time per request (wall): " +
wallTime/(threadCount * requestCount));
Assert.assertEquals(((long)requestCount) * threadCount, totalSuccess);
}
@Before
public void setUp() throws Exception {
ConcurrentMessageDigest.init("MD5");
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser(USER, PWD);
realm.addUserRole(USER, ROLE);
// Add the Realm to the Context
Context context = new StandardContext();
context.setName(CONTEXT_PATH);
context.setRealm(realm);
// Configure the Login config
LoginConfig config = new LoginConfig();
config.setRealmName(REALM);
context.setLoginConfig(config);
// Make the Context and Realm visible to the Authenticator
authenticator.setContainer(context);
authenticator.setNonceCountWindowSize(8 * 1024);
authenticator.start();
}
private static class TesterRunnable implements Runnable {
private String nonce;
private int requestCount;
private int success = 0;
private long time = 0;
private TesterDigestRequest request;
private HttpServletResponse response;
private DigestAuthenticator authenticator;
private static final String A1 = USER + ":" + REALM + ":" + PWD;
private static final String A2 = METHOD + ":" + CONTEXT_PATH + URI;
private static final String MD5A1 = MD5Encoder.encode(
ConcurrentMessageDigest.digest("MD5", A1.getBytes()));
private static final String MD5A2 = MD5Encoder.encode(
ConcurrentMessageDigest.digest("MD5", A2.getBytes()));
// All init code should be in here. run() needs to be quick
public TesterRunnable(DigestAuthenticator authenticator,
String nonce, int requestCount) throws Exception {
this.authenticator = authenticator;
this.nonce = nonce;
this.requestCount = requestCount;
request = new TesterDigestRequest();
request.getMappingData().context = authenticator.context;
response = new TesterHttpServletResponse();
}
@Override
public void run() {
long start = System.currentTimeMillis();
for (int i = 0; i < requestCount; i++) {
try {
request.setAuthHeader(buildDigestResponse(nonce));
if (authenticator.authenticate(request, response)) {
success++;
}
// Clear out authenticated user ready for next iteration
request.setUserPrincipal(null);
} catch (IOException ioe) {
// Ignore
}
}
time = System.currentTimeMillis() - start;
}
public int getSuccess() {
return success;
}
public long getTime() {
return time;
}
private String buildDigestResponse(String nonce) {
String ncString = String.format("%1$08x",
Integer.valueOf(nonceCount.incrementAndGet()));
String cnonce = "cnonce";
String response = MD5A1 + ":" + nonce + ":" + ncString + ":" +
cnonce + ":" + QOP + ":" + MD5A2;
String md5response = MD5Encoder.encode(
ConcurrentMessageDigest.digest("MD5", response.getBytes()));
StringBuilder auth = new StringBuilder();
auth.append("Digest username=\"");
auth.append(USER);
auth.append("\", realm=\"");
auth.append(REALM);
auth.append("\", nonce=\"");
auth.append(nonce);
auth.append("\", uri=\"");
auth.append(CONTEXT_PATH + URI);
auth.append("\", opaque=\"");
auth.append(authenticator.getOpaque());
auth.append("\", response=\"");
auth.append(md5response);
auth.append("\"");
auth.append(", qop=");
auth.append(QOP);
auth.append(", nc=");
auth.append(ncString);
auth.append(", cnonce=\"");
auth.append(cnonce);
auth.append("\"");
return auth.toString();
}
}
private static class TesterDigestRequest extends Request {
private String authHeader = null;
@Override
public String getRemoteAddr() {
return "127.0.0.1";
}
public void setAuthHeader(String authHeader) {
this.authHeader = authHeader;
}
@Override
public String getHeader(String name) {
if (CLIENT_AUTH_HEADER.equalsIgnoreCase(name)) {
return authHeader;
} else {
return super.getHeader(name);
}
}
@Override
public String getMethod() {
return METHOD;
}
@Override
public String getQueryString() {
return null;
}
@Override
public String getRequestURI() {
return CONTEXT_PATH + URI;
}
@Override
public org.apache.coyote.Request getCoyoteRequest() {
return new org.apache.coyote.Request();
}
}
}

View File

@@ -0,0 +1,446 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator.jaspic;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.security.auth.message.config.AuthConfigFactory;
import javax.security.auth.message.config.AuthConfigProvider;
import javax.security.auth.message.config.RegistrationListener;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.Globals;
public class TestAuthConfigFactoryImpl {
private String oldCatalinaBase;
private static final File TEST_CONFIG_FILE = new File("test/conf/jaspic-providers.xml");
@Test
public void testRegistrationNullLayer() {
doTestResistration(null, "AC_1", ":AC_1");
}
@Test
public void testRegistrationNullAppContext() {
doTestResistration("L_1", null, "L_1:");
}
@Test
public void testRegistrationNullLayerAndNullAppContext() {
doTestResistration(null, null, ":");
}
@Test
public void testSearchNoMatch01() {
doTestSearchOrder("foo", "bar", 1);
}
@Test
public void testSearchNoMatch02() {
doTestSearchOrder(null, "bar", 1);
}
@Test
public void testSearchNoMatch03() {
doTestSearchOrder("foo", null, 1);
}
@Test
public void testSearchNoMatch04() {
doTestSearchOrder(null, null, 1);
}
@Test
public void testSearchOnlyAppContextMatch01() {
doTestSearchOrder("foo", "AC_1", 2);
}
@Test
public void testSearchOnlyAppContextMatch02() {
doTestSearchOrder(null, "AC_1", 2);
}
@Test
public void testSearchOnlyAppContextMatch03() {
doTestSearchOrder("L_2", "AC_1", 2);
}
@Test
public void testSearchOnlyLayerMatch01() {
doTestSearchOrder("L_1", "bar", 3);
}
@Test
public void testSearchOnlyLayerMatch02() {
doTestSearchOrder("L_1", null, 3);
}
@Test
public void testSearchOnlyLayerMatch03() {
doTestSearchOrder("L_1", "AC_2", 3);
}
@Test
public void testSearchBothMatch() {
doTestSearchOrder("L_2", "AC_2", 4);
}
private void doTestSearchOrder(String layer, String appContext, int expected) {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp1, null, null, "1");
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp2, null, "AC_1", "2");
AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp3, "L_1", null, "3");
AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp4, "L_2", "AC_2", "4");
AuthConfigProvider searchResult = factory.getConfigProvider(layer, appContext, null);
int searchIndex;
if (searchResult == acp1) {
searchIndex = 1;
} else if (searchResult == acp2) {
searchIndex = 2;
} else if (searchResult == acp3) {
searchIndex = 3;
} else if (searchResult == acp4) {
searchIndex = 4;
} else {
searchIndex = -1;
}
Assert.assertEquals(expected, searchIndex);
}
private void doTestResistration(String layer, String appContext, String expectedRegId) {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
SimpleRegistrationListener listener = new SimpleRegistrationListener(layer, appContext);
String regId = factory.registerConfigProvider(acp1, layer, appContext, null);
Assert.assertEquals(expectedRegId, regId);
factory.getConfigProvider(layer, appContext, listener);
factory.removeRegistration(regId);
Assert.assertTrue(listener.wasCorrectlyCalled());
listener.reset();
factory.registerConfigProvider(acp1, layer, appContext, null);
factory.getConfigProvider(layer, appContext, listener);
// Replace it
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp2, layer, appContext, null);
Assert.assertTrue(listener.wasCorrectlyCalled());
}
@Test
public void testRegistrationInsertExact01() {
doTestRegistrationInsert("L_3", "AC_2", "L_3", "AC_2");
}
@Test
public void testRegistrationInsertExact02() {
doTestRegistrationInsert("L_2", "AC_3", "L_2", "AC_3");
}
@Test
public void testRegistrationInsertExact03() {
doTestRegistrationInsert("L_4", "AC_4", "L_4", "AC_4");
}
@Test
public void testRegistrationInsertAppContext01() {
doTestRegistrationInsert(null, "AC_3", "L_2", "AC_3");
}
@Test
public void testRegistrationInsertAppContext02() {
doTestRegistrationInsert(null, "AC_4", "L_4", "AC_4");
}
@Test
public void testRegistrationInsertLayer01() {
doTestRegistrationInsert("L_4", null, "L_4", "AC_4");
}
private void doTestRegistrationInsert(String newLayer, String newAppContext,
String expectedListenerLayer, String expectedListenerAppContext) {
// Set up
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp2, null, "AC_2", null);
AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp3, "L_2", null, null);
AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp4, null, null, null);
SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
factory.getConfigProvider("L_1", "AC_1", listener1);
SimpleRegistrationListener listener2 = new SimpleRegistrationListener("L_3", "AC_2");
factory.getConfigProvider("L_3", "AC_2", listener2);
SimpleRegistrationListener listener3 = new SimpleRegistrationListener("L_2", "AC_3");
factory.getConfigProvider("L_2", "AC_3", listener3);
SimpleRegistrationListener listener4 = new SimpleRegistrationListener("L_4", "AC_4");
factory.getConfigProvider("L_4", "AC_4", listener4);
List<SimpleRegistrationListener> listeners = new ArrayList<>();
listeners.add(listener1);
listeners.add(listener2);
listeners.add(listener3);
listeners.add(listener4);
// Register a new provider that will impact some existing registrations
AuthConfigProvider acpNew = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acpNew, newLayer, newAppContext, null);
// Check to see if the expected listener fired.
for (SimpleRegistrationListener listener : listeners) {
if (listener.wasCalled()) {
Assert.assertEquals(listener.layer, expectedListenerLayer);
Assert.assertEquals(listener.appContext, expectedListenerAppContext);
Assert.assertTrue(listener.wasCorrectlyCalled());
} else {
Assert.assertFalse((listener.layer.equals(expectedListenerLayer) &&
listener.appContext.equals(expectedListenerAppContext)));
}
}
}
@Test
public void testDetachListenerNonexistingRegistration() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
factory.getConfigProvider("L_1", "AC_1", listener1);
factory.removeRegistration(registrationId);
String[] registrationIds = factory.detachListener(listener1, "L_1", "AC_1");
Assert.assertTrue(registrationIds.length == 0);
}
@Test
public void testDetachListener() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
factory.getConfigProvider("L_1", "AC_1", listener1);
String[] registrationIds = factory.detachListener(listener1, "L_1", "AC_1");
Assert.assertTrue(registrationIds.length == 1);
Assert.assertEquals(registrationId, registrationIds[0]);
}
@Test
public void testRegistrationNullListener() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
factory.getConfigProvider("L_1", "AC_1", null);
boolean result = factory.removeRegistration(registrationId);
Assert.assertTrue(result);
}
@Test
public void testAllRegistrationIds() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId1 = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
String registrationId2 = factory.registerConfigProvider(acp2, "L_2", "AC_2", null);
String[] registrationIds = factory.getRegistrationIDs(null);
Assert.assertTrue(registrationIds.length == 2);
Set<String> ids = new HashSet<>(Arrays.asList(registrationIds));
Assert.assertTrue(ids.contains(registrationId1));
Assert.assertTrue(ids.contains(registrationId2));
}
@Before
public void setUp() {
// set CATALINA_BASE to test so that the file with persistent providers will be written in test/conf folder
oldCatalinaBase = System.getProperty(Globals.CATALINA_BASE_PROP);
System.setProperty(Globals.CATALINA_BASE_PROP, "test");
if (TEST_CONFIG_FILE.exists()) {
if (!TEST_CONFIG_FILE.delete()) {
Assert.fail("Failed to delete " + TEST_CONFIG_FILE);
}
}
}
@After
public void cleanUp() {
if (oldCatalinaBase != null ) {
System.setProperty(Globals.CATALINA_BASE_PROP, oldCatalinaBase);
} else {
System.clearProperty(Globals.CATALINA_BASE_PROP);
}
if (TEST_CONFIG_FILE.exists()) {
if (!TEST_CONFIG_FILE.delete()) {
Assert.fail("Failed to delete " + TEST_CONFIG_FILE);
}
}
}
@Test
public void testRemovePersistentRegistration() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
factory.registerConfigProvider(
SimpleAuthConfigProvider.class.getName(), null, "L_1", "AC_1", null);
String registrationId2 = factory.registerConfigProvider(
SimpleAuthConfigProvider.class.getName(), null, "L_2", "AC_2", null);
factory.removeRegistration(registrationId2);
factory.refresh();
String[] registrationIds = factory.getRegistrationIDs(null);
for (String registrationId : registrationIds) {
Assert.assertNotEquals(registrationId2, registrationId);
}
}
@Test
public void testRegistrationNullClassName() {
doTestNullClassName(false, "L_1", "AC_1");
}
@Test
public void testRegistrationNullClassOverrideExisting() {
doTestNullClassName(true, "L_1", "AC_1");
}
@Test
public void testRegistrationNullClassNullLayerNullAppContext() {
doTestNullClassName(false, null, null);
}
private void doTestNullClassName(boolean shouldOverrideExistingProvider, String layer, String appContext) {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
if (shouldOverrideExistingProvider) {
factory.registerConfigProvider(SimpleAuthConfigProvider.class.getName(), null, layer, appContext, null);
}
String registrationId = factory.registerConfigProvider(null, null, layer, appContext, null);
factory.refresh();
String[] registrationIds = factory.getRegistrationIDs(null);
Set<String> ids = new HashSet<>(Arrays.asList(registrationIds));
Assert.assertTrue(ids.contains(registrationId));
AuthConfigProvider provider = factory.getConfigProvider(layer, appContext, null);
Assert.assertNull(provider);
}
private static class SimpleRegistrationListener implements RegistrationListener {
private final String layer;
private final String appContext;
private boolean called = false;
private String layerNotified;
private String appContextNotified;
public SimpleRegistrationListener(String layer, String appContext) {
this.layer = layer;
this.appContext = appContext;
}
@Override
public void notify(String layer, String appContext) {
called = true;
layerNotified = layer;
appContextNotified = appContext;
}
public boolean wasCalled() {
return called;
}
public boolean wasCorrectlyCalled() {
return called && areTheSame(layer, layerNotified) &&
areTheSame(appContext, appContextNotified);
}
public void reset() {
called = false;
layerNotified = null;
appContextNotified = null;
}
private static boolean areTheSame(String a, String b) {
if (a == null) {
return b == null;
}
return a.equals(b);
}
}
}

View File

@@ -0,0 +1,135 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator.jaspic;
import java.io.File;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Provider;
import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Providers;
public class TestPersistentProviderRegistrations {
@Test
public void testLoadEmpty() {
File f = new File("test/conf/jaspic-test-01.xml");
Providers result = PersistentProviderRegistrations.loadProviders(f);
Assert.assertEquals(0, result.getProviders().size());
}
@Test
public void testLoadSimple() {
File f = new File("test/conf/jaspic-test-02.xml");
Providers result = PersistentProviderRegistrations.loadProviders(f);
validateSimple(result);
}
private void validateSimple(Providers providers) {
Assert.assertEquals(1, providers.getProviders().size());
Provider p = providers.getProviders().get(0);
Assert.assertEquals("a", p.getClassName());
Assert.assertEquals("b", p.getLayer());
Assert.assertEquals("c", p.getAppContext());
Assert.assertEquals("d", p.getDescription());
Assert.assertEquals(2, p.getProperties().size());
Assert.assertEquals("f", p.getProperties().get("e"));
Assert.assertEquals("h", p.getProperties().get("g"));
}
@Test
public void testSaveSimple() {
File f = new File("test/conf/jaspic-test-03.xml");
if (f.exists()) {
Assert.assertTrue(f.delete());
}
// Create a config and write it out
Providers start = new Providers();
Provider p = new Provider();
p.setClassName("a");
p.setLayer("b");
p.setAppContext("c");
p.setDescription("d");
p.addProperty("e", "f");
p.addProperty("g", "h");
start.addProvider(p);
PersistentProviderRegistrations.writeProviders(start, f);
// Read it back
Providers end = PersistentProviderRegistrations.loadProviders(f);
validateSimple(end);
if (f.exists()) {
Assert.assertTrue("Failed to clean up [" + f + "]", f.delete());
}
}
@Test
public void testLoadProviderWithoutLayerAndAC() {
File f = new File("test/conf/jaspic-test-04.xml");
Providers providers = PersistentProviderRegistrations.loadProviders(f);
validateNoLayerAndAC(providers);
}
private void validateNoLayerAndAC(Providers providers) {
Assert.assertEquals(1, providers.getProviders().size());
Provider p = providers.getProviders().get(0);
Assert.assertEquals("a", p.getClassName());
Assert.assertNull(p.getLayer());
Assert.assertNull(p.getAppContext());
Assert.assertEquals("d", p.getDescription());
}
@Test
public void testSaveProviderWithoutLayerAndAC() {
File f = new File("test/conf/jaspic-test-05.xml");
if (f.exists()) {
Assert.assertTrue(f.delete());
}
// Create a config and write it out
Providers initialProviders = new Providers();
Provider p = new Provider();
p.setClassName("a");
p.setDescription("d");
initialProviders.addProvider(p);
PersistentProviderRegistrations.writeProviders(initialProviders, f);
// Read it back
Providers loadedProviders = PersistentProviderRegistrations.loadProviders(f);
try {
validateNoLayerAndAC(loadedProviders);
} finally {
if (f.exists()) {
if (!f.delete()) {
Assert.fail("Failed to delete " + f);
}
}
}
}
}

View File

@@ -0,0 +1,74 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator.jaspic;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
import org.junit.Assert;
import org.junit.Test;
public class TestSimpleServerAuthConfig {
private static final String SERVER_AUTH_MODULE_KEY_PREFIX =
"org.apache.catalina.authenticator.jaspic.ServerAuthModule.";
private static final Map<String,String> CONFIG_PROPERTIES;
static {
CONFIG_PROPERTIES = new HashMap<>();
CONFIG_PROPERTIES.put(SERVER_AUTH_MODULE_KEY_PREFIX + "1",
TesterServerAuthModuleA.class.getName());
}
@Test
public void testConfigOnServerAuthConfig() throws Exception {
ServerAuthConfig serverAuthConfig =
new SimpleServerAuthConfig(null, null, null, CONFIG_PROPERTIES);
ServerAuthContext serverAuthContext = serverAuthConfig.getAuthContext(null, null, null);
validateServerAuthContext(serverAuthContext);
}
@Test
public void testConfigOnGetAuthContext() throws Exception {
ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null);
ServerAuthContext serverAuthContext =
serverAuthConfig.getAuthContext(null, null, CONFIG_PROPERTIES);
validateServerAuthContext(serverAuthContext);
}
@Test(expected=AuthException.class)
public void testConfigNone() throws Exception {
ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null);
serverAuthConfig.getAuthContext(null, null, null);
}
private void validateServerAuthContext(ServerAuthContext serverAuthContext) throws Exception {
MessageInfo msgInfo = new TesterMessageInfo();
serverAuthContext.cleanSubject(msgInfo, null);
Assert.assertEquals("init()-cleanSubject()-", msgInfo.getMap().get("trace"));
}
}

View File

@@ -0,0 +1,55 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator.jaspic;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.message.MessageInfo;
public class TesterMessageInfo implements MessageInfo {
private Object requestMessage;
private Object responseMessage;
private final Map<String,String> map = new HashMap<>();
@Override
public Object getRequestMessage() {
return requestMessage;
}
@Override
public Object getResponseMessage() {
return responseMessage;
}
@Override
public void setRequestMessage(Object request) {
requestMessage = request;
}
@Override
public void setResponseMessage(Object response) {
responseMessage = response;
}
@SuppressWarnings("rawtypes")
@Override
public Map getMap() {
return map;
}
}

View File

@@ -0,0 +1,64 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.authenticator.jaspic;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.module.ServerAuthModule;
public class TesterServerAuthModuleA implements ServerAuthModule {
private StringBuilder trace = new StringBuilder("init()-");
@Override
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
Subject serviceSubject) throws AuthException {
return null;
}
@Override
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
throws AuthException {
return null;
}
@SuppressWarnings("unchecked")
@Override
public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
trace.append("cleanSubject()-");
messageInfo.getMap().put("trace", trace.toString());
}
@Override
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
CallbackHandler handler, @SuppressWarnings("rawtypes") Map options)
throws AuthException {
// NO-OP
}
@SuppressWarnings("rawtypes")
@Override
public Class[] getSupportedMessageTypes() {
return null;
}
}