diff --git a/src/main/java/com/imgfloat/app/config/TwitchCredentialsValidator.java b/src/main/java/com/imgfloat/app/config/TwitchCredentialsValidator.java new file mode 100644 index 0000000..1e1a606 --- /dev/null +++ b/src/main/java/com/imgfloat/app/config/TwitchCredentialsValidator.java @@ -0,0 +1,31 @@ +package com.imgfloat.app.config; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +@Component +public class TwitchCredentialsValidator { + private final String clientId; + private final String clientSecret; + + public TwitchCredentialsValidator( + @Value("${spring.security.oauth2.client.registration.twitch.client-id}") String clientId, + @Value("${spring.security.oauth2.client.registration.twitch.client-secret}") String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + @PostConstruct + void validate() { + ensurePresent(clientId, "TWITCH_CLIENT_ID"); + ensurePresent(clientSecret, "TWITCH_CLIENT_SECRET"); + } + + private void ensurePresent(String value, String name) { + if (!StringUtils.hasText(value) || "changeme".equalsIgnoreCase(value.trim())) { + throw new IllegalStateException(name + " must be set in the environment or .env file"); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 65d22bd..0366670 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,6 +7,8 @@ server: key-store-type: ${SSL_KEYSTORE_TYPE:PKCS12} spring: + config: + import: optional:file:.env[.properties] application: name: imgfloat thymeleaf: @@ -16,8 +18,8 @@ spring: client: registration: twitch: - client-id: ${TWITCH_CLIENT_ID:changeme} - client-secret: ${TWITCH_CLIENT_SECRET:changeme} + client-id: ${TWITCH_CLIENT_ID} + client-secret: ${TWITCH_CLIENT_SECRET} redirect-uri: "{baseUrl}/login/oauth2/code/twitch" authorization-grant-type: authorization_code scope: ["user:read:email"] diff --git a/src/test/java/com/imgfloat/app/ChannelApiIntegrationTest.java b/src/test/java/com/imgfloat/app/ChannelApiIntegrationTest.java index e8f7804..fa2c66a 100644 --- a/src/test/java/com/imgfloat/app/ChannelApiIntegrationTest.java +++ b/src/test/java/com/imgfloat/app/ChannelApiIntegrationTest.java @@ -19,7 +19,10 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest +@SpringBootTest(properties = { + "spring.security.oauth2.client.registration.twitch.client-id=test-client-id", + "spring.security.oauth2.client.registration.twitch.client-secret=test-client-secret" +}) @AutoConfigureMockMvc class ChannelApiIntegrationTest { diff --git a/src/test/java/com/imgfloat/app/TwitchEnvironmentValidationTest.java b/src/test/java/com/imgfloat/app/TwitchEnvironmentValidationTest.java new file mode 100644 index 0000000..dcd3ebd --- /dev/null +++ b/src/test/java/com/imgfloat/app/TwitchEnvironmentValidationTest.java @@ -0,0 +1,40 @@ +package com.imgfloat.app; + +import com.imgfloat.app.config.TwitchCredentialsValidator; +import org.junit.jupiter.api.Test; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ConfigurableApplicationContext; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class TwitchEnvironmentValidationTest { + + @Test + void failsToStartWhenTwitchCredentialsMissing() { + assertThatThrownBy(() -> new SpringApplicationBuilder(ImgfloatApplication.class) + .properties("server.port=0") + .run()) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasRootCauseMessage("Could not resolve placeholder 'TWITCH_CLIENT_ID' in value \"${TWITCH_CLIENT_ID}\""); + } + + @Test + void loadsCredentialsFromDotEnvFile() { + ConfigurableApplicationContext context = null; + try { + context = new SpringApplicationBuilder(ImgfloatApplication.class) + .properties( + "server.port=0", + "spring.config.import=optional:file:src/test/resources/valid.env[.properties]") + .run(); + ConfigurableApplicationContext finalContext = context; + assertThatCode(() -> finalContext.getBean(TwitchCredentialsValidator.class)) + .doesNotThrowAnyException(); + } finally { + if (context != null) { + context.close(); + } + } + } +} diff --git a/src/test/resources/valid.env b/src/test/resources/valid.env new file mode 100644 index 0000000..82b3f5f --- /dev/null +++ b/src/test/resources/valid.env @@ -0,0 +1,2 @@ +TWITCH_CLIENT_ID=test-client-id +TWITCH_CLIENT_SECRET=test-client-secret