Reject http requests handled by endpoints that don't specify security restrictions - java

I'm trying to protect against accidentally publishing an endpoint with no security in place. #Secured, #PreAuthorize, #PostAuthorize, etc.
I would want requests made to endpoints that don't specify any security requirements to be automatically rejected (403 Forbidden)
If an endpoint should indeed be open to the public it would need to be tagged by an annotation or similar.
#RestController
public class Controller {
#GetMapping("/endpoint1")
public void accidentallyOpen() {
}
#GetMapping("/endpoint2")
#UnrestrictedEndpoint
public void intentionallyOpen() {
}
}
In the above example I'm expecting
Requests made to /endpoint1 should return 403 Forbidden.
Requests made to /endpoint2 should return 204 No Content
I'm not sure where to start. Also if there are better ways of doing this I'm also happy to learn.

Related

Authenticate spring websocket via MessageMapping

Problem
I have set up a stomp websocket on spring, and have endpoints defined via the #MessageMapping annotation.
I had read that #PreAuthorize could be used to authorize on a per mapping basis but this doesn't appear to work. When using the #PreAuthorize, the request is not denied when the user is not in a specific role.
Code
#PreAuthorize("hasRole('ROLE_ADMIN')")
#MessageMapping(value="/addComment/{ID}")
public void addComment(#DestinationVariable Integer ID, String content, Principal principal)
throws Exception {
//Do stuff with ID,content etc
}
I currently have it set up like so
#Configuration
public class WebSocketSecurityConfig extends
AbstractSecurityWebSocketMessageBrokerConfigurer {
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpDestMatchers("/put/addComment/**").hasRole("ADMIN");
}
}
Although would prefer to annotate on each mapping since it is clearer for me.
Question(s)
Can preauthorize be used with mappings?
If so is there a reason that it is not working in the above example?
If not, is there a way to do this per mapping, instead of in the configurer?
Extra
Using Spring 4
Any more information needed let me know

How to intercept a request in Jersey using Annotations?

I'm using Jersey to build a REST API with your standard annotation based routing.
My question is, how can I use a "custom annotation" (in this case #InternalOnly) to intercept a request and run code before the request hits the resource and the // DO STUFF block is executed?
#Path("app")
#Produces(MediaType.APPLICATION_JSON)
public final class SomeResource {
#GET
#Path("something")
#InternalOnly
public Response getSomething() {
// DO STUFF
}
}
I would then have another class that would handle the #InternalOnly header...
public final class InternalOnlyHandler implements SomethingProbably {
public void handle(Object someContext) {
// HANDLE INTERNAL ONLY AUTHENTICATION
}
}
My purpose is basically I need certain API resources to be available internally ONLY and others publicly available and the Authentication is slightly different for both so registering a simple ContainerRequestFilter won't do the job.
I can't figure this out -- please help!

Authorization header not passed by ZuulProxy starting with Brixton.RC1

In switching from Spring Cloud Brixton.M5 to Brixton.RC1 my ZuulProxy no longer passes Authorization headers downstream to my proxied services.
There's various actors in play in my setup, but most all of them are fairly simple:
- AuthorizationServer: runs separately; hands out JWTs to clients
- Clients: get JWTs from OAuth server; each with access to a subset of resources.
- ResourceServers: consume JWTs for access decisions
- MyZuulProxy: proxies various resource servers; should relay JWTs.
It should be noted that MyZuulProxy has no security dependencies whatsoever; It passed the Authorization: Bearer {JWT} header it receives to the ResourceServers, pre-RC1. MyZuulProxy is explicitly not a Client itself, and does not use #EnableOAuth2SSO or similar at the moment.
What could I do to get MyZuulProxy to relay the JWTs to the ResourceServers again when using Spring Cloud Brixton.RC1?
There's very little code to post: It's just #EnableZuulProxy, #EnableAuthorizationServer and #EnableResourceServer in three different jars. My Clients are not Spring applications.
Update: Fixed in https://github.com/spring-cloud/spring-cloud-netflix/pull/963/files
Sensitive headers can also be set globally setting zuul.sensitiveHeaders. If sensitiveHeaders is set on a route, this will override the global sensitiveHeaders setting.
So use:
# Pass Authorization header downstream
zuul:
sensitiveHeaders: Cookie,Set-Cookie
So pending a fix for https://github.com/spring-cloud/spring-cloud-netflix/issues/944, jebeaudet was kind enough to provide a workaround:
#Component
public class RelayTokenFilter extends ZuulFilter {
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// Alter ignored headers as per: https://gitter.im/spring-cloud/spring-cloud?at=56fea31f11ea211749c3ed22
Set<String> headers = (Set<String>) ctx.get("ignoredHeaders");
// We need our JWT tokens relayed to resource servers
headers.remove("authorization");
return null;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 10000;
}
}
Set the sensitiveHeaders globally helped me solve the issue
zuul:
sensitiveHeaders: Cookie,Set-Cookie
Please note that the property name is sensitiveHeaders not sensitive-headers
[I use spring-cloud-starter-zuul version:1.3.1.RELEASE ]

Spring REST service returns HTTP 400 status

I have a REST service written with the Spring Framework v 3.2.0
Here is the Controller:
#Controller
#RequestMapping("/volunteer")
public class VolunteerController {
//several methods not included (all HTTP GET & working properly)
#RequestMapping(value="/{volunteerId}/assignments/{sessionId}",
method=RequestMethod.PUT, params={"worked"})
#ResponseStatus(HttpStatus.NO_CONTENT)
public void setWorkedFlag(#PathVariable int volunteerId,
#PathVariable int sessionId,
#RequestParam int worked) {
assignmentMapper.setWorked(volunteerId, sessionId, worked);
}
}
When I submit a PUT request with this URL:
http://localhost:8080/volunteer/298/assignments/1?worked=true
I get a 400 restonse with the message : The request sent by the client was syntactically incorrect.
I can also confirm that the underlying resource has not need modified.
I have compared the annotations against the request URL and can't see anything obviously wrong. Also, the other methods (omitted here) all work correctly, so the Application Context has been set up OK.
Any ideas?

Jersey Async ContainerRequestFilter

I have a Jersey REST API and am using a ContainerRequestFilter to handle authorization. I'm also using #ManagedAsync on all endpoints so that my API can serve thousands of concurrent requests.
My authorization filter hits a remote service, but when the filter is run, Jersey hasn't yet added the current thread to it's internal ExecutorService, so I'm completely losing the async benefits.
Can I tell Jersey that I want this ContainerRequestFilter to be asynchronous?
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
#Inject
private AuthorizationService authSvc;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authToken = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// HITS A REMOTE SERVER
AuthorizationResponse authResponse = authSvc.authorize(authToken);
if (!authResponse.isAuthorized())
{
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("unauthorized!")
.build());
}
}
}
And here's an example resource:
#Path("/stuff")
#Produces(MediaType.APPLICATION_JSON)
public class StuffResource
{
#GET
#Path("/{id}")
#ManagedAsync
public void getById(#PathParam("id") long id, #Suspended final AsyncResponse ar)
{
Stuff s;
// HIT THE DATABASE FOR STUFF
ar.resume(s);
}
}
UPDATE Just heard back from the Jersey guys, and this is not possible as of 2.7. Only the resource method itself is invoked asynchronously, not filters. Any suggestions for proceeding still welcome.
This is not built in to Jersey as of 2.7.
#ManagedAsync is useless if you have any filters or interceptors that do any serious work (like hit a remote authorization service). They may add the ability to run filters asynchronously in the future, but for now you're on your own.
UPDATE - there are other ways...
After a long and perilous journey, I have found a very hacky solution that I'm using in the short term. Here is a rundown of what I tried and why it failed/worked.
Guice AOP - failed
I use Guice for DI (getting Guice injection to work with Jersey is a feat in itself!), so I figured I could use Guice AOP to get around the issue. Though Guice injection works, it is impossible to get Guice to create resource classes with Jersey 2, so Guice AOP cannot work with resource class methods. If you are trying desperately to get Guice to create resource classes with Jersey 2, don't waste your time because it will not work. This is a well-known problem.
HK2 AOP - RECOMMENDED SOLUTION
HK2 just recently released an AOP feature, see this question for details on how to get it working.
Monitoring - also worked
This is not for the faint of heart, and it is completely discouraged in the Jersey docs. You can register and ApplicationEventListener and override onRequest to return a RequestEventListener that listens for RESOURCE_METHOD_START and calls an authentication/authorization service. This event is triggered from the #ManagedAsync thread, which is the whole goal here. One caveat, the abortWith method is a no-op, so this won't work quite like a normal ContainerRequestFilter. Instead, you can throw an exception if auth fails instead, and register an ExceptionMapper to handle your exception. If someone is bold enough to give this a try, let me know and I'll post code.
I am not sure if this is what you were looking for but, have you looked into Spring's OncePerRequestFilter? I am currently using it for my authorization layer where each request goes through some filter that extends this OncePerRequestFilter depending on how my filters are mapped to the URLs. Here's a quick overview of how I am using it:
Authentication/Authorization of a resource in Dropwizard
I am not very clear on the async dispatch parts of these filters but I hope this link atleast sheds some light to what you are trying to achieve!
We use Spring security for authentication/authorization. I worked around the problem using a sub-resource locator with empty path as shown below:
#Path("/customers")
public class CustomerResource {
#Inject
private CustomerService customerService;
#Path("")
public CustomerSubResource delegate() {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new CustomerSubResource(auth);
}
public class CustomerSubResource {
private final Authentication auth;
public CustomerSubResource(final Authentication auth) {
this.auth = auth;
}
#POST
#Path("")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ManagedAsync
public void createCustomer(final Customer customer, #Suspended final AsyncResponse response) {
// Stash the Spring security context into the Jersey-managed thread
SecurityContextHolder.getContext().setAuthentication(this.auth);
// Invoke service method requiring pre-authorization
final Customer newCustomer = customerService.createCustomer(customer);
// Resume the response
response.resume(newCustomer);
}
}
}

Categories

Resources