How can I use RequestHeader with actuator endpoint? - spring-boot

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");
...
}
}

Related

ConversationScoped in 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.

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.

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.

How to check security acess (#Secured or #PreAuthorize) before validation (#Valid) in my Controller?

here is my Controller code :
#PreAuthorize("hasRole('CREATE_USER')")
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public UserReturnRO createUser(#Valid #RequestBody UserRO userRO) throws BadParameterException{
return userService.createUser(userRO);
}
My need is when a client without the appropriate role tries to create a user, the controller responds "Not authorized" even if the data sent are not valid. Instead of that, if the client (without the appropriate role) tries to create a user with wrong data, my controller responds with the #Valid message (ex : "password cannot be empty"), while I want it responds "not authorized".
In the PreAuthorized Interface we can find this sentence :
Annotation for specifying a method access-control expression which will be evaluated to decide whether a method invocation is allowed or not.
but it seems that it's not the case.
You can not do this directly, since #Valid is processed before an actual method call and as a result before #PreAuthorize.
But what you can do instead is to inject BindingResult just right after your model (userRO) and in doing so - take control of validation process. Then check if BindingResult has some errors and if so return bad request response (similar to what spring does).
Example:
#ResponseBody
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("hasRole('CREATE_USER')")
public ResponseEntity<?> createUser(#RequestBody #Valid UserRO userRO, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
return ResponseEntity.ok(userService.createUser(userRO));
}
As already stated, Spring Security's #PreAuthorize is method advice, which means that it does not get to participate until the method and its arguments have already been resolved.
Aside from the answer already given, there are a few ways to move authorization before argument resolution, instead.
Filter Security
First, Spring Security checks URLs before the request is mapped to a method. And since this is a #Controller, it's reasonable to suppose that you could instead map the request to the role at that level instead of #PreAuthorize:
http
.authorizeRequests()
.mvcMatchers(POST, "/somepath").hasRole("CREATE_USER")
Handler Interceptor
Second, Spring MVC does ship with limited support for checking authorities before parsing method arguments. For example, you can do:
#EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
UserRoleAuthorizationInterceptor userRole =
new UserRoleAuthorizationInterceptor();
userRole.setAuthorizedRoles("CREATE_USER");
registry.addInterceptor(userRole);
}
}
This is much more basic than #PreAuthorize since it's a global setting, but I've included it for completeness.
Handler Interceptor, Part 2
Third (warning, some inelegance ahead), you can create your own HandlerInterceptor.
The flow is:
FilterSecurityInterceptor <== where .mvcMatchers(...).hasRole(...) lives
Then HandlerInterceptors
Then argument validation
Then MethodSecurityInterceptor <== where #PreAuthorize lives
So, your HandlerInterceptor would check before arguments are resolved. It doesn't have to be as involved as MethodSecurityInterceptor, though. It could, for example, simply be:
static class AuthorizationInterceptor extends HandlerInterceptorAdapter {
SecurityMetadataSource securityMetadataSource;
AccessDecisionManager accessDecisionManager;
#Override
public void preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
Authentication authenticated = (Authentication) request.getUserPrincipal();
MethodInvocation mi = convert(handler);
Collection<ConfigAttribute> attributes =
this.securityMetadataSource.getAttributes(mi);
// throws AccessDeniedException
this.accessDecisionManager.decide(authenticated, mi, attributes);
return true;
}
}
Then you wire it together with:
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodConfig extends GlobalMethodSecurityConfiguration {
#Bean
HandlerInterceptor preAuthorize() throws Exception {
return new AuthorizationInterceptor(
accessDecisionManager(), methodSecurityMetadataSource());
}
}
#EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
#Autowired
AuthorizationInterceptor authorizationInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor);
}
}
It's inelegant because MethodSecurityInterceptor would still participate in authorized requests, which would ostensibly be the majority.

Resources