Wildfly GraphQL Smallrye #Routingcontext - graphql

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.

Related

Spring Boot #RequestScope and Hibernate schema based multi-tenancy

I'm working on a schema based multi-tenant app, in which I want to resolve the Tenant Identifier using a #RequestScope bean. My understanding is that #RequestScope uses/injects proxies for the request scoped beans, wherever they are referred (e.g. in other singleton beans). However, this is not working in the #Component that implements CurrentTenantIdentifierResolver and I get the following error when I start my service,
Caused by: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.userContext': Scope 'request' is not active for the current thread;
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Following are the relevant pieces of code.
#Component
public class CurrentTenant implements CurrentTenantIdentifierResolver {
#Autowired
private UserContext userContext;
#Override
public String resolveCurrentTenantIdentifier() {
return Optional.of(userContext)
.map(u -> u.getDomain())
.get();
}
#Component
#RequestScope
public class UserContext {
private UUID id;
private String domain;
My questions,
Isn't the proxy for the #RequestScope injected (by default)? Do I need to do anything more?
Is Hibernate/Spring trying to establish a connection to the DB at startup (even when there is no tenant available)?
Hibernate properties:
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
properties.remove(AvailableSettings.DEFAULT_SCHEMA);
properties.put(AvailableSettings.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
properties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
For the time being, I'm preventing the NullPointerException by checking if we are in the RequestContext. However, a connection still gets established to the master database (although I've explicitly specified the dialect and am not specifying hbm2ddl.auto). Since this connection is not associated with any schema, I'd like to avoid making it, so that it does not look for any tables that it won't find anyways.
What seems to be happenning is that when a HTTP request is received, hibernate is trying to resolve the current tenant identifier, even before my #RequestScope bean is created (and even before my #RestController method is called.) If a provide the default connection to the databse, I then get the following error. If I don't provide a connection, it throws an exception and aborts.
2021-09-26 11:55:44.882 WARN 19759 --- [nio-8082-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 42P01
2021-09-26 11:55:44.882 ERROR 19759 --- [nio-8082-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "employees" does not exist
Position: 301
2021-09-26 11:55:44.884 ERROR 19759 --- [nio-8082-exec-2] o.t.n.controller.EmployeeController : Exception: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet

Wildfly / Infinispan HTTP session replication hits ClassNotFoundException when unmarshalling CGLIB Session Bean

I'm running Wildfly 20.0.1.Final in standalone, two-node cluster. I'm trying to implement HTTP Session sharing between the nodes.
In my Spring web application I have <distributable/> in my web.xml.
My session object is this:
package my.package;
#Component
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public class MySessionBean implements Serializable {
// omitted for brevity
}
As you can see, I have ScopedProxyMode.TARGET_CLASS.
When I perform a failover in Wildfly, my HTTP Session can't be restored however, as I hit this warning:
2021-02-22 13:24:18,651 WARN [org.wildfly.clustering.web.infinispan] (default task-1) WFLYCLWEBINF0007:
Failed to activate attributes of session Pd9oI0OBiZSC9we0uXsZdBwkLnadO1l4TUfvoJZf:
org.wildfly.clustering.marshalling.spi.InvalidSerializedFormException:
java.lang.ClassNotFoundException: my.package.MySessionBean$$EnhancerBySpringCGLIB$$9c0fa1df
from [Module "deployment.myDeployment.war" from Service Module Loader]
...
Caused by: java.lang.ClassNotFoundException: my.package.MySessionBean$$EnhancerBySpringCGLIB$$9c0fa1df from [Module "deployment.myDeployment.war" from Service Module Loader]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:255)
at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:398)
at org.jboss.marshalling#2.0.9.Final//org.jboss.marshalling.ModularClassResolver.resolveClass(ModularClassResolver.java:133)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadClassDescriptor(RiverUnmarshaller.java:1033)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadNewObject(RiverUnmarshaller.java:1366)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:283)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:216)
at org.jboss.marshalling#2.0.9.Final//org.jboss.marshalling.AbstractObjectInput.readObject(AbstractObjectInput.java:41)
at org.wildfly.clustering.marshalling.spi#20.0.1.Final//org.wildfly.clustering.marshalling.spi.util.MapExternalizer.readObject(MapExternalizer.java:65)
...
Note, that the ClassNotFoundException is complaining because the lack of my.package.MySessionBean$$EnhancerBySpringCGLIB$$9c0fa1df, which is the Spring-enhanced bean of my MySessionBean bean.
Changing to ScopedProxyMode.INTERFACES is not an option.
Can you please point me in the right direction with this?
I managed to fix this by creating a simple POJO, called MySessionDTO, and using that in my session.
So initially I had this (which threw the exception in the question):
request.getSession().setAttribute("mySession", mySessionBean);
...and after I created MySessionDTO (see below), I refactored it into this:
request.getSession().setAttribute("mySession", mySessionBean.getMySessionDTO());
MySessionDTO is a simple POJO:
package my.package;
import java.io.Serializable;
public class MySessionDTO extends MySessionBean implements Serializable {
public MySessionDTO (MySessionBean mySessionBean) {
this.setAttributeX(mySessionBean.getAttributeX());
this.setAttributeY(mySessionBean.getAttributeY());
}
}

SpEL evaluation of path variable in WebsocketSecurityConfiguration

I am running a JHipster 6.1.2 Gateway with Websockets and am trying to restrict access to a messaging topic, so that users can only subscribe to topics of the institution they belong to. So basically I want to perform a check on the id from the subscription path.
My current solution is based on https://stackoverflow.com/a/44895369/4246074 and looks as follows:
WebsocketSecurityConfiguration.java:
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
//...
//User can only subscribe to own institution topic
.simpSubscribeDestMatchers("/topic/institution.{id}")
.access("#institutionIdGuard.checkInstitutionId(#id)")
//...
}
InstitutionIdGuard.java:
#Component
public class InstitutionIdGuard {
public boolean checkInstitutionId(Long institutionId) {
//validation logic for institutionId would go here
return true;
}
The problem:
Apparently the SpEL expression can't access {id} from the path because i get a nullpointer error with the following log:
2019-08-08 10:30:08.367 ERROR 31097 --- [ XNIO-1 I/O-1] o.s.w.s.m.StompSubProtocolHandler : Failed to send client message to application via MessageChannel in session j2a0jlos. Sending STOMP ERROR to client.
org.springframework.messaging.MessageDeliveryException: Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression '#institutionIdGuard.checkInstitutionId(#id)'
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:146)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:122)
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:284)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:324)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.delegateMessages(AbstractSockJsSession.java:386)
at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.handleMessage(WebSocketServerSockJsSession.java:195)
at org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler.handleTextMessage(SockJsWebSocketHandler.java:93)
at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:113)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:84)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at io.undertow.websockets.jsr.FrameHandler$7.run(FrameHandler.java:286)
at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:170)
at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:167)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:604)
at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:594)
at io.undertow.websockets.jsr.FrameHandler.invokeTextHandler(FrameHandler.java:266)
at io.undertow.websockets.jsr.FrameHandler.onFullTextMessage(FrameHandler.java:317)
at io.undertow.websockets.core.AbstractReceiveListener$2.complete(AbstractReceiveListener.java:156)
at io.undertow.websockets.core.AbstractReceiveListener$2.complete(AbstractReceiveListener.java:152)
at io.undertow.websockets.core.BufferedTextMessage.read(BufferedTextMessage.java:105)
at io.undertow.websockets.core.AbstractReceiveListener.readBufferedText(AbstractReceiveListener.java:152)
at io.undertow.websockets.core.AbstractReceiveListener.bufferFullMessage(AbstractReceiveListener.java:90)
at io.undertow.websockets.jsr.FrameHandler.onText(FrameHandler.java:182)
at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:44)
at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:33)
at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:951)
at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:932)
at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88)
at org.xnio.nio.WorkerThread.run(WorkerThread.java:561)
Caused by: java.lang.IllegalArgumentException: Failed to evaluate expression '#institutionIdGuard.checkInstitutionId(#id)'
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
at org.springframework.security.messaging.access.expression.MessageExpressionVoter.vote(MessageExpressionVoter.java:57)
at org.springframework.security.messaging.access.expression.MessageExpressionVoter.vote(MessageExpressionVoter.java:39)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:63)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
at org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor.preSend(ChannelSecurityInterceptor.java:69)
at org.springframework.messaging.support.AbstractMessageChannel$ChannelInterceptorChain.applyPreSend(AbstractMessageChannel.java:178)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:132)
... 37 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method checkInstitutionId(null) cannot be found on type com.mycompany.websocketgateway.security.InstitutionIdGuard
at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134)
at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:390)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:300)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:26)
... 44 common frames omitted
I would be grateful for any ideas how to make my solution work or other other ways of performing checks in the id.
I found the solution on the Spring Security issue tracker. Apparently before Spring Security 5.2 you can pass the implicit message variable to the SpEL expression
.simpSubscribeDestMatchers("/topic/institution.*")
.access("#institutionIdGuard.checkInstitutionId(authentication, message)")
Then in the verification method its possible to get the path from the message and do your own verification with it:
public boolean checkInstitutionId(Authentication authentication, Message<?> message) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
String topic = sha.getDestination();
String id = topic.replace("/topic/institution/", "");
//validation logic for institutionId would go here
return true;
}
Spring Security 5.2 should have fixed the issue according to this pull request.

Spring-boot repository not being registered as a valid bean

Spring-boot does not seem to want to register my repository as a valid bean :( Here is the error I am seeing:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.frustrated.stats.MyRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
The underlying cause seems to be:
o.s.c.a.ClassPathBeanDefinitionScanner : Ignored because not a concrete top-level class: file [/Users/me/Code/my_app/my_service/build/classes/java/main/com/frustrated/stats/MyRepository.class]
Have I configured my application improperly somewhere?
Here are my files:
MyRepository.java:
package com.frustrated.stats;
#Repository
public interface MyRepository extends JpaRepository<StatsEvent, Long> {}
StatsEvent.java:
package com.frustrated.stats;
#Entity
public class StatsEvent { ... }
Application.java:
#SpringBootApplication
#ComponentScan(basePackages = { "com.frustrated" })
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer {
Here is my package structure:
com:
frustrated:
- Application.java
stats:
- MyRepository.java
- StatsEvent.java
Attempts to Debug
After trying a lot of different annotations, I thought it may be more productive to simply step through the registration process. I have traced the code, and it seems to be failing here:
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
It is the metadata.hasAnnotatedMethods(Lookup.class.getName()) that is false and causing my repository to not be instantiated as such.
It may also be of note that my StatsEvent is also ignored because:
o.s.c.a.ClassPathBeanDefinitionScanner : Ignored because not matching any filter: file [/Users/me/Code/my_app/my_service/build/classes/java/main/com/frustrated/stats/StatsEvent.class]
Any help would be greatly appreciated!
Judging by MyRepository extending JpaRepository, you are trying to use Spring Data JPA but your have excluded the auto-configuration for Hibernate and a DataSource. Unless you have some manual configuration for Hibernate or another JPA provider that you haven't shown, the support for JpaRepository will be disabled as it requires a JPA provider and a data source.
Try to add this on top of your Application.class
#EnableJpaRepositories(basePackages = "com.frustrated.stats.MyRepository")
#ComponentScan("com.frustrated.stats.service") // if you have it
#EntityScan("com.frustrated.stats.entity") // of your entities

Spring Boot with Embedded Mongo : Cannot assign requested address: JVM_Bind

I am trying to setup a JUnit test for a Spring Boot with embedded Mongo & Kafka :-
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = {AccountingApplication.class})
#DataMongoTest
public class BaseEmbeddedTest {
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true);
#Autowired
private MongoTemplate mongoTemplate;
#Test
public void emptyTest(){
}
}
src/test/resources/application.yml :-
spring:
data:
mongodb:
port: 0
kafka:
bootstrap-servers: ${spring.embedded.kafka.brokers}
PROBLEM
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [de.flapdoodle.embed.mongo.config.IMongodConfig]: Factory method 'embeddedMongoConfiguration' threw exception; nested exception is java.net.BindException: Cannot assign requested address: JVM_Bind
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
... 140 more
Caused by: java.net.BindException: Cannot assign requested address: JVM_Bind
at java.net.DualStackPlainSocketImpl.bind0(Native Method)
at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190)
at java.net.ServerSocket.bind(ServerSocket.java:375)
at java.net.ServerSocket.<init>(ServerSocket.java:237)
at de.flapdoodle.embed.process.runtime.Network.getFreeServerPort(Network.java:80)
at org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration.embeddedMongoConfiguration(EmbeddedMongoAutoConfiguration.java:147)
What am I doing wrong here ?
Version:-
dependencyManagementPluginVersion = '1.0.3.RELEASE'
springBootVersion = '1.5.6.RELEASE'
springCloudVersion = 'Dalston.SR2'
projectVersion = '0.0.1-SNAPSHOT'
javaVersion = 1.8
kotlinVersion = '1.1.4'
This annotation: #DataMongoTest causes Spring Boot to create an embedded Mongo instance. The exception messages tells us that the embedded Mongo instance cannot start because there is already a process running on the port it is trying to run on.
The embedded Mongo instance is configured by EmbeddedMongoAutoConfiguration and the strategy applied by Spring Boot - for port allocation - is as follows:
if configured Mongo port > 0 then
use the configured port
else
assign a random port
end
So, I suspect that your test context is configured with a non zero value for spring.data.mongodb.port. I know you posted your application.yml which implies that you are - correctly - assigning a zero value to spring.data.mongodb.port but if you put a breakpoint inside the EmbeddedMongoAutoConfiguration constructor and peek inside the properties parameter I think you'll see that the actual value in use by that configuration class is not zero. If the port value passed to EmbeddedMongoAutoConfiguration is actually zero but you are still getting the JVM_Bind error then that implies that this call: Network.getFreeServerPort(this.getHost()) is not returning a free port and that seems unlikely.
In order to fix this issue: as long as you configure your test context with spring.data.mongodb.port=0 then the embedded Mongo instance will be assigned a random port and this random port will be made known to other aspects of your Spring context (such as your MongoTemplate) which need to talk to that Mongo instance.

Resources