From 334b6b98944f431fede6da3e62bf90ced799d859 Mon Sep 17 00:00:00 2001 From: William R Larsen Jr Date: Wed, 26 Jul 2023 12:17:48 -0400 Subject: [PATCH 1/2] Fixed some issues related to Pulsar Auth updates in previous release --- build.gradle | 4 +- .../com/siliconmtn/pulsar/OAuthConfig.java | 34 ------- .../pulsar/PulsarAuthenticator.java | 41 ++++----- .../pulsar/PulsarClientManager.java | 4 +- .../com/siliconmtn/pulsar/PulsarConfig.java | 30 ++++++ .../pulsar/PulsarAuthenticatorTest.java | 92 ++++++++++++------- .../pulsar/PulsarClientManagerTest.java | 15 +-- .../siliconmtn/pulsar/PulsarConfigTest.java | 71 ++++++++++++++ 8 files changed, 193 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/com/siliconmtn/pulsar/OAuthConfig.java create mode 100644 src/test/java/com/siliconmtn/pulsar/PulsarConfigTest.java diff --git a/build.gradle b/build.gradle index 45004fc..b93141b 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ group = 'com.siliconmtn' * For 'release' publishing, use: version = n.n.n * */ -version = '1.2.11-SNAPSHOT' +version = '1.2.11.3-SNAPSHOT' //version = '1.2.11' sourceCompatibility = '11' @@ -53,8 +53,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.security:spring-security-oauth2-client' - implementation 'org.springframework.security.oauth:spring-security-oauth2:2.5.2.RELEASE' implementation 'org.springframework.security:spring-security-crypto:5.5.2' implementation 'org.hibernate:hibernate-validator:7.0.1.Final' diff --git a/src/main/java/com/siliconmtn/pulsar/OAuthConfig.java b/src/main/java/com/siliconmtn/pulsar/OAuthConfig.java deleted file mode 100644 index a4a23dc..0000000 --- a/src/main/java/com/siliconmtn/pulsar/OAuthConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.siliconmtn.pulsar; - -import org.springframework.context.annotation.Configuration; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -/** - * Title: OAuthConfig.java - * Project: Notifications MicroService - * Description: Manages configuration for the PulseClient OAuth security - * - * Copyright: 2022 - * Company: Silicon Mountain Technologies - * - * @author raptor - * @version 1.0 - * @since Jul 7, 2022 - * @updates - * - */ -@Configuration -@NoArgsConstructor -@Setter -@Getter -@ToString -public class OAuthConfig { - - String audience; - String credentialsUrl; - String issuerUrl; -} diff --git a/src/main/java/com/siliconmtn/pulsar/PulsarAuthenticator.java b/src/main/java/com/siliconmtn/pulsar/PulsarAuthenticator.java index 96da1f5..fbd7aef 100644 --- a/src/main/java/com/siliconmtn/pulsar/PulsarAuthenticator.java +++ b/src/main/java/com/siliconmtn/pulsar/PulsarAuthenticator.java @@ -8,8 +8,6 @@ import java.util.function.Supplier; import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.JsonNode; @@ -48,16 +46,14 @@ public class PulsarAuthenticator implements Supplier { //Autowired Member Variables protected PulsarConfig config; - protected ClientRegistrationRepository repo; //Instance Variables private String token; protected ObjectMapper mapper; protected SMTHttpConnectionManager manager; - public PulsarAuthenticator(PulsarConfig config, ClientRegistrationRepository repo) { + public PulsarAuthenticator(PulsarConfig config) { this.config = config; - this.repo = repo; this.manager = new SMTHttpConnectionManager(); prepareManager(this.manager); this.mapper = new ObjectMapper(); @@ -79,7 +75,7 @@ protected void prepareManager(SMTHttpConnectionManager manager) { */ @Override public String get() { - log.info("Pulsar JWT Token is being requested"); + log.debug("Pulsar JWT Token is being requested"); return token; } @@ -93,14 +89,16 @@ public String get() { */ @Scheduled(cron = "${pulsar.cronSchedule:-}") public void updateToken() { - log.info("Populating Pulsar JWT Token"); - ClientRegistration reg = repo.findByRegistrationId(OAUTH_IDENTIFIER); - if(reg != null && !StringUtil.isEmpty(reg.getClientId()) && !StringUtil.isEmpty(reg.getClientSecret()) && !StringUtil.isEmpty(reg.getProviderDetails().getTokenUri())) { - retrieveNPEJWTToken(reg); - } else if (!StringUtil.isEmpty(config.getAdminJWT()) || !StringUtil.isEmpty(config.getClientJWT())) { + log.debug("Populating Pulsar JWT Token"); + if(config.hasNPEAuth()) { + retrieveNPEJWTToken(config); + } else if (config.hasJWTAuth()) { token = StringUtil.defaultString(config.getAdminJWT(), config.getClientJWT()); + } else { + //Authenticator requires at least an empty string to avoid an NPE. + token = ""; } - log.info("Populated Pulsar JWT Token"); + log.debug("Populated Pulsar JWT Token"); } /** @@ -115,20 +113,21 @@ public void updateToken() { * --data-urlencode 'grant_type=client_credentials' * @param reg */ - protected void retrieveNPEJWTToken(ClientRegistration reg) { - if(reg == null) { - token = null; + protected void retrieveNPEJWTToken(PulsarConfig config) { + token = null; + + if(config == null) { return; } Map postBody = new HashMap<>(); - postBody.put(CLIENT_ID, reg.getClientId()); - postBody.put(CLIENT_SECRET, reg.getClientSecret()); - postBody.put(SCOPE, reg.getScopes().iterator().next()); - postBody.put(GRANT_TYPE, reg.getAuthorizationGrantType().getValue()); + postBody.put(CLIENT_ID, config.getClientId()); + postBody.put(CLIENT_SECRET, config.getClientSecret()); + postBody.put(SCOPE, config.getScope()); + postBody.put(GRANT_TYPE, config.getAuthorizationGrantType()); try { - byte [] data = manager.getRequestData(reg.getProviderDetails().getTokenUri(), postBody, HttpConnectionType.POST); - log.info("Received Pulsar JWT Token"); + byte [] data = manager.getRequestData(config.getTokenUri(), postBody, HttpConnectionType.POST); + log.debug("Received Pulsar JWT Token"); JsonNode g = mapper.readTree(data); token = g.get(ACCESS_TOKEN).asText(); } catch(Exception e) { diff --git a/src/main/java/com/siliconmtn/pulsar/PulsarClientManager.java b/src/main/java/com/siliconmtn/pulsar/PulsarClientManager.java index 2c67143..3f107d0 100644 --- a/src/main/java/com/siliconmtn/pulsar/PulsarClientManager.java +++ b/src/main/java/com/siliconmtn/pulsar/PulsarClientManager.java @@ -44,7 +44,9 @@ protected ClientConfigurationData buildClientConfig() { conf.setRequestTimeoutMs(1000); conf.setOperationTimeoutMs(1000); conf.setTlsAllowInsecureConnection(config.isTlsAllowInsecureConnection()); - conf.setAuthentication(AuthenticationFactory.token(auth)); + if(config.hasAuth()) { + conf.setAuthentication(AuthenticationFactory.token(auth)); + } return conf; } diff --git a/src/main/java/com/siliconmtn/pulsar/PulsarConfig.java b/src/main/java/com/siliconmtn/pulsar/PulsarConfig.java index 6c7a200..0084577 100644 --- a/src/main/java/com/siliconmtn/pulsar/PulsarConfig.java +++ b/src/main/java/com/siliconmtn/pulsar/PulsarConfig.java @@ -2,6 +2,7 @@ import java.util.Map; +import org.apache.pulsar.shade.org.apache.commons.lang3.StringUtils; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.context.annotation.Configuration; @@ -39,4 +40,33 @@ public class PulsarConfig { private String cronSchedule; private boolean tlsAllowInsecureConnection; private Map topics; + private String clientId; + private String clientSecret; + private String scope; + private String tokenUri; + private String authorizationGrantType; + + /** + * Helper method to check if we have a valid JWT Token Configuration + * @return + */ + public boolean hasJWTAuth() { + return !StringUtils.isEmpty(adminJWT) || !StringUtils.isEmpty(clientJWT); + } + + /** + * Helper method to check if we have a valid NPE Configuration + * @return + */ + public boolean hasNPEAuth() { + return !StringUtils.isEmpty(clientId) && !StringUtils.isEmpty(clientSecret) && !StringUtils.isEmpty(tokenUri) && !StringUtils.isEmpty(scope) && !StringUtils.isEmpty(authorizationGrantType); + } + + /** + * Helper method to determine if there is a valid authentication method present in config. + * @return + */ + public boolean hasAuth() { + return hasJWTAuth() || hasNPEAuth(); + } } \ No newline at end of file diff --git a/src/test/java/com/siliconmtn/pulsar/PulsarAuthenticatorTest.java b/src/test/java/com/siliconmtn/pulsar/PulsarAuthenticatorTest.java index 7f34382..6fa3807 100644 --- a/src/test/java/com/siliconmtn/pulsar/PulsarAuthenticatorTest.java +++ b/src/test/java/com/siliconmtn/pulsar/PulsarAuthenticatorTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -17,7 +16,6 @@ import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; -import java.util.Set; import org.apache.pulsar.shade.com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -25,10 +23,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.core.AuthorizationGrantType; import com.siliconmtn.io.http.SMTHttpConnectionManager; import com.siliconmtn.io.http.SMTHttpConnectionManager.HttpConnectionType; @@ -43,9 +37,6 @@ class PulsarAuthenticatorTest { @Mock PulsarConfig config; - @Mock - ClientRegistrationRepository repo; - @InjectMocks PulsarAuthenticator auth; @@ -53,13 +44,22 @@ class PulsarAuthenticatorTest { void prepareManager() throws MalformedURLException { SMTHttpConnectionManager manager = new SMTHttpConnectionManager(); auth.prepareManager(manager); - assertNotNull(manager.getSslSocketFactory()); assertTrue(manager.getRequestHeaders().containsKey(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE)); - assertEquals(manager.getRequestHeaders().get(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE), "application/x-www-form-urlencoded"); + assertEquals("application/x-www-form-urlencoded", manager.getRequestHeaders().get(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE)); + } + + @Test + void updateTokenNoAuth() { + when(config.hasNPEAuth()).thenReturn(false); + when(config.hasJWTAuth()).thenReturn(false); + assertDoesNotThrow(() -> auth.updateToken()); + assertEquals("", auth.get()); } @Test void updateTokenFromConfig() { + when(config.hasNPEAuth()).thenReturn(false); + when(config.hasJWTAuth()).thenReturn(true); String clientToken = "client"; String adminToken = "admin"; when(config.getClientJWT()).thenReturn(clientToken); @@ -70,17 +70,17 @@ void updateTokenFromConfig() { assertEquals(adminToken, auth.get()); } + @SuppressWarnings("unchecked") @Test void retrieveNPEJWTToken() throws IOException { assertDoesNotThrow(() -> auth.retrieveNPEJWTToken(null)); assertNull(auth.get()); - ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules(); String scope = "email"; String id = "clientId"; String secret = "clientSecret"; - AuthorizationGrantType grantType = AuthorizationGrantType.CLIENT_CREDENTIALS; + String grantType = "CLIENT_CREDENTIALS"; String tokenUri = "tokenUri"; String accessToken = "token"; Map retData = new HashMap<>(); @@ -88,32 +88,64 @@ void retrieveNPEJWTToken() throws IOException { byte [] data = mapper.writeValueAsBytes(retData); SMTHttpConnectionManager manager = mock(SMTHttpConnectionManager.class); - ProviderDetails det = mock(ProviderDetails.class); - ClientRegistration reg = mock(ClientRegistration.class); auth.manager = manager; - when(reg.getScopes()).thenReturn(Set.of(scope)); - when(reg.getClientId()).thenReturn(id); - when(reg.getClientSecret()).thenReturn(secret); - when(reg.getAuthorizationGrantType()).thenReturn(grantType); - when(reg.getProviderDetails()).thenReturn(det); - when(det.getTokenUri()).thenReturn(tokenUri); + when(config.getScope()).thenReturn(scope); + when(config.getClientId()).thenReturn(id); + when(config.getClientSecret()).thenReturn(secret); + when(config.getAuthorizationGrantType()).thenReturn(grantType); + when(config.getTokenUri()).thenReturn(tokenUri); when(manager.getRequestData(eq(tokenUri), any(Map.class), eq(HttpConnectionType.POST))).thenReturn(data); - assertDoesNotThrow(() -> auth.retrieveNPEJWTToken(reg)); + assertDoesNotThrow(() -> auth.retrieveNPEJWTToken(config)); assertEquals(accessToken, auth.get()); } + @SuppressWarnings("unchecked") + @Test + void retrieveNPEJWTTokenCatchError() throws IOException { + assertDoesNotThrow(() -> auth.retrieveNPEJWTToken(null)); + assertNull(auth.get()); + ObjectMapper mapper = new ObjectMapper(); + mapper.findAndRegisterModules(); + String scope = "email"; + String id = "clientId"; + String secret = "clientSecret"; + String grantType = "CLIENT_CREDENTIALS"; + String tokenUri = "tokenUri"; + String accessToken = "token"; + Map retData = new HashMap<>(); + retData.put(PulsarAuthenticator.ACCESS_TOKEN, accessToken); + + byte [] data = null; + SMTHttpConnectionManager manager = mock(SMTHttpConnectionManager.class); + auth.manager = manager; + + when(config.getScope()).thenReturn(scope); + when(config.getClientId()).thenReturn(id); + when(config.getClientSecret()).thenReturn(secret); + when(config.getAuthorizationGrantType()).thenReturn(grantType); + when(config.getTokenUri()).thenReturn(tokenUri); + + when(manager.getRequestData(eq(tokenUri), any(Map.class), eq(HttpConnectionType.POST))).thenReturn(data); + + assertDoesNotThrow(() -> auth.retrieveNPEJWTToken(config)); + + assertEquals(null, auth.get()); + } + + @SuppressWarnings("unchecked") @Test void updateTokenFromOauth() throws IOException { + when(config.hasNPEAuth()).thenReturn(true); ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules(); String scope = "email"; String id = "clientId"; String secret = "clientSecret"; - AuthorizationGrantType grantType = AuthorizationGrantType.CLIENT_CREDENTIALS; + String grantType = "CLIENT_CREDENTIALS"; String tokenUri = "tokenUri"; String accessToken = "token"; Map retData = new HashMap<>(); @@ -121,16 +153,12 @@ void updateTokenFromOauth() throws IOException { byte [] data = mapper.writeValueAsBytes(retData); SMTHttpConnectionManager manager = mock(SMTHttpConnectionManager.class); - ProviderDetails det = mock(ProviderDetails.class); - ClientRegistration reg = mock(ClientRegistration.class); auth.manager = manager; - when(repo.findByRegistrationId(PulsarAuthenticator.OAUTH_IDENTIFIER)).thenReturn(reg); - when(reg.getScopes()).thenReturn(Set.of(scope)); - when(reg.getClientId()).thenReturn(id); - when(reg.getClientSecret()).thenReturn(secret); - when(reg.getAuthorizationGrantType()).thenReturn(grantType); - when(reg.getProviderDetails()).thenReturn(det); - when(det.getTokenUri()).thenReturn(tokenUri); + when(config.getScope()).thenReturn(scope); + when(config.getClientId()).thenReturn(id); + when(config.getClientSecret()).thenReturn(secret); + when(config.getAuthorizationGrantType()).thenReturn(grantType); + when(config.getTokenUri()).thenReturn(tokenUri); when(manager.getRequestData(eq(tokenUri), any(Map.class), eq(HttpConnectionType.POST))).thenReturn(data); diff --git a/src/test/java/com/siliconmtn/pulsar/PulsarClientManagerTest.java b/src/test/java/com/siliconmtn/pulsar/PulsarClientManagerTest.java index de38f41..617073f 100644 --- a/src/test/java/com/siliconmtn/pulsar/PulsarClientManagerTest.java +++ b/src/test/java/com/siliconmtn/pulsar/PulsarClientManagerTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.when; import java.net.MalformedURLException; @@ -12,7 +13,6 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; @@ -51,7 +51,8 @@ void setup() { @Test void buildPulsarClientConfigTest() throws MalformedURLException { - Mockito.when(config.getUrl()).thenReturn("pulsar://localhost:6650"); + when(config.getUrl()).thenReturn("pulsar://localhost:6650"); + when(config.hasAuth()).thenReturn(false); ClientConfigurationData conf = manager.buildClientConfig(); assertEquals("pulsar://localhost:6650", conf.getServiceUrl()); @@ -59,17 +60,17 @@ void buildPulsarClientConfigTest() throws MalformedURLException { @Test void buildPulsarClientConfigTestWithClientJWT() throws MalformedURLException { - Mockito.when(config.getUrl()).thenReturn("pulsar://localhost:6650"); - Mockito.when(config.getClientJWT()).thenReturn("HelloWorld"); - + when(config.getUrl()).thenReturn("pulsar://localhost:6650"); + when(config.getClientJWT()).thenReturn("HelloWorld"); + when(config.hasAuth()).thenReturn(true); ClientConfigurationData conf = manager.buildClientConfig(); assertNotNull(conf.getAuthentication()); } @Test void pulsarClientTest() { - Mockito.when(config.getUrl()).thenReturn("pulsar://localhost:6650"); - Mockito.when(config.getClientJWT()).thenReturn("HelloWorld"); + when(config.getUrl()).thenReturn("pulsar://localhost:6650"); + when(config.getClientJWT()).thenReturn("HelloWorld"); PulsarClient client = assertDoesNotThrow(() -> manager.createPulsarClient()); assertNotNull(client); } diff --git a/src/test/java/com/siliconmtn/pulsar/PulsarConfigTest.java b/src/test/java/com/siliconmtn/pulsar/PulsarConfigTest.java new file mode 100644 index 0000000..2928925 --- /dev/null +++ b/src/test/java/com/siliconmtn/pulsar/PulsarConfigTest.java @@ -0,0 +1,71 @@ +/** + * + */ +package com.siliconmtn.pulsar; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * + */ +class PulsarConfigTest { + + @Test + void hasJWTAuth() { + PulsarConfig config = new PulsarConfig(); + assertFalse(config.hasJWTAuth()); + config.setClientJWT("test"); + assertTrue(config.hasJWTAuth()); + config.setAdminJWT("test"); + assertTrue(config.hasJWTAuth()); + } + + @Test + void hasNPEAuth() { + PulsarConfig config = new PulsarConfig(); + assertFalse(config.hasNPEAuth()); + config.setClientId("test"); + assertFalse(config.hasNPEAuth()); + config.setClientSecret("test"); + assertFalse(config.hasNPEAuth()); + config.setTokenUri("test"); + assertFalse(config.hasNPEAuth()); + config.setScope("test"); + assertFalse(config.hasNPEAuth()); + config.setAuthorizationGrantType("test"); + assertTrue(config.hasNPEAuth()); + } + + @Test + void hasAuthWithClient() { + PulsarConfig config = new PulsarConfig(); + assertFalse(config.hasAuth()); + config.setClientJWT("test"); + assertTrue(config.hasAuth()); + config.setAdminJWT("test"); + assertTrue(config.hasAuth()); + } + + @Test + void hasAuthWithAdmin() { + PulsarConfig config = new PulsarConfig(); + assertFalse(config.hasAuth()); + config.setAdminJWT("test"); + assertTrue(config.hasAuth()); + } + + @Test + void hasAuthWithNPE() { + PulsarConfig config = new PulsarConfig(); + assertFalse(config.hasAuth()); + config.setClientId("test"); + config.setClientSecret("test"); + config.setTokenUri("test"); + config.setScope("test"); + config.setAuthorizationGrantType("test"); + assertTrue(config.hasAuth()); + } +} From f9019e7c8612f14ca8d4062d36a7fc49fedf4f91 Mon Sep 17 00:00:00 2001 From: Billy Larsen Date: Wed, 26 Jul 2023 13:04:10 -0400 Subject: [PATCH 2/2] Update build.gradle --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b93141b..318de79 100644 --- a/build.gradle +++ b/build.gradle @@ -25,8 +25,8 @@ group = 'com.siliconmtn' * For 'release' publishing, use: version = n.n.n * */ -version = '1.2.11.3-SNAPSHOT' -//version = '1.2.11' +//version = '1.2.12-SNAPSHOT' +version = '1.2.12' sourceCompatibility = '11' archivesBaseName = "spacelibs-java"