Springboot Testing via Greenmail - spring

I tried to test my Gmail smtp in my Spring-Application and followed this Tutorial. I have implemented it as specified in the tutorial, but my EmailService throws a MailSendException:
org.springframework.mail.MailSendException: Mail server connection failed; nested exception is com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 2525; timeout 5000;
nested exception is:
java.net.ConnectException: Connection refused: connect. Failed messages: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 2525; timeout 5000;
nested exception is:
java.net.ConnectException: Connection refused: connect
; message exception details (1) are:
Failed message 1:
com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 2525; timeout 5000;
nested exception is:
java.net.ConnectException: Connection refused: connect
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2210)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:722)
at javax.mail.Service.connect(Service.java:342)
Has anyone a tip how to solve this? (Never tested something like SMTP/Email and therefore just followed the tutorial above.
EDIT: I can send emails without any problem manually, but I need to test it.
my application.yml:
spring.mail.host: smtp.gmail.com
spring.mail.port: 587
spring.mail.properties.mail.smtp.starttls.enable: true
spring.mail.username: ************#gmail.com
spring.mail.password: ***************
spring.mail.properties.mail.smtp.starttls.required: true
spring.mail.properties.mail.smtp.auth: true
spring.mail.properties.mail.smtp.connectiontimeout: 5000
spring.mail.properties.mail.smtp.timeout: 5000
spring.mail.properties.mail.smtp.writetimeout: 5000

From the log you can see, that Spring is trying to connect to port 2525 but can't make connection. That means that there's no running mail server on this port, during test execution - which should be provided by JUnit Rule implementation (If you're using JUnit5 use Extensions)
Make sure that your test configuration is properly setup. That means, check whether your test code, e.g.
#Rule
public SmtpServerRule smtpServerRule = new SmtpServerRule(2525);
matches
src/test/resources/application.yml
spring:
mail:
default-encoding: UTF-8
host: localhost
jndi-name:
username: username
password: secret
port: 2525
properties:
mail:
debug: false
smtp:
debug: false
auth: true
starttls: true
protocol: smtp
test-connection: false
I would also recommend to update the code from the Tutorial and add test user - as it could be used with secured protocols.
public class SmtpServerRule extends ExternalResource {
// omitted for brevity
#Override
protected void before() throws Throwable {
super.before();
smtpServer = new GreenMail(new ServerSetup(port, null, "smtp"));
smtpServer.addUser("username", "secret");
smtpServer.start();
}
// omitted for brevity
}
Also, refer to the official GreenMail documentation for more specific setup.

Related

How to disable oauth2 client registration in #WebFluxTest?

Having configured oauth2:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${keycloak.server-url}/realms/${keycloak.realm}
client:
provider:
keycloak:
issuer-uri: ${keycloak.server-url}/realms/${keycloak.realm}
user-name-attribute: email
registration:
keycloak:
client-id: ${keycloak.client-id}
client-secret: ${keycloak.client-secret}
scope: openid
I wonder how to run a #WebFluxTest for a controller without having to run an actual keycloak server. The following test runs fine, when I have the local keycloak server running:
#WebFluxTest(TaskController::class)
class TaskControllerTest {
#Autowired
private lateinit var client: WebTestClient
#MockkBean
private lateinit var taskService: TaskServiceImpl
#Test
#WithMockUser(roles = ["user"])
fun getByIds() {
val ids = setOf<Long>(1, 2)
every { taskService.getByIds(ids, any()) } returns flowOf(
TaskWithRating(
Task(
id = 1,
title = "title",
description = "desc"
),
Rating(0, 0),
)
)
client
.get()
.uri { uriBuilder ->
uriBuilder
.path("/tasks/byIds")
.queryParam("ids", ids)
.build()
}
.exchange()
.expectStatus().isOk
.expectBody()
.jsonPath("$.length()").isEqualTo(1)
.jsonPath("$.1.task.id").isEqualTo(1)
}
}
When I stop the local keycloak server, the test fails, because spring tries to read the oauth2 configuration during startup, while creating the ApplicationContext:
Failed to load ApplicationContext
...
Factory method 'clientRegistrationRepository' threw exception; nested exception is java.lang.IllegalArgumentException: Unable to resolve Configuration with the provided Issuer of "https://keycloak.local.com/auth/realms/myrealm"
...
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://keycloak.local.com/auth/realms/myrealm/.well-known/openid-configuration": Connection refused; nested exception is java.net.ConnectException: Connection refused
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:784)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:669)
at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:156)
at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:209)
... 175 more
Caused by: java.net.ConnectException: Connection refused
The test does not need it, because the user is mocked anyway. So I think of disabling the auth2 configuration in #WebFluxTest, but I could not find out how and if this is a valid approach?
I tried this.
#WebFluxTest(
controllers = [TaskController::class],
excludeAutoConfiguration = [ReactiveSecurityAutoConfiguration::class],
)
and this
#WebFluxTest(
controllers = [TaskController::class],
excludeAutoConfiguration = [ReactiveOAuth2ClientAutoConfiguration::class],
)
But the error is the same.
Turns out I was on the right path. Both, the oauth2 client and resource server auto configurations must be disabled. This works:
#WebFluxTest(
controllers = [TaskController::class],
excludeAutoConfiguration = [
ReactiveOAuth2ClientAutoConfiguration::class,
ReactiveOAuth2ResourceServerAutoConfiguration::class,
],
)
Note that this bypasses potentially disabled features and hence, for example, requires a csrf() mutation on the test client:
client
.mutateWith(SecurityMockServerConfigurers.csrf())
An alternative to this would be to configure a test-configuration for the security setup using #Import(TestSecurityConfiguration::class).

JavaMail Spring boot properties does not recognize properties

I have a spring boot application with javamail, i configure spring.properties but it seems to not take properties to send email.
#mail properties
spring.mail.host = smtp.gmail.com
spring.mail.port = 587
spring.mail.username = eeee#gmail.com
spring.mail.password = eeee
spring.mail.protocol = smtp
spring.mail.defaultEncoding=UTF-8
spring.mail.smtp.starttls.enable=false
spring.mail.smtp.starttls.required=false
spring.mail.smtp.auth=true
spring.mail.smtp.connectiontimeout=5000
spring.mail.smtp.timeout=5000
spring.mail.smtp.writetimeout=5000
here the java code to send email
#Service
public class MailSenderServiceImpl implements IMailSenderService{
#Autowired
JavaMailSender mailSender;
#Override
public void sendEmailStatusNotification(Shipment shipment) throws IOException, MessagingException{
final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
message.setFrom(shipment.getSender().getSenderEmail());
message.setTo(new String[] {shipment.getRecipient().getRecipientEmail()});
message.setSubject("Shipment "+shipment.getTrackingNumber()+"status update");
message.setText("the shipment status is now :"+shipment.getStatus().getLabel());
this.mailSender.send(mimeMessage);
}
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
here we see this error
org.springframework.mail.MailSendException: Mail server connection failed; nested exception is com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection refused: connect. Failed messages: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection refused: connect; message exceptions (1) are:
Failed message 1: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection refused: connect
Any idea ?
By default spring.properties is not a properties source, try putting them in application.properties.

Got bad greeting from SMTP host: smtp.yandex.ru, port: 465, response: [EOF]] with root cause Yandex

I use brand new spring boot project with next Maven dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
Implementation of method
#Autowired
JavaMailSender emailSender;
#GetMapping("/send")
public String send() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("some-name#domain.com");
message.setTo("toReceiver#gmail.com");
message.setSubject(null);
message.setText("Hello World");
emailSender.send(message);
return "success send email " + now();
}
application.yml
host: smtp.yandex.ru
username: some-name#domain.io
password: password
port: 465
And receive the next exception
2020-08-03 23:02:35.102 ERROR 21615 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.mail.MailSendException: Mail server connection failed; nested exception is javax.mail.MessagingException: Got bad greeting from SMTP host: smtp.yandex.com, port: 465, response: [EOF].
Failed messages: javax.mail.MessagingException: Got bad greeting from SMTP host: smtp.yandex.com, port: 465, response: [EOF]; message exceptions (1) are: Failed message 1: javax.mail.MessagingException: Got bad greeting from SMTP host: smtp.yandex.com, port: 465, response: [EOF]] with root cause
But the same code works perfectly with Mailtrap service
According to this link I used not secure 25 port
After which I received the next exception
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.mail.MailSendException: Mail server connection failed; nested exception is com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.yandex.ru, 25; timeout -1;
587 port =
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: [EOF];
I guess a problem with SSL
Similar issue
With port: 465 (SMTP Protocol), you can try enable ssl via properties:
"mail.smtp.ssl.enable": true
mail.smtp.ssl.enable: If set to true, use SSL to connect and use the SSL port by default. Defaults to false for the "smtp" protocol and true for the "smtps" protocol.
Using spring boot, add properties to application.yml:
mail:
...
properties:
"mail.smtp.ssl.enable": true
References:
Outgoing mail
mail server address — smtp.yandex.com
connection security — SSL
port — 465
The SMTP protocol provider supports the following properties:
https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html
This properties help me
spring:
boot:
admin:
mail:
to: ${spring.mail.username}, test#yandex.ru
from: ${spring.mail.username}
mail:
host: smtp.yandex.ru
username: test#yandex.ru
password: password
port: 587
protocol: smtp
properties:
"mail.transport.protocol": smtp
"mail.smtp.auth": true
"mail.smtp.starttls.enable": true

Not able to send email using Javamailserver, Connection refused

I'm trying to send an email from my Spring boot application. Here are my configuration properties.
spring:
application:
name: spring-base-template
profiles:
active: ${env:local}
mail:
default-encoding: UTF-8
host: company.host.name
username: myusername#domain.com
password: mypassword
port: 587
properties:
mail:
smtp:
auth: true
starttls:
enable: true
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
fallback: false
protocol: smtp
test-connection: false
My sample code look like this
try {
SimpleMailMessage mail = new SimpleMailMessage();
mail.setTo("myusername#domain.com");
mail.setFrom("myusername#domain.com");
mail.setSubject("Registration Confirmation");
mail.setText("You are registered Successfully! Thank you for being with us!");
javaMailSender.send(mail);
} catch(Exception e) {
e.printStackTrace();
}
When I try to send the email, following is the exception
org.springframework.mail.MailSendException: Mail server connection failed; nested exception is javax.mail.MessagingException: Could not connect to SMTP host: company.host.name, port: 587;
nested exception is:
java.net.SocketException: Connection reset. Failed messages: javax.mail.MessagingException: Could not connect to SMTP host: company.host.name, port: 587;
nested exception is:
java.net.SocketException: Connection reset; message exception details (1)
are:
Failed message 1:
javax.mail.MessagingException: Could not connect to SMTP host:
company.host.name, port: 587;
Any ideas would be greatly appreciated.

Spring Boot: disable https for actuator endpoint

Is possible to config Spring Booot application to have some url with non-secure (non-https) ex: /actuator/info, /actuator/prometheous While All other enpoint forced to be secure?
Enable SSL like this:
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=xxxx
I was try to set:
management.server.port=8762
management.server.ssl.enabled=false
and
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().antMatchers("/actuator").requiresInsecure();
http.requiresChannel().anyRequest().requiresSecure();
// accept only IP in range to access metric
http.csrf().disable().authorizeRequests().antMatchers("/actuator/**")
.access("hasIpAddress('" + ipRangeMain + "') or hasIpAddress('" + ipRangeSecond + "')");
}
But it's still not working
When I try to access /actuator/info, it show error:
Bad Request
This combination of host and port requires TLS.
"/actuator/info" endpoint need to accessed by Load Balancer and "/actuator/prometheous" to Monitoring, But now it not work.
In case anybody stumbles over this as well. I solved it using 2 Ports like #user3611168 suggested:
applycation.yml:
server:
port: 10000
ssl:
key-store-type: ...
key-store: ...
key-store-password: ...
key-alias: ...
management:
server:
port: 8080
ssl:
enabled: false
Port 10000 with SSL. Port for Prometheus 8080 without SSL

Resources