175 lines
6.5 KiB
Java
175 lines
6.5 KiB
Java
/*
|
|
* 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.realm;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.Principal;
|
|
|
|
import javax.naming.NamingEnumeration;
|
|
import javax.naming.NamingException;
|
|
import javax.naming.directory.BasicAttributes;
|
|
import javax.naming.directory.DirContext;
|
|
import javax.naming.directory.InitialDirContext;
|
|
import javax.naming.directory.SearchControls;
|
|
import javax.naming.directory.SearchResult;
|
|
|
|
import org.junit.Assert;
|
|
import org.junit.BeforeClass;
|
|
import org.junit.Test;
|
|
|
|
import org.apache.catalina.Context;
|
|
import org.apache.catalina.LifecycleException;
|
|
import org.apache.naming.NameParserImpl;
|
|
import org.apache.tomcat.unittest.TesterContext;
|
|
import org.apache.tomcat.util.security.MD5Encoder;
|
|
import org.easymock.EasyMock;
|
|
|
|
public class TestJNDIRealm {
|
|
|
|
private static final String ALGORITHM = "MD5";
|
|
|
|
private static final String USER = "test-user";
|
|
private static final String PASSWORD = "test-password";
|
|
private static final String REALM = "test-realm";
|
|
|
|
private static final String NONCE = "test-nonce";
|
|
private static final String HA2 = "test-md5a2";
|
|
public static final String USER_PASSWORD_ATTR = "test-pwd";
|
|
|
|
private static MessageDigest md5Helper;
|
|
|
|
@BeforeClass
|
|
public static void setupClass() throws Exception {
|
|
md5Helper = MessageDigest.getInstance(ALGORITHM);
|
|
}
|
|
|
|
@Test
|
|
public void testAuthenticateWithoutUserPassword() throws Exception {
|
|
// GIVEN
|
|
JNDIRealm realm = buildRealm(PASSWORD);
|
|
|
|
// WHEN
|
|
String expectedResponse =
|
|
MD5Encoder.encode(md5Helper.digest((ha1() + ":" + NONCE + ":" + HA2).getBytes()));
|
|
Principal principal =
|
|
realm.authenticate(USER, expectedResponse, NONCE, null, null, null, REALM, HA2);
|
|
|
|
// THEN
|
|
Assert.assertNull(principal);
|
|
}
|
|
|
|
@Test
|
|
public void testAuthenticateWithUserPassword() throws Exception {
|
|
// GIVEN
|
|
JNDIRealm realm = buildRealm(PASSWORD);
|
|
realm.setUserPassword(USER_PASSWORD_ATTR);
|
|
|
|
// WHEN
|
|
String expectedResponse =
|
|
MD5Encoder.encode(md5Helper.digest((ha1() + ":" + NONCE + ":" + HA2).getBytes()));
|
|
Principal principal =
|
|
realm.authenticate(USER, expectedResponse, NONCE, null, null, null, REALM, HA2);
|
|
|
|
// THEN
|
|
Assert.assertTrue(principal instanceof GenericPrincipal);
|
|
Assert.assertEquals(PASSWORD, ((GenericPrincipal)principal).getPassword());
|
|
}
|
|
|
|
@Test
|
|
public void testAuthenticateWithUserPasswordAndCredentialHandler() throws Exception {
|
|
// GIVEN
|
|
JNDIRealm realm = buildRealm(ha1());
|
|
realm.setCredentialHandler(buildCredentialHandler());
|
|
realm.setUserPassword(USER_PASSWORD_ATTR);
|
|
|
|
// WHEN
|
|
String expectedResponse =
|
|
MD5Encoder.encode(md5Helper.digest((ha1() + ":" + NONCE + ":" + HA2).getBytes()));
|
|
Principal principal =
|
|
realm.authenticate(USER, expectedResponse, NONCE, null, null, null, REALM, HA2);
|
|
|
|
// THEN
|
|
Assert.assertTrue(principal instanceof GenericPrincipal);
|
|
Assert.assertEquals(ha1(), ((GenericPrincipal)principal).getPassword());
|
|
}
|
|
|
|
|
|
private JNDIRealm buildRealm(String password) throws javax.naming.NamingException,
|
|
NoSuchFieldException, IllegalAccessException, LifecycleException {
|
|
Context context = new TesterContext();
|
|
JNDIRealm realm = new JNDIRealm();
|
|
realm.setContainer(context);
|
|
realm.setUserSearch("");
|
|
|
|
Field field = JNDIRealm.class.getDeclaredField("context");
|
|
field.setAccessible(true);
|
|
field.set(realm, mockDirContext(mockSearchResults(password)));
|
|
|
|
realm.start();
|
|
|
|
return realm;
|
|
}
|
|
|
|
private MessageDigestCredentialHandler buildCredentialHandler()
|
|
throws NoSuchAlgorithmException {
|
|
MessageDigestCredentialHandler credentialHandler = new MessageDigestCredentialHandler();
|
|
credentialHandler.setAlgorithm(ALGORITHM);
|
|
return credentialHandler;
|
|
}
|
|
|
|
private NamingEnumeration<SearchResult> mockSearchResults(String password)
|
|
throws NamingException {
|
|
@SuppressWarnings("unchecked")
|
|
NamingEnumeration<SearchResult> searchResults =
|
|
EasyMock.createNiceMock(NamingEnumeration.class);
|
|
EasyMock.expect(Boolean.valueOf(searchResults.hasMore()))
|
|
.andReturn(Boolean.TRUE)
|
|
.andReturn(Boolean.FALSE)
|
|
.andReturn(Boolean.TRUE)
|
|
.andReturn(Boolean.FALSE);
|
|
EasyMock.expect(searchResults.next())
|
|
.andReturn(new SearchResult("ANY RESULT", "",
|
|
new BasicAttributes(USER_PASSWORD_ATTR, password)))
|
|
.times(2);
|
|
EasyMock.replay(searchResults);
|
|
return searchResults;
|
|
}
|
|
|
|
private DirContext mockDirContext(NamingEnumeration<SearchResult> namingEnumeration)
|
|
throws NamingException {
|
|
DirContext dirContext = EasyMock.createNiceMock(InitialDirContext.class);
|
|
EasyMock.expect(dirContext.search(EasyMock.anyString(), EasyMock.anyString(),
|
|
EasyMock.anyObject(SearchControls.class)))
|
|
.andReturn(namingEnumeration)
|
|
.times(2);
|
|
EasyMock.expect(dirContext.getNameParser(""))
|
|
.andReturn(new NameParserImpl()).times(2);
|
|
EasyMock.expect(dirContext.getNameInNamespace())
|
|
.andReturn("ANY NAME")
|
|
.times(2);
|
|
EasyMock.replay(dirContext);
|
|
return dirContext;
|
|
}
|
|
|
|
private String ha1() {
|
|
String a1 = USER + ":" + REALM + ":" + PASSWORD;
|
|
return MD5Encoder.encode(md5Helper.digest(a1.getBytes()));
|
|
}
|
|
}
|