Remove dependency on commons-codec by using java.util.Base64

Closes gh-11318
This commit is contained in:
j3graham 2022-06-01 14:32:55 -04:00 committed by Josh Cummings
parent cf69cdf008
commit 29ba67b6d7
11 changed files with 156 additions and 55 deletions

View File

@ -73,12 +73,6 @@ updateDependenciesSettings {
alphaBetaVersions() alphaBetaVersions()
snapshotVersions() snapshotVersions()
addRule { components -> addRule { components ->
components.withModule("commons-codec:commons-codec") { selection ->
ModuleComponentIdentifier candidate = selection.getCandidate();
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
selection.reject("commons-codec updates break saml tests");
}
}
components.withModule("org.python:jython") { selection -> components.withModule("org.python:jython") { selection ->
ModuleComponentIdentifier candidate = selection.getCandidate(); ModuleComponentIdentifier candidate = selection.getCandidate();
if (!candidate.getVersion().equals(selection.getCurrentVersion())) { if (!candidate.getVersion().equals(selection.getCurrentVersion())) {

View File

@ -23,7 +23,6 @@ dependencies {
api "com.squareup.okhttp3:mockwebserver:3.14.9" api "com.squareup.okhttp3:mockwebserver:3.14.9"
api "com.squareup.okhttp3:okhttp:3.14.9" api "com.squareup.okhttp3:okhttp:3.14.9"
api "com.unboundid:unboundid-ldapsdk:4.0.14" api "com.unboundid:unboundid-ldapsdk:4.0.14"
api "commons-codec:commons-codec:1.15"
api "commons-collections:commons-collections:3.2.2" api "commons-collections:commons-collections:3.2.2"
api "io.mockk:mockk:1.12.3" api "io.mockk:mockk:1.12.3"
api "io.projectreactor.tools:blockhound:1.0.6.RELEASE" api "io.projectreactor.tools:blockhound:1.0.6.RELEASE"

View File

@ -15,7 +15,6 @@ dependencies {
optional 'jakarta.servlet:jakarta.servlet-api' optional 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(path: ':spring-security-core', configuration: 'tests') testImplementation project(path: ':spring-security-core', configuration: 'tests')
testImplementation 'commons-codec:commons-codec'
testImplementation "org.assertj:assertj-core" testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params" testImplementation "org.junit.jupiter:junit-jupiter-params"

View File

@ -18,15 +18,14 @@ package org.springframework.security.saml2.provider.service.web;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.function.Function; import java.util.function.Function;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream; import java.util.zip.InflaterOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.CodecPolicy;
import org.apache.commons.codec.binary.Base64;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2Error;
@ -49,7 +48,11 @@ import org.springframework.util.Assert;
*/ */
public final class Saml2AuthenticationTokenConverter implements AuthenticationConverter { public final class Saml2AuthenticationTokenConverter implements AuthenticationConverter {
private static Base64 BASE64 = new Base64(0, new byte[] { '\n' }, false, CodecPolicy.STRICT); // MimeDecoder allows extra line-breaks as well as other non-alphabet values.
// This matches the behaviour of the commons-codec decoder.
private static final Base64.Decoder BASE64 = Base64.getMimeDecoder();
private static final Base64Checker BASE_64_CHECKER = new Base64Checker();
private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver; private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;
@ -127,6 +130,7 @@ public final class Saml2AuthenticationTokenConverter implements AuthenticationCo
private byte[] samlDecode(String base64EncodedPayload) { private byte[] samlDecode(String base64EncodedPayload) {
try { try {
BASE_64_CHECKER.checkAcceptable(base64EncodedPayload);
return BASE64.decode(base64EncodedPayload); return BASE64.decode(base64EncodedPayload);
} }
catch (Exception ex) { catch (Exception ex) {
@ -149,4 +153,58 @@ public final class Saml2AuthenticationTokenConverter implements AuthenticationCo
} }
} }
static class Base64Checker {
private static final int[] values = genValueMapping();
Base64Checker() {
}
private static int[] genValueMapping() {
byte[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.getBytes(StandardCharsets.ISO_8859_1);
int[] values = new int[256];
Arrays.fill(values, -1);
for (int i = 0; i < alphabet.length; i++) {
values[alphabet[i] & 0xff] = i;
}
return values;
}
boolean isAcceptable(String s) {
int goodChars = 0;
int lastGoodCharVal = -1;
// count number of characters from Base64 alphabet
for (int i = 0; i < s.length(); i++) {
int val = values[0xff & s.charAt(i)];
if (val != -1) {
lastGoodCharVal = val;
goodChars++;
}
}
// in cases of an incomplete final chunk, ensure the unused bits are zero
switch (goodChars % 4) {
case 0:
return true;
case 2:
return (lastGoodCharVal & 0b1111) == 0;
case 3:
return (lastGoodCharVal & 0b11) == 0;
default:
return false;
}
}
void checkAcceptable(String ins) {
if (!isAcceptable(ins)) {
throw new IllegalArgumentException("Unaccepted Encoding");
}
}
}
} }

View File

@ -20,7 +20,6 @@ dependencies {
provided 'jakarta.servlet:jakarta.servlet-api' provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(path: ':spring-security-core', configuration: 'tests') testImplementation project(path: ':spring-security-core', configuration: 'tests')
testImplementation 'commons-codec:commons-codec'
testImplementation 'io.projectreactor:reactor-test' testImplementation 'io.projectreactor:reactor-test'
testImplementation 'jakarta.xml.bind:jakarta.xml.bind-api' testImplementation 'jakarta.xml.bind:jakarta.xml.bind-api'
testImplementation 'org.hamcrest:hamcrest' testImplementation 'org.hamcrest:hamcrest'

View File

@ -0,0 +1,55 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.test.web;
import java.util.Base64;
import org.springframework.util.DigestUtils;
public final class CodecTestUtils {
private CodecTestUtils() {
}
public static String encodeBase64(String unencoded) {
return Base64.getEncoder().encodeToString(unencoded.getBytes());
}
public static String encodeBase64(byte[] unencoded) {
return Base64.getEncoder().encodeToString(unencoded);
}
public static String decodeBase64(String encoded) {
return new String(Base64.getDecoder().decode(encoded));
}
public static boolean isBase64(byte[] arrayOctet) {
try {
Base64.getMimeDecoder().decode(arrayOctet);
return true;
}
catch (Exception ex) {
return false;
}
}
public static String md5Hex(String data) {
return DigestUtils.md5DigestAsHex(data.getBytes());
}
}

View File

@ -20,8 +20,6 @@ import java.util.Date;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -34,6 +32,7 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.test.web.CodecTestUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -77,7 +76,7 @@ public class TokenBasedRememberMeServicesTests {
} }
private long determineExpiryTimeFromBased64EncodedToken(String validToken) { private long determineExpiryTimeFromBased64EncodedToken(String validToken) {
String cookieAsPlainText = new String(Base64.decodeBase64(validToken.getBytes())); String cookieAsPlainText = CodecTestUtils.decodeBase64(validToken);
String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":"); String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
if (cookieTokens.length == 3) { if (cookieTokens.length == 3) {
try { try {
@ -93,9 +92,9 @@ public class TokenBasedRememberMeServicesTests {
// format is: // format is:
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + // username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" +
// password + ":" + key) // password + ":" + key)
String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key); String signatureValue = CodecTestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
String tokenValue = username + ":" + expiryTime + ":" + signatureValue; String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
return new String(Base64.encodeBase64(tokenValue.getBytes())); return CodecTestUtils.encodeBase64(tokenValue);
} }
@Test @Test
@ -135,7 +134,7 @@ public class TokenBasedRememberMeServicesTests {
@Test @Test
public void autoLoginReturnsNullAndClearsCookieIfMissingThreeTokensInCookieValue() { public void autoLoginReturnsNullAndClearsCookieIfMissingThreeTokensInCookieValue() {
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
new String(Base64.encodeBase64("x".getBytes()))); CodecTestUtils.encodeBase64("x"));
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setCookies(cookie); request.setCookies(cookie);
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
@ -176,7 +175,7 @@ public class TokenBasedRememberMeServicesTests {
@Test @Test
public void autoLoginClearsCookieIfTokenDoesNotContainANumberInCookieValue() { public void autoLoginClearsCookieIfTokenDoesNotContainANumberInCookieValue() {
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
new String(Base64.encodeBase64("username:NOT_A_NUMBER:signature".getBytes()))); CodecTestUtils.encodeBase64("username:NOT_A_NUMBER:signature"));
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setCookies(cookie); request.setCookies(cookie);
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
@ -276,7 +275,7 @@ public class TokenBasedRememberMeServicesTests {
assertThat(Long.parseLong(expiryTime) > expectedExpiryTime - 10000).isTrue(); assertThat(Long.parseLong(expiryTime) > expectedExpiryTime - 10000).isTrue();
assertThat(cookie).isNotNull(); assertThat(cookie).isNotNull();
assertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds()); assertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());
assertThat(Base64.isArrayByteBase64(cookie.getValue().getBytes())).isTrue(); assertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();
assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue(); assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();
} }
@ -290,7 +289,7 @@ public class TokenBasedRememberMeServicesTests {
Cookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); Cookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
assertThat(cookie).isNotNull(); assertThat(cookie).isNotNull();
assertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds()); assertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());
assertThat(Base64.isArrayByteBase64(cookie.getValue().getBytes())).isTrue(); assertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();
assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue(); assertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();
} }
@ -316,7 +315,7 @@ public class TokenBasedRememberMeServicesTests {
assertThat(determineExpiryTimeFromBased64EncodedToken(cookie.getValue()) assertThat(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())
- System.currentTimeMillis() > AbstractRememberMeServices.TWO_WEEKS_S - 50).isTrue(); - System.currentTimeMillis() > AbstractRememberMeServices.TWO_WEEKS_S - 50).isTrue();
assertThat(cookie.getMaxAge()).isEqualTo(-1); assertThat(cookie.getMaxAge()).isEqualTo(-1);
assertThat(Base64.isArrayByteBase64(cookie.getValue().getBytes())).isTrue(); assertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();
} }
} }

View File

@ -18,7 +18,6 @@ package org.springframework.security.web.authentication.www;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -29,6 +28,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.test.web.CodecTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -57,7 +57,7 @@ public class BasicAuthenticationConverterTests {
public void testNormalOperation() { public void testNormalOperation() {
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
UsernamePasswordAuthenticationToken authentication = this.converter.convert(request); UsernamePasswordAuthenticationToken authentication = this.converter.convert(request);
verify(this.authenticationDetailsSource).buildDetails(any()); verify(this.authenticationDetailsSource).buildDetails(any());
assertThat(authentication).isNotNull(); assertThat(authentication).isNotNull();
@ -68,7 +68,7 @@ public class BasicAuthenticationConverterTests {
public void requestWhenAuthorizationSchemeInMixedCaseThenAuthenticates() { public void requestWhenAuthorizationSchemeInMixedCaseThenAuthenticates() {
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "BaSiC " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "BaSiC " + CodecTestUtils.encodeBase64(token));
UsernamePasswordAuthenticationToken authentication = this.converter.convert(request); UsernamePasswordAuthenticationToken authentication = this.converter.convert(request);
verify(this.authenticationDetailsSource).buildDetails(any()); verify(this.authenticationDetailsSource).buildDetails(any());
assertThat(authentication).isNotNull(); assertThat(authentication).isNotNull();
@ -88,7 +88,7 @@ public class BasicAuthenticationConverterTests {
public void testWhenInvalidBasicAuthorizationTokenThenError() { public void testWhenInvalidBasicAuthorizationTokenThenError() {
String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON"; String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request)); assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request));
} }
@ -103,7 +103,7 @@ public class BasicAuthenticationConverterTests {
public void convertWhenEmptyPassword() { public void convertWhenEmptyPassword() {
String token = "rod:"; String token = "rod:";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
UsernamePasswordAuthenticationToken authentication = this.converter.convert(request); UsernamePasswordAuthenticationToken authentication = this.converter.convert(request);
verify(this.authenticationDetailsSource).buildDetails(any()); verify(this.authenticationDetailsSource).buildDetails(any());
assertThat(authentication).isNotNull(); assertThat(authentication).isNotNull();

View File

@ -23,7 +23,6 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -39,6 +38,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.web.CodecTestUtils;
import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
@ -105,7 +105,7 @@ public class BasicAuthenticationFilterTests {
public void testInvalidBasicAuthorizationTokenIsIgnored() throws Exception { public void testInvalidBasicAuthorizationTokenIsIgnored() throws Exception {
String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON"; String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
request.setSession(new MockHttpSession()); request.setSession(new MockHttpSession());
final MockHttpServletResponse response = new MockHttpServletResponse(); final MockHttpServletResponse response = new MockHttpServletResponse();
@ -135,7 +135,7 @@ public class BasicAuthenticationFilterTests {
public void testNormalOperation() throws Exception { public void testNormalOperation() throws Exception {
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
// Test // Test
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
@ -151,7 +151,7 @@ public class BasicAuthenticationFilterTests {
public void doFilterWhenSchemeLowercaseThenCaseInsensitveMatchWorks() throws Exception { public void doFilterWhenSchemeLowercaseThenCaseInsensitveMatchWorks() throws Exception {
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
// Test // Test
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
@ -166,7 +166,7 @@ public class BasicAuthenticationFilterTests {
public void doFilterWhenSchemeMixedCaseThenCaseInsensitiveMatchWorks() throws Exception { public void doFilterWhenSchemeMixedCaseThenCaseInsensitiveMatchWorks() throws Exception {
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "BaSiC " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "BaSiC " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
FilterChain chain = mock(FilterChain.class); FilterChain chain = mock(FilterChain.class);
@ -201,7 +201,7 @@ public class BasicAuthenticationFilterTests {
public void testSuccessLoginThenFailureLoginResultsInSessionLosingToken() throws Exception { public void testSuccessLoginThenFailureLoginResultsInSessionLosingToken() throws Exception {
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
final MockHttpServletResponse response1 = new MockHttpServletResponse(); final MockHttpServletResponse response1 = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class); FilterChain chain = mock(FilterChain.class);
@ -213,7 +213,7 @@ public class BasicAuthenticationFilterTests {
// NOW PERFORM FAILED AUTHENTICATION // NOW PERFORM FAILED AUTHENTICATION
token = "otherUser:WRONG_PASSWORD"; token = "otherUser:WRONG_PASSWORD";
request = new MockHttpServletRequest(); request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
final MockHttpServletResponse response2 = new MockHttpServletResponse(); final MockHttpServletResponse response2 = new MockHttpServletResponse();
chain = mock(FilterChain.class); chain = mock(FilterChain.class);
this.filter.doFilter(request, response2, chain); this.filter.doFilter(request, response2, chain);
@ -229,7 +229,7 @@ public class BasicAuthenticationFilterTests {
public void testWrongPasswordContinuesFilterChainIfIgnoreFailureIsTrue() throws Exception { public void testWrongPasswordContinuesFilterChainIfIgnoreFailureIsTrue() throws Exception {
String token = "rod:WRONG_PASSWORD"; String token = "rod:WRONG_PASSWORD";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
request.setSession(new MockHttpSession()); request.setSession(new MockHttpSession());
this.filter = new BasicAuthenticationFilter(this.manager); this.filter = new BasicAuthenticationFilter(this.manager);
@ -245,7 +245,7 @@ public class BasicAuthenticationFilterTests {
public void testWrongPasswordReturnsForbiddenIfIgnoreFailureIsFalse() throws Exception { public void testWrongPasswordReturnsForbiddenIfIgnoreFailureIsFalse() throws Exception {
String token = "rod:WRONG_PASSWORD"; String token = "rod:WRONG_PASSWORD";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
request.setSession(new MockHttpSession()); request.setSession(new MockHttpSession());
assertThat(this.filter.isIgnoreFailure()).isFalse(); assertThat(this.filter.isIgnoreFailure()).isFalse();
@ -263,7 +263,7 @@ public class BasicAuthenticationFilterTests {
public void skippedOnErrorDispatch() throws Exception { public void skippedOnErrorDispatch() throws Exception {
String token = "bad:credentials"; String token = "bad:credentials";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error"); request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
@ -287,7 +287,7 @@ public class BasicAuthenticationFilterTests {
String token = "rod:äöü"; String token = "rod:äöü";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", request.addHeader("Authorization",
"Basic " + new String(Base64.encodeBase64(token.getBytes(StandardCharsets.UTF_8)))); "Basic " + CodecTestUtils.encodeBase64(token.getBytes(StandardCharsets.UTF_8)));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
// Test // Test
@ -316,7 +316,7 @@ public class BasicAuthenticationFilterTests {
String token = "rod:äöü"; String token = "rod:äöü";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", request.addHeader("Authorization",
"Basic " + new String(Base64.encodeBase64(token.getBytes(StandardCharsets.ISO_8859_1)))); "Basic " + CodecTestUtils.encodeBase64(token.getBytes(StandardCharsets.ISO_8859_1)));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
// Test // Test
@ -345,7 +345,7 @@ public class BasicAuthenticationFilterTests {
String token = "rod:äöü"; String token = "rod:äöü";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", request.addHeader("Authorization",
"Basic " + new String(Base64.encodeBase64(token.getBytes(StandardCharsets.UTF_8)))); "Basic " + CodecTestUtils.encodeBase64(token.getBytes(StandardCharsets.UTF_8)));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
// Test // Test
@ -378,7 +378,7 @@ public class BasicAuthenticationFilterTests {
this.filter.setSecurityContextRepository(securityContextRepository); this.filter.setSecurityContextRepository(securityContextRepository);
String token = "rod:koala"; String token = "rod:koala";
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token));
request.setServletPath("/some_file.html"); request.setServletPath("/some_file.html");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
// Test // Test

View File

@ -18,13 +18,12 @@ package org.springframework.security.web.authentication.www;
import java.util.Map; import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.DisabledException;
import org.springframework.security.test.web.CodecTestUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -41,11 +40,11 @@ public class DigestAuthenticationEntryPointTests {
// Check the nonce seems to be generated correctly // Check the nonce seems to be generated correctly
// format of nonce is: // format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key)) // base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
assertThat(Base64.isArrayByteBase64(nonce.getBytes())).isTrue(); assertThat(CodecTestUtils.isBase64(nonce.getBytes())).isTrue();
String decodedNonce = new String(Base64.decodeBase64(nonce.getBytes())); String decodedNonce = CodecTestUtils.decodeBase64(nonce);
String[] nonceTokens = StringUtils.delimitedListToStringArray(decodedNonce, ":"); String[] nonceTokens = StringUtils.delimitedListToStringArray(decodedNonce, ":");
assertThat(nonceTokens).hasSize(2); assertThat(nonceTokens).hasSize(2);
String expectedNonceSignature = DigestUtils.md5Hex(nonceTokens[0] + ":" + "key"); String expectedNonceSignature = CodecTestUtils.md5Hex(nonceTokens[0] + ":" + "key");
assertThat(nonceTokens[1]).isEqualTo(expectedNonceSignature); assertThat(nonceTokens[1]).isEqualTo(expectedNonceSignature);
} }

View File

@ -24,8 +24,6 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -41,6 +39,7 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.cache.NullUserCache; import org.springframework.security.core.userdetails.cache.NullUserCache;
import org.springframework.security.test.web.CodecTestUtils;
import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -106,9 +105,9 @@ public class DigestAuthenticationFilterTests {
private static String generateNonce(int validitySeconds, String key) { private static String generateNonce(int validitySeconds, String key) {
long expiryTime = System.currentTimeMillis() + (validitySeconds * 1000); long expiryTime = System.currentTimeMillis() + (validitySeconds * 1000);
String signatureValue = DigestUtils.md5Hex(expiryTime + ":" + key); String signatureValue = CodecTestUtils.md5Hex(expiryTime + ":" + key);
String nonceValue = expiryTime + ":" + signatureValue; String nonceValue = expiryTime + ":" + signatureValue;
return new String(Base64.encodeBase64(nonceValue.getBytes())); return CodecTestUtils.encodeBase64(nonceValue);
} }
@AfterEach @AfterEach
@ -183,7 +182,7 @@ public class DigestAuthenticationFilterTests {
@Test @Test
public void testInvalidDigestAuthorizationTokenGeneratesError() throws Exception { public void testInvalidDigestAuthorizationTokenGeneratesError() throws Exception {
String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON"; String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON";
this.request.addHeader("Authorization", "Digest " + new String(Base64.encodeBase64(token.getBytes()))); this.request.addHeader("Authorization", "Digest " + CodecTestUtils.encodeBase64(token));
MockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false); MockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);
assertThat(response.getStatus()).isEqualTo(401); assertThat(response.getStatus()).isEqualTo(401);
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
@ -211,7 +210,7 @@ public class DigestAuthenticationFilterTests {
@Test @Test
public void testNonceWithIncorrectSignatureForNumericFieldReturnsForbidden() throws Exception { public void testNonceWithIncorrectSignatureForNumericFieldReturnsForbidden() throws Exception {
String nonce = new String(Base64.encodeBase64("123456:incorrectStringPassword".getBytes())); String nonce = CodecTestUtils.encodeBase64("123456:incorrectStringPassword");
String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET", REQUEST_URI, String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET", REQUEST_URI,
QOP, nonce, NC, CNONCE); QOP, nonce, NC, CNONCE);
this.request.addHeader("Authorization", this.request.addHeader("Authorization",
@ -223,7 +222,7 @@ public class DigestAuthenticationFilterTests {
@Test @Test
public void testNonceWithNonNumericFirstElementReturnsForbidden() throws Exception { public void testNonceWithNonNumericFirstElementReturnsForbidden() throws Exception {
String nonce = new String(Base64.encodeBase64("hello:ignoredSecondElement".getBytes())); String nonce = CodecTestUtils.encodeBase64("hello:ignoredSecondElement");
String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET", REQUEST_URI, String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET", REQUEST_URI,
QOP, nonce, NC, CNONCE); QOP, nonce, NC, CNONCE);
this.request.addHeader("Authorization", this.request.addHeader("Authorization",
@ -235,7 +234,7 @@ public class DigestAuthenticationFilterTests {
@Test @Test
public void testNonceWithoutTwoColonSeparatedElementsReturnsForbidden() throws Exception { public void testNonceWithoutTwoColonSeparatedElementsReturnsForbidden() throws Exception {
String nonce = new String(Base64.encodeBase64("a base 64 string without a colon".getBytes())); String nonce = CodecTestUtils.encodeBase64("a base 64 string without a colon");
String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET", REQUEST_URI, String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET", REQUEST_URI,
QOP, nonce, NC, CNONCE); QOP, nonce, NC, CNONCE);
this.request.addHeader("Authorization", this.request.addHeader("Authorization",