Add DelegatingPasswordEncoder
Fixes gh-4666
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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
|
||||
*
|
||||
* 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.springframework.security.crypto.password;
|
||||
|
||||
import com.sun.org.apache.xpath.internal.SourceTree;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DelegatingPasswordEncoderTests {
|
||||
@Mock
|
||||
private PasswordEncoder bcrypt;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder noop;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder invalidId;
|
||||
|
||||
private String bcryptId = "bcrypt";
|
||||
|
||||
private String noopId = "noop";
|
||||
|
||||
private String rawPassword = "password";
|
||||
|
||||
private String encodedPassword = "ENCODED-PASSWORD";
|
||||
|
||||
private String bcryptEncodedPassword = "{bcrypt}" + this.encodedPassword;
|
||||
|
||||
private String noopEncodedPassword = "{noop}" + this.encodedPassword;
|
||||
|
||||
private Map<String,PasswordEncoder> delegates;
|
||||
|
||||
private DelegatingPasswordEncoder passwordEncoder;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.delegates = new HashMap<>();
|
||||
this.delegates.put(this.bcryptId, this.bcrypt);
|
||||
this.delegates.put(this.noopId, this.noop);
|
||||
|
||||
this.passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorWhenIdForEncodeNullThenIllegalArgumentException() {
|
||||
String id = null;
|
||||
new DelegatingPasswordEncoder(id, this.delegates);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorWhenIdForEncodeDoesNotExistThenIllegalArgumentException() {
|
||||
new DelegatingPasswordEncoder(this.bcryptId + "INVALID", this.delegates);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setDefaultPasswordEncoderForMatchesWhenNullThenIllegalArgumentException() {
|
||||
this.passwordEncoder.setDefaultPasswordEncoderForMatches(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenCustomDefaultPasswordEncoderForMatchesThenDelegates() {
|
||||
String encodedPassword = "{unmapped}" + this.rawPassword;
|
||||
this.passwordEncoder.setDefaultPasswordEncoderForMatches(this.invalidId);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, encodedPassword)).isFalse();
|
||||
|
||||
verify(this.invalidId).matches(this.rawPassword, encodedPassword);
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenValidThenUsesIdForEncode() {
|
||||
when(this.bcrypt.encode(this.rawPassword)).thenReturn(this.encodedPassword);
|
||||
|
||||
assertThat(this.passwordEncoder.encode(this.rawPassword)).isEqualTo(this.bcryptEncodedPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenBCryptThenDelegatesToBCrypt() {
|
||||
when(this.bcrypt.matches(this.rawPassword, this.encodedPassword)).thenReturn(true);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.bcryptEncodedPassword)).isTrue();
|
||||
|
||||
verify(this.bcrypt).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyZeroInteractions(this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoopThenDelegatesToNoop() {
|
||||
when(this.noop.matches(this.rawPassword, this.encodedPassword)).thenReturn(true);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.noopEncodedPassword)).isTrue();
|
||||
|
||||
verify(this.noop).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyZeroInteractions(this.bcrypt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenUnMappedThenIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{unmapped}" + this.rawPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("There is no PasswordEncoder mapped for the id \"unmapped\"");
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentExcetion() {
|
||||
assertThatThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoStartingPrefixStringThenFalse() {
|
||||
assertThatThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "bcrypt}" + this.rawPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoIdStringThenFalse() {
|
||||
assertThatThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{}" + this.rawPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("There is no PasswordEncoder mapped for the id \"\"");
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenPrefixInMiddleThenFalse() {
|
||||
assertThatThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "invalid" + this.bcryptEncodedPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNullIdThenDelegatesToInvalidId() {
|
||||
this.delegates.put(null, this.invalidId);
|
||||
this.passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
when(this.invalidId.matches(this.rawPassword, this.encodedPassword)).thenReturn(true);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.encodedPassword)).isTrue();
|
||||
|
||||
verify(this.invalidId).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user