ConversationScoped in Quarkus - quarkus

I am migrating an application from Thorntail to Quarkus. It uses the the conversation scope annotation in a bean that provides the token information during all the rest api request to any service interested in it. But in Quarkus documentation it says the conversation scope is not implemented. Is there a similar feature I can use?
Here is what I want to do:
#Path
#ApplicationScoped
public class FruitsResource {
#Inject FruitsService fruitsService;
#POST
public int post (Fruit fruit) {
return fruitsService.post(fruit);
}
}
#Provider
#ApplicationScoped
private class AuthorizationFilter implements ContainerRequestFilter {
#Inject AuthorizationHolder authorizationHolder;
#Override
public void filter (ContainerRequestContext request) {
String token = request.getHeaderString(HttpHeaders.AUTHORIZATION);
Authorization authorization = createAuthorizationFromToken(token);
authorizationHolder.setAuthorization(authorization);
}
}
#ConversationScoped
private class AuthorizationHolder {
private Authorization authorization;
#Produces
public Authorization getAuthorization () {
return authorization;
}
public void setAuthorization (Authorization authorization) {
this.authorization = authorization;
}
}
#ApplicationScoped
private class FruitsService {
#Inject Authorization authorization;
#Inject EntityManager entityManager;
#Transactional
public void post (Fruit fruit) {
// do some complex validation with the authorization object
...
// persist object
entityManager.persist(fruit);
entityManager.flush();
return fruit.getId();
}
}

Is the Authorization header present in each request? I suppose it is (or should be), in which case just using #RequestScoped instead of #ConversationScoped should work. This is probably the best thing to do, anyway.
In case the header is only present in "first" request and subsequent requests in the same session can reuse the token, then you can just replace #ConversationScoped with #SessionScoped. I think enforcing the header to be present in all requests would be better, though.
Finally, if you'd really like to emulate conversations, you can do something like this (not tested, not even written in an IDE, just from the top of my head):
#SessionScoped
private class AuthorizationHolder {
private ConcurrentMap<String, Authorization> authorizations = new ConcurrentHashMap<>();
public Authorization getAuthorization(ContainerRequestContext request) {
return authorizations.get(getConversationId(request));
}
public void setAuthorization(ContainerRequestContext request, Authorization authorization) {
this.authorizations.put(getConversationId(request), authorization);
}
private String getConversationId(ContainerRequestContext request) {
MultivaluedMap<String, String> query = request.getUriInfo().getQueryParameters();
return query.getFirst("cid");
}
}
However, as I said above, I really think you should make the bean #RequestScoped and force the clients to send the Authorization header with each request.

Related

How can I use RequestHeader with actuator endpoint?

I have customized my actuator/info endpoint and I want to use information from the header to authorize a RestTemplate call to another service.
I am implementing the InfoContributor as here:
https://www.baeldung.com/spring-boot-info-actuator-custom
I want to accept request headers in the contribute() method. For any user defined REST endpoint, I can define a #RequestHeader parameter and access headers.
But unfortunately, the InfoContributor's contribute() method takes only one parameter.
How can I access a request header inside the contribute() method?
You can autowire HttpServletRequest into your InfoContributor
import javax.servlet.http.HttpServletRequest;
#Component
public class Custom implements InfoContributor {
#Autowired
private HttpServletRequest request;
#Override
public void contribute(Info.Builder builder) {
...
request.getHeader("your header");
...
}
}
Or you can use RequestContextHolder to get hold of it
#Component
public class Custom implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
...
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
.getRequest();
request.getHeader("your header");
...
}
}

Using #FeignClient with OAuth2Authentication in Javaclient

I would like to use a #FeignClient in a simple spring boot application (CommandLineRunner) to call a microservice endpoint. How can I provide an OAuth2Authentication to call a protected endpoint like helloUser() ?
#FeignClient(name = "sampleService", contextId = "greetingService")
public interface GreetingService {
#GetMapping("/hello-anonymous")
String helloAnonymous();
#GetMapping("/hello-user")
#Secured({ Role.USER })
String helloUser();
#GetMapping("/hello-admin")
#Secured({ Role.ADMIN })
String helloAdmin();
}
You can use Feign RequestInterceptor to pass the auth header downstream:
public class FeignRequestInterceptor implements RequestInterceptor {
#Override
public final void apply(RequestTemplate template) {
template.header("Authorization", "foo token");
}
}
This way all the feign calls will be provisioned with an auth header.

How to keep a data between requests at web service?

Suppose I develop a ticket order web service. There are some steps to order a ticket and need to keep some users data between the steps.
Suppose I use Spring (Boot) technology stack and MVC
How is better to implement it?
Use stateless REST and move the date back and forth from step to step using cookies?
Store it in session context?
Use stateful beans (what are they like in Spring? Prototype? )
Use some stateful protocol, like SOAP (is it stateful?)
It depends.
1 If you want to use multiple instances of your web service (for balance load, etc) then your choice is a stateless REST and token-based authentication
2 If you don't need this functionality you can store your session information in MVC Model (It will put it in session, anyway)
#RestController
#SessionAttributes("armUserSession")
public class SessionController {
#Autowired
private LoginService loginService;
#ModelAttribute("armUserSession")
public ArmUserSession getArmUserSession() {
return new ArmUserSession();
}
#CrossOrigin
#RequestMapping({"/login"})
public ArmUserSession login(#ModelAttribute("armUserSession") ArmUserSession userSession,
Model model,
#RequestParam(required = false) String login,
#RequestParam(required = false) String password) {
if (!userSession.isLoggedIn()) {
userSession = loginService.login(login, password);
model.addAttribute("armUserSession", userSession);
}
return userSession;
}
#CrossOrigin
#RequestMapping({"/logout"})
public ArmUserSession logout(SessionStatus status) {
status.setComplete();
return new ArmUserSession();
}
}
3 You can use session scoped beans too, but it is a little more complicated.
By default Spring beans are singletons. When you want to use session scoped bean (they are not singletons) in singleton your need a proxy.
#Service
public class LoginServiceImpl implements LoginService {
#Autowired
private ArmUserSessionProxy armUserSessionProxy;
#Override
public ArmUserSession login(String login, String password) {
ArmUserSession armUserSession = armUserSessionProxy.getArmUserSession();
...................................
}
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ArmUserSessionProxy {
private ArmUserSession armUserSession = new ArmUserSession();
public ArmUserSession getArmUserSession() {
return armUserSession;
}
}

Spring Data Rest - How to receive Headers in #RepositoryEventHandler

I'm using the latest Spring Data Rest and I'm handling the event "before create". The requirement I have is to capture also the HTTP Headers submitted to the POST endpoint for the model "Client". However, the interface for the RepositoryEventHandler does not expose that.
#Component
#RepositoryEventHandler
public class ClientEventHandler {
#Autowired
private ClientService clientService;
#HandleBeforeCreate
public void handleClientSave(Client client) {
...
...
}
}
How can we handle events and capture the HTTP Headers? I'd like to have access to the parameter like Spring MVC that uses the #RequestHeader HttpHeaders headers.
You can simply autowire the request to a field of your EventHandler
#Component
#RepositoryEventHandler
public class ClientEventHandler {
private HttpServletRequest request;
public ClientEventHandler(HttpServletRequest request) {
this.request = request;
}
#HandleBeforeCreate
public void handleClientSave(Client client) {
System.out.println("handling events like a pro");
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements())
System.out.println(names.nextElement());
}
}
In the code given I used Constructor Injection, which I think is the cleanest, but Field or Setter injection should work just as well.
I actually found the solution on stackoverflow: Spring: how do I inject an HttpServletRequest into a request-scoped bean?
Oh, and I just noticed #Marc proposed this in thecomments ... but I actually tried it :)

Spring MVC accessing Spring Security ConfigAttributes?

I want to produce HTTP Response Body with an error message referencing something like _"missing ... 'CUSTOM_AUTHORITY'"_ in addition to a 403 Forbidden HTTP Status code.
My application is Spring Boot with a Spring-Security-Secured #PreAuthorize method within a Spring-MVC-REST #Controller:
MyController
#Controller
#RequestMapping("/foo")
public FooController{
#PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')")
public Object getSomething(){ ... }
}
GlobalExceptionHandlerResolver
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(AccessDeniedException.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
public Object forbidden(AccessDeniedException exception){ ... }
}
What I want is to expose/inject Collection<ConfigAttribute>. The Spring Security docs reference it.
There doesn't seem to be a straightforward way of accomplishing this. The AccessDecisionManager (which is AffirmativeBased) throws the AccessDeniedException with none of the information you want. So if you want to "expose/inject" the Collection<ConfigAttribute>, you'll want to provide your own AccessDecisionManager that throws a custom exception that holds the ConfigAttributes.
The easiest way to do this could be to wrap the default AccessDecisionManager with your own and delegate method calls to it:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration
#Override
protected AccessDecisionManager accessDecisionManager() {
AccessDecisionManager default = super.accessDecisionManager();
MyCustomDecisionManager custom = new CustomDecisionManager(default);
}
}
You could define your custom AccessDecisionManager as follows:
public class MyCustomDecisionManager implements AccessDecisionManager {
private AccessDecisionManager default;
public MyCustomDecisionManager(AccessDecisionManager acm) {
this.default = acm;
}
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{
try {
default.decide(authentication, object, configAttributes)
} catch(AccessDeniedException ex) {
throw new CustomAccessDeniedException(ex.getMessage(), configAttributes);
}
}
// other methods delegate to default
}
Now whenever access is denied, you will get an exception that holds the Collection<ConfigAttribute>.
Your custom exception could look like this:
public class CustomAccessDeniedException extends AccessDeniedException {
private Collection<ConfigAttribute> attributes;
public CustomAccessDeniedException(String message, Collection<ConfigAttribute> attr) {
super(message);
this.attributes = attr;
}
public Collection<ConfigAttribute> getAttributes() {
return this.attributes;
}
}
Now your #ExceptionHandler could handle your CustomAccessDeniedException and have access to the ConfigAttributes.
HOWEVER...
I am not sure that will provide you with the error message you wanted. The ConfigAttribute interface only has one method:
String getAttribute();
And the javadoc states:
If the ConfigAttribute cannot be expressed with sufficient precision as a String, null should be returned.
Since we can't rely on the interface method, how you deal with each ConfigAttribute will be heavily dependent on the type of the particular object you're dealing with.
For example, the ConfigAttribute that corresponds to #PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')") is PreInvocationExpressionAttribute, and to print something that resembles what you want, you could do:
PreInvocationExpressionAttribute attr = (PreInvocationExpressionAttribute)configAttribute;
String expressionString = attr.getAuthorizeExpression().getExpressionString();
System.out.println(expressionString); // "hasAuthority('CUSTOM_AUTHORITY')"
That's the major drawback. Also, you would get ALL the ConfigAttributes, not necessarily the ones that failed.

Resources