From bccd50ac4ada9524b84f5daddb4650209edce275 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 19 Nov 2022 18:28:39 +0100 Subject: [PATCH] #78 put a secureRandom value as default value for the JWT token --- .../properties/JwtSecurityProperties.java | 15 ++++++- .../api/security/SecurityControllerTest.java | 14 ++++++- ...urityLoginViaHeaderAndLoginFilterTest.java | 1 - .../security/SecurityLoginViaHeaderTest.java | 1 - .../AbstractPropertyValidatorTest.java | 39 +++++++++++++++++++ ...InMemoryUsersValidationControllerTest.java | 37 +++--------------- .../properties/JwtSecurityPropertiesTest.java | 39 +++++++++++++++++++ 7 files changed, 109 insertions(+), 37 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/AbstractPropertyValidatorTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityPropertiesTest.java diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityProperties.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityProperties.java index d54721d..7f6df4d 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityProperties.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityProperties.java @@ -3,17 +3,28 @@ package it.fabioformosa.quartzmanager.api.security.properties; import lombok.Data; import lombok.Getter; import lombok.Setter; -import org.apache.commons.lang3.RandomStringUtils; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import java.security.SecureRandom; +import java.util.Base64; + @Configuration @ConfigurationProperties(prefix = "quartz-manager.security.jwt") @Getter @Setter public class JwtSecurityProperties { - private String secret = RandomStringUtils.randomAlphabetic(10); + private String secret; + + { + SecureRandom random = new SecureRandom(); + byte[] bytes = new byte[20]; + random.nextBytes(bytes); + Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding(); + secret = encoder.encodeToString(bytes); + } + private long expirationInSec = 28800; private CookieStrategy cookieStrategy = new CookieStrategy(); diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityControllerTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityControllerTest.java index c2d88d4..93eb5e2 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityControllerTest.java @@ -2,6 +2,8 @@ package it.fabioformosa.quartzmanager.api.security; import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths; import it.fabioformosa.quartzmanager.api.security.controllers.TestController; +import it.fabioformosa.quartzmanager.api.security.properties.JwtSecurityProperties; +import org.assertj.core.api.Assertions; import org.hamcrest.core.IsNot; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -19,9 +21,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc @TestPropertySource(properties = { - "quartz-manager.security.jwt.enabled=true", "quartz-manager.security.jwt.secret=bibidibobidiboo", - "quartz-manager.security.jwt.expiration-in-sec=28800", + "quartz-manager.security.jwt.expiration-in-sec=36000", "quartz-manager.security.jwt.header-strategy.enabled=false", "quartz-manager.security.jwt.header-strategy.header=Authorization", "quartz-manager.security.jwt.cookie-strategy.enabled=true", @@ -36,6 +37,9 @@ class SecurityControllerTest { @Autowired private MockMvc mockMvc; + @Autowired + private JwtSecurityProperties jwtSecurityProperties; + @Test void givenAnAnonymousUser_whenCalledADMZController_thenShouldRaiseForbidden() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/dmz")) @@ -72,4 +76,10 @@ class SecurityControllerTest { .andExpect(status().isOk()); } + @Test + void givenSecurityProps_whenTheBootstrapHasCompleted_thenJWTPropertiesShouldBeSetAccordingly() throws Exception { + Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(36000); + Assertions.assertThat(jwtSecurityProperties.getSecret()).isEqualTo("bibidibobidiboo"); + } + } diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderAndLoginFilterTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderAndLoginFilterTest.java index 64b41d4..7633478 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderAndLoginFilterTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderAndLoginFilterTest.java @@ -13,7 +13,6 @@ import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = { "quartz-manager.security.login-model.form-login-enabled = false", "quartz-manager.security.login-model.userpwd-filter-enabled = true", - "quartz-manager.security.jwt.enabled=true", "quartz-manager.security.jwt.secret=bibidibobidiboo", "quartz-manager.security.jwt.expiration-in-sec=28800", "quartz-manager.security.jwt.header-strategy.enabled=true", diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderTest.java index dd78ef2..591f305 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/SecurityLoginViaHeaderTest.java @@ -12,7 +12,6 @@ import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = { "quartz-manager.security.login-model.form-login-enabled = true", "quartz-manager.security.login-model.userpwd-filter-enabled = false", - "quartz-manager.security.jwt.enabled=true", "quartz-manager.security.jwt.secret=bibidibobidiboo", "quartz-manager.security.jwt.expiration-in-sec=28800", "quartz-manager.security.jwt.header-strategy.enabled=true", diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/AbstractPropertyValidatorTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/AbstractPropertyValidatorTest.java new file mode 100644 index 0000000..1a96f18 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/AbstractPropertyValidatorTest.java @@ -0,0 +1,39 @@ +package it.fabioformosa.quartzmanager.api.security.properties; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.springframework.boot.context.properties.bind.BindResult; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySource; +import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; + +import javax.validation.Validation; +import javax.validation.Validator; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +public class AbstractPropertyValidatorTest { + protected static Validator propertyValidator; + + @BeforeAll + public static void setup() { + propertyValidator = Validation.buildDefaultValidatorFactory().getValidator(); + } + + protected static T inflateConfigurationPropertyFromAMap(Map properties, String configurationPropName, Class propClass) { + ConfigurationPropertySource source = new MapConfigurationPropertySource(properties); + Binder binder = new Binder(source); + BindResult result = binder.bind(configurationPropName, propClass); + if (properties != null && !properties.isEmpty()) { + Assertions.assertThat(result.isBound()).isTrue(); + T configPropObject = result.get(); + return configPropObject; + } else { + try { + return propClass.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/InMemoryUsersValidationControllerTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/InMemoryUsersValidationControllerTest.java index 4b29016..5c026d9 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/InMemoryUsersValidationControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/InMemoryUsersValidationControllerTest.java @@ -1,25 +1,16 @@ package it.fabioformosa.quartzmanager.api.security.properties; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.boot.context.properties.bind.BindResult; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.context.properties.source.ConfigurationPropertySource; -import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; -import javax.validation.Validation; -import javax.validation.Validator; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; -public class InMemoryUsersValidationControllerTest { - - private static Validator propertyValidator; +public class InMemoryUsersValidationControllerTest extends AbstractPropertyValidatorTest { static Stream notValidInMemoryProps = Stream.of( Arguments.of( @@ -34,26 +25,15 @@ public class InMemoryUsersValidationControllerTest { ); - @BeforeAll - public static void setup() { - propertyValidator = Validation.buildDefaultValidatorFactory().getValidator(); - } - - static Stream getNotValidInMemoryProps(){ + static Stream getNotValidInMemoryProps() { return notValidInMemoryProps; } @ParameterizedTest @MethodSource("it.fabioformosa.quartzmanager.api.security.properties.InMemoryUsersValidationControllerTest#getNotValidInMemoryProps") void givenAMissingUsername_whenThePropertyValidationIsApplied_thenShouldRaiseValidationError(Map properties) { - ConfigurationPropertySource source = new MapConfigurationPropertySource(properties); - - Binder binder = new Binder(source); - BindResult result = binder.bind("quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class); - - Assertions.assertThat(result.isBound()).isTrue(); - - InMemoryAccountProperties inMemoryAccountProperties = result.get(); + InMemoryAccountProperties inMemoryAccountProperties = inflateConfigurationPropertyFromAMap(properties, + "quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class); Assertions.assertThat(propertyValidator.validate(inMemoryAccountProperties)).isNotEmpty(); } @@ -65,14 +45,9 @@ public class InMemoryUsersValidationControllerTest { properties.put("quartz-manager.security.accounts.in-memory.users[0].password", "bar"); properties.put("quartz-manager.security.accounts.in-memory.users[0].roles[0]", "admin"); - ConfigurationPropertySource source = new MapConfigurationPropertySource(properties); + InMemoryAccountProperties inMemoryAccountProperties = inflateConfigurationPropertyFromAMap(properties, + "quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class); - Binder binder = new Binder(source); - BindResult result = binder.bind("quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class); - - Assertions.assertThat(result.isBound()).isTrue(); - - InMemoryAccountProperties inMemoryAccountProperties = result.get(); Assertions.assertThat(propertyValidator.validate(inMemoryAccountProperties)).isEmpty(); } diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityPropertiesTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityPropertiesTest.java new file mode 100644 index 0000000..aea2902 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/api/security/properties/JwtSecurityPropertiesTest.java @@ -0,0 +1,39 @@ +package it.fabioformosa.quartzmanager.api.security.properties; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +class JwtSecurityPropertiesTest extends AbstractPropertyValidatorTest { + + @Test + void givenAllJWTSecurityPropSet_whenThePropertyValidationIsApplied_thenShouldBeValid() { + Map properties = new HashMap<>(); + String secret = "helloworld"; + properties.put("quartz-manager.security.jwt.secret", secret); + String expirationInSec = "36000"; + properties.put("quartz-manager.security.jwt.expirationInSec", expirationInSec); + + JwtSecurityProperties jwtSecurityProperties = inflateConfigurationPropertyFromAMap(properties, + "quartz-manager.security.jwt", JwtSecurityProperties.class); + + Assertions.assertThat(propertyValidator.validate(jwtSecurityProperties)).isEmpty(); + + Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(Long.valueOf(expirationInSec)); + Assertions.assertThat(jwtSecurityProperties.getSecret()).isEqualTo(secret); + } + + @Test + void givenTheMandatoryJWTSecurityPropUnset_whenThePropertyValidationIsApplied_thenShouldBeSetWithDefault() { + Map properties = new HashMap<>(); + + JwtSecurityProperties jwtSecurityProperties = inflateConfigurationPropertyFromAMap(properties, + "quartz-manager.security.jwt", JwtSecurityProperties.class); + + Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(28800L); + Assertions.assertThat(jwtSecurityProperties.getSecret()).isNotBlank(); + } + +}