How to disable oauth2 client registration in #WebFluxTest? - spring-boot

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).

Related

Wildfly GraphQL Smallrye #Routingcontext

I have setup a Wildfly 27 server with GraphQL Featurepack.
I need to access the Request Headers to fetch a bearertoken.
I find no good doc on how to do this.
My assumption is that i should inject the RoutingContext like this.
#GraphQLApi
#ApplicationScoped
public class FilmResource {
#Inject
GalaxyService service;
#Inject
RoutingContext routingContext;
#Query("allFilms")
#Description("Get all Films from a galaxy far far away")
public List<Film> getAllFilms() {
return service.getAllFilms();
}
}
However this fails runtime with
ERROR [controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("deploy") failed - address: ([("deployment" => "cms-graph-ql-1.1.0-SNAPSHOT.war")]) - failure description:
{"WFLYCTL0080: Failed services" => {"jboss.deployment.unit."cms-graph-ql-1.1.0-SNAPSHOT.war".WeldStartService" => "Failed to start service
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type RoutingContext with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject com.scanreco.cms.microprofile.graphql.FilmResource.routingContext
at com.scanreco.cms.microprofile.graphql.FilmResource.routingContext(FilmResource.java:0)
I would be greatful for any help.
So i figured this one out.
Since i am using WF and not Quarkus i am forced to fetch the request headers via #Webfilter.
(Vert.x is not at all used for HTTP in my stack).
This is a minor inconvenince but quite alright.
#WebFilter(servletNames = {"SmallRyeGraphQLExecutionServlet"},
filterName = "CMS Azure AD Filter",
description = "Filter all ServletCalls for Azure AD Bearer token",
dispatcherTypes = {DispatcherType.REQUEST})
#RequestScoped
public class CmsGraphQlServletAzureAdFilter implements Filter {
The name of the executionservlet was found in their gitRepo.

Spring Test Wants To Connect to Database

I am trying to write some tests for my application and encountered following problem:
I defined a application-test.yml with folling content:
server:
port: 8085
spring:
security:
oauth2:
resourceserver:
jwt:
# change localhost:8081 with container name
issuer-uri: http://localhost:8081/auth/realms/drivingschool
jwk-set-uri: http://localhost:8081/auth/realms/drivingschool/protocol/openid-connect/certs
keycloak:
realm: drivingschool
auth-server-url: http://localhost:8081/auth
ssl-required: external
resource: client-interface
use-resource-role-mappings: true
credentials:
secret: xxx
bearer-only: true
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private StudentService service;
#MockBean
private StudentRepository repository;
#Test
public void contextLoads(){}
//more tests
}
the test all pass green BUT in the log i can see, that my app tries to connect to a database configured in my (basic) application.yml.
ava.sql.SQLNonTransientConnectionException: Could not connect to address=(host=localhost)(port=9005)(type=master) : Socket fail to connect to host:localhost, port:9005. Verbindungsaufbau abgelehnt (Connection refused)
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73) ~[mariadb-java-client-2.6.1.jar:na]
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:192) ~[mariadb-java-client-2.6.1.jar:na]
jpa:
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
use-new-id-generator-mappings: false
ddl-auto: create
datasource:
url: jdbc:mariadb://localhost:9005/waterloo
username: waterloo
password: xxx
driver-class-name: org.mariadb.jdbc.Driver
when creating a application-prod.yml and moving all content from application.yml to application-prod.yml it tells me I have to configure a datasource URL
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
I have the following questions:
Do the application.yml files get layered (*-test.yml settings on top of application.yml)?
Why does Spring try to build a connection to my database when I am not setting a datasource on my application-test.yml AND mocking the repository on the test?
Is it normal that Spring trys to establish a connection at this part?
3.1) If not: How to i prevent it from doing so?
Thanks and kind regards!
Failed to determine a suitable driver class
You need to add mariadb driver dependency to your gradle or maven file.
https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client/2.6.2
Make sure that dependency scope is suitable for test
If you already have and its still not working try to clean and rebuild your project.
Your Questions:
Do the application.yml files get layered (*-test.yml settings on top
of application.yml)?
If you add #ActiveProfiles("test") to you TestClass Spring will try to find an application-test.yml and overrrides application.yml properties with the given properties
Why does Spring try to build a connection to my database when I am not
setting a datasource on my application-test.yml AND mocking the
repository on the test?
Thats the magic of spring boot - it has default configurations for everything. You just need to set the Datasource properties and it will create the bean by itself.
Is it normal that Spring trys to establish a connection at this part? 3.1) If not: How to i prevent it from doing so?
You are starting the whole spring context with #SpringBootTest Annotation.
So it will startup all Repositories and try to establish connection to your database. If you don't want spring to startup the database layer you can just use #WebMvcTest
eg:
#RunWith(SpringRunner.class)
#WebMvcTest
#ActiveProfiles("test")
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#Test
public void contextLoads(){}
//more tests
}
Check this out: https://spring.io/guides/gs/testing-web/
If you need to startup the whole SpringContext you can also disable Spring Data AutoConfiguration with:
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
Check this out: https://www.baeldung.com/spring-data-disable-auto-config
missed following annotation:
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})

Springboot Testing via Greenmail

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.

Spring Boot with OAuth2 behind reverse proxy

I'm new with Spring Security and trying to develop Spring Boot app with Google login using OAuth2 which runs under hostname:8080. This app is behind Apache reverse proxy server https://url.com.
Spring Boot version 2.1.0
Spring Security version 5.1.1
build.gradle:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.security:spring-security-oauth2-client")
implementation("org.springframework.security:spring-security-oauth2-jose")
}
application.yml:
oauth2:
client:
registration:
google:
clientId: <clientId>
clientSecret: <clientSecret>
scope: profile, email, openid
server:
use-forward-headers: true
servlet:
session:
cookie:
http-only: false
Spring Security config:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login();
}
}
I request https://url.com
Get redirected to https://accounts.google.com/signin/oauth/
When authenticated get redirected back to
https://url.com/login/oauth2/code/google?state={state}&code={code}&scope=openid+email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.me&authuser=0&session_state={session_state}&prompt=none which timed out with error:
[invalid_token_response] An error occurred while attempting to
retrieve the OAuth 2.0 Access Token Response: I/O error on POST
request for "https://www.googleapis.com/oauth2/v4/token": Connection
timed out (Connection timed out); nested exception is
java.net.ConnectException: Connection timed out (Connection timed out)
Is this error caused by the proxy server settings or boot app? Thanks for help.
Solved. I had to set the JVM parameters:
https.proxyHost=[host]
https.proxyPort=[port]
http.proxyHost=[host]
http.proxyPort=[port]

Creating RDS Datasource fails with "AmazonRDSException: The security token included in the request is invalid"

I am unable to start an application with
#EnableRdsInstance(databaseName = "test",
dbInstanceIdentifier = "test",
password = "password",
username = "username",
readReplicaSupport = true
)
The exception I get is:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'test': Invocation of init method failed; nested exception is com.amazonaws.services.rds.model.AmazonRDSException: The security token included in the request is invalid. (Service: AmazonRDS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 925519ec-582e-11e7-8ca6-8159eafdc3e8)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
...
Caused by: com.amazonaws.services.rds.model.AmazonRDSException: The security token included in the request is invalid. (Service: AmazonRDS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 925519ec-582e-11e7-8ca6-8159eafdc3e8)
at ...
Tried all configurations that are suggested in Spring Cloud AWS Docs including ENV variable, System.setProperties(), and in application.yml as below
cloud:
aws:
credentials:
accessKey: XXXXXXX
secretKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
region:
static: us-east-2
also tried even hardcoding in in aws-beans
<beans ...>
<aws-context:context-credentials>
<aws-context:simple-credentials access-key="XXXXXXXXXX" secret-key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"/>
</aws-context:context-credentials>
<aws-context:context-resource-loader/>
<aws-context:context-region region="us-east-2" />
</beans>
and nothing works, help is appreciated....
Possible reasons to get this exception:
Make sure public access is enabled for RDS Instance and allow incoming traffic for your machine in Security Group.
Make sure internet gateway is attached with VPC and very importantly you have to use public subnet rather than private subnet.

Resources