From 2e5f8f41694f8cca15d9da2d25ff5b3c19e0afa6 Mon Sep 17 00:00:00 2001 From: Andrei Isvoran Date: Thu, 31 Aug 2023 14:28:49 +0300 Subject: [PATCH] RED-7238 - Fix test SMTP connection --- .../external/SMTPConfigurationResource.java | 7 +-- .../external/SMTPConfigurationController.java | 31 ++++++++++-- .../model/SMTPConfiguration.java | 4 +- .../model/SMTPResponse.java | 21 ++++++++ .../service/RealmService.java | 20 +++++++- .../service/UserService.java | 2 +- .../tests/SMTPConfigurationTest.java | 48 +++++++++++++++---- 7 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPResponse.java diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/SMTPConfigurationResource.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/SMTPConfigurationResource.java index 6667002..d3981e3 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/SMTPConfigurationResource.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/SMTPConfigurationResource.java @@ -6,11 +6,11 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration; +import com.knecon.fforesight.tenantusermanagement.model.SMTPResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -38,11 +38,12 @@ public interface SMTPConfigurationResource { void updateSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel); + @ResponseBody @ResponseStatus(value = HttpStatus.OK) - @PostMapping(value = SMTP_PATH + TEST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = SMTP_PATH + TEST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Test SMTP Settings to KeyCloak") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "SMTP Configuration is valid."), @ApiResponse(responseCode = "400", description = "SMTP test failed.")}) - void testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel); + SMTPResponse testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel); @ResponseStatus(value = HttpStatus.NO_CONTENT) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/SMTPConfigurationController.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/SMTPConfigurationController.java index 47a4df0..042794a 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/SMTPConfigurationController.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/SMTPConfigurationController.java @@ -6,6 +6,9 @@ import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagem import java.util.HashMap; import java.util.Map; +import javax.ws.rs.core.Response; + +import org.keycloak.admin.client.resource.UserResource; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -16,10 +19,10 @@ import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource; import com.knecon.fforesight.tenantusermanagement.api.external.SMTPConfigurationResource; import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration; +import com.knecon.fforesight.tenantusermanagement.model.SMTPResponse; import com.knecon.fforesight.tenantusermanagement.service.RealmService; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -74,13 +77,31 @@ public class SMTPConfigurationController implements SMTPConfigurationResource, P } - @SneakyThrows @Override @PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')") - public void testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) { + public SMTPResponse testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) { - var propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel); - realmService.realm(TenantContext.getTenantId()).testSMTPConnection(propertiesMap); + // We set the service account email to what the user set in the "from" field because we need the service account email in order to send the test SMTP email. + // After we send the email we reset it back to empty. + // Will be replaced with an actual email coming from a different field in the future. + Response response = null; + try { + Map propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel); + UserResource serviceAccount = realmService.getServiceAccount("master"); + + realmService.setServiceAccountEmail(serviceAccount, smtpConfigurationModel.getFrom()); + + response = realmService.realm(TenantContext.getTenantId()).testSMTPConnection(propertiesMap); + log.info("Test SMTP response {}, reason {}", response.getStatus(), response.getStatusInfo().getReasonPhrase()); + + realmService.setServiceAccountEmail(serviceAccount, ""); + + return SMTPResponse.builder().statusCode(response.getStatusInfo().getStatusCode()).reasonPhrase(response.getStatusInfo().getReasonPhrase()).build(); + } finally { + if (response != null) { + response.close(); + } + } } diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPConfiguration.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPConfiguration.java index 1d29896..f237255 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPConfiguration.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPConfiguration.java @@ -12,9 +12,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @Schema(description = "Object containing the SMTP configuration.") public class SMTPConfiguration { - - @Schema(description = "Parameter containing the ID of the SMTP configuration.") - private String id; + @Schema(description = "Parameter containing the email of the sender.") private String from; @Schema(description = "Parameter containing the display name of the sender.") diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPResponse.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPResponse.java new file mode 100644 index 0000000..29758c1 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/SMTPResponse.java @@ -0,0 +1,21 @@ +package com.knecon.fforesight.tenantusermanagement.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "Object containing a simplified version of the SMTP test connection response.") +public class SMTPResponse { + + @Schema(description = "Parameter containing status code of the response.") + private int statusCode; + @Schema(description = "Parameter containing the reason phrase of the response.") + private String reasonPhrase; + +} diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/RealmService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/RealmService.java index d1381d2..80a06bc 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/RealmService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/RealmService.java @@ -1,13 +1,17 @@ package com.knecon.fforesight.tenantusermanagement.service; import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.representations.idm.UserRepresentation; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import com.knecon.fforesight.tenantcommons.model.AuthDetails; import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties; +import com.nimbusds.jwt.JWTParser; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @Service @@ -19,12 +23,26 @@ public class RealmService { private final TenantUserManagementProperties tenantUserManagementProperties; - public RealmResource realm(String tenantId) { return keycloak.getAdminClient().realm(tenantId); } + public void setServiceAccountEmail(UserResource serviceAccount, String email) { + + UserRepresentation user = serviceAccount.toRepresentation(); + user.setEmail(email); + serviceAccount.update(user); + } + + @SneakyThrows + public UserResource getServiceAccount(String realm) { + + String token = keycloak.getAdminClient().tokenManager().getAccessToken().getToken(); + String userId = JWTParser.parse(token).getJWTClaimsSet().getSubject(); + return realm(realm).users().get(userId); + } + public AuthDetails getOpenIdConnectDetails(String tenantId) { diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java index 799fd2a..393687a 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java @@ -299,7 +299,7 @@ public class UserService { try { changeEmailClient.tokenManager().getAccessTokenString(); } catch (NotAuthorizedException e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid password"); } changeEmailClient.close(); diff --git a/src/test/java/com/knecon/fforesight/tests/SMTPConfigurationTest.java b/src/test/java/com/knecon/fforesight/tests/SMTPConfigurationTest.java index 1cf095f..7cbec3b 100644 --- a/src/test/java/com/knecon/fforesight/tests/SMTPConfigurationTest.java +++ b/src/test/java/com/knecon/fforesight/tests/SMTPConfigurationTest.java @@ -9,12 +9,16 @@ import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest; import com.knecon.fforesight.feigntestclients.external.SMTPConfigurationClient; import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration; +import com.knecon.fforesight.tenantusermanagement.service.RealmService; public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrationTest { @Autowired private SMTPConfigurationClient smtpConfigurationClient; + @Autowired + private RealmService realmService; + @Test public void testSMTPConfiguration() { @@ -27,14 +31,7 @@ public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrati System.out.println(e.getMessage()); } - SMTPConfiguration newConfig = new SMTPConfiguration(); - newConfig.setAuth(true); - newConfig.setFrom("from@knecon.com"); - newConfig.setHost("test.knecon.com"); - newConfig.setPassword("secret"); - newConfig.setUser("user"); - newConfig.setStarttls(true); - newConfig.setSsl(false); + SMTPConfiguration newConfig = provideSMTPConfiguration(); smtpConfigurationClient.updateSMTPConfiguration(newConfig); var currentSMTPConfiguration = smtpConfigurationClient.getCurrentSMTPConfiguration(); @@ -50,4 +47,39 @@ public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrati } + @Test + public void testSMTPConnection() { + + TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID); + + SMTPConfiguration smtpConfiguration = provideSMTPConfiguration(); + + smtpConfigurationClient.updateSMTPConfiguration(smtpConfiguration); + var response = smtpConfigurationClient.testSMTPConfiguration(smtpConfiguration); + + // Fails because we are not using a real email/pw + assertThat(response.getStatusCode()).isEqualTo(500); + assertThat(response.getReasonPhrase()).isEqualTo("Internal Server Error"); + + // Check the email was cleared + var serviceAccount = realmService.getServiceAccount("master"); + assertThat(serviceAccount.toRepresentation().getEmail()).isNull(); + + TenantContext.clear(); + } + + private SMTPConfiguration provideSMTPConfiguration() { + + SMTPConfiguration smtpConfiguration = new SMTPConfiguration(); + smtpConfiguration.setAuth(true); + smtpConfiguration.setFrom("from@knecon.com"); + smtpConfiguration.setHost("test.knecon.com"); + smtpConfiguration.setPassword("secret"); + smtpConfiguration.setUser("user"); + smtpConfiguration.setStarttls(true); + smtpConfiguration.setSsl(false); + + return smtpConfiguration; + } + }