Authorization in dropwizard - java

I want to make an small application using dropwizard in 0.8.0-rc3-SNAPSHOT. In that I want if any user will call my api user should pass an authtoken in the header part.What I have done till now is---
#Override
public void run(HelloWorldConfigurationhelloWorldConfiguration,Environment environment) throws Exception{
environment.jersey().register(new ViewResource());
environment.servlets().addFilter("MyCustomRequestFilter", new MyCustomRequestFilter())
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),false, "/*");
}
public class MyCustomRequestFilter implements ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
System.out.print("test");
if ( request.getQueryParameters().containsKey("validateMeParam") ) {
/* validation logic */
}
// finished validation
return request;
}
}
I don't know what I am doing wrong.It's not working.

ContainerRequestFilter is not a Servlet Filter, which is what you are assuming by doing environment.servlets().addFilter. This should be added to the Jersey configuration.
environment.jersey().register(MyCustomRequestFilter.class);
And don't forget the #Provider annotation on the filter class.
See more about filters in Jersey Filters in the Dropwizard documentation.
UPDATE
I see another serious problem. You say you're using Dropwizard 0.8.0, which uses Jersey 2. In which case, the ContainerRequestFilter you posted should not even exist. In Jersey 1, the parameter to the filter method, is ContainerRequest, while the argument in Jersey 2 is ContainerRequestContext. Please show you dependencies, and verify that the class you have above is the actual class

I hope you are looking for this type of samples
https://github.com/stevenalexander/dropwizard-security
/* An example security provider that will look at each request when received by an endpoint using the auth attribute */
public class ExampleSecurityProvider<T> implements InjectableProvider<Auth, Parameter> {
public final static String CUSTOM_HEADER = "custom-security-token";
private final Authenticator<ExampleCredentials, T> authenticator;
public ExampleSecurityProvider(Authenticator<ExampleCredentials, T> authenticator) {
this.authenticator = authenticator;
}
private static class ExampleSecurityInjectable<T> extends AbstractHttpContextInjectable<T> {
private final Authenticator<ExampleCredentials, T> authenticator;
private final boolean required;
private ExampleSecurityInjectable(Authenticator<ExampleCredentials, T> authenticator, boolean required) {
this.authenticator = authenticator;
this.required = required;
}
#Override
public T getValue(HttpContext c) {
// This is where the credentials are extracted from the request
final String header = c.getRequest().getHeaderValue(CUSTOM_HEADER);
try {
if (header != null) {
final Optional<T> result = authenticator.authenticate(new ExampleCredentials(header));
if (result.isPresent()) {
return result.get();
}
}
} catch (AuthenticationException e) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
if (required) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
return null;
}
}

Related

Chain of responsibility and Spring dependency injection

I implemented a validation using the chain of responsibility pattern. The request payload to validate can have different parameters. The logic is: if the payload has some parameters, validate it and continue to validate other, else throw an exception. In a level of the validation chain I need to call other services, and here comes into play the Dependency Injection.
The validation structure is like a tree, starting from top to bottom.
So, the class where I need to start the Validation
#Service
public class ServiceImpl implements Service {
private final .....;
private final Validator validator;
public ServiceImpl(
#Qualifier("lastLevelValidator") Validator validator, .....) {
this.validator = validator;
this...........=............;
}
/...../
private void validateContext(RequestContex rc) {
Validator validation = new FirstLevelValidator(validator);
validation.validate(rc);
}
}
So the Validator Interface
public interface Validator<T> {
void validate(T object);
}
The validation classes that implements Validator
#Component
public class FirstLevelValidator implements Validator<RequestContext>{
private final Validator<RequestContext> validator;
#Autowired
public FirstLevelValidator(#Qualifier("lastLevelValidator") Validator<RequestContext> validator) {
this.validator = validator;
}
#Override
public void validate(RequestContext requestContext) {
if ( requestContext.getData() == null ) {
LOGGER.error(REQUEST_ERROR_MSG);
throw new BadRequestException(REQUEST_ERROR_MSG, INVALID_CODE);
}
if (requestContex.getData() == "Some Data") {
Validator validator = new SecondLevelValidator(this.validator);
validator.validate(requestContext);
} else {/* other */ }
}
#Component
public class SecondLevelValidator implements Validator<RequestContext>{
private final Validator<RequestContext> validator;
#Autowired
public SecondLevelValidator(#Qualifier("lastLevelValidator") Validator<RequestContext> validator) {
this.validator = validator;
}
#Override
public void validate(RequestContext requestContext) {
if ( requestContext.getOption() == null ) {
LOGGER.error(REQUEST_ERROR_MSG);
throw new BadRequestException(REQUEST_ERROR_MSG, INVALID_CODE);
}
if ( requestContext.getOption() == " SOME " ) {
validator.validate(requestContext); //HERE WHERE I CALL THE Qualifier
}
}
#Component
public class LastLevelValidator implements Validator<RequestContext>{
private final ClientService1 client1;
private final ClientService2 client2;
public LastLevelValidator(ClientService1 client1, ClientService2 client2) {
this.client1 = client1;
this.client2 = client2;
}
#Override
public void validate(RequestContext requestContext) {
Integer userId = client2.getId()
List<ClientService1Response> list = client1.call(requestContext.id(), userId);
boolean isIdListValid = list
.stream()
.map(clientService1Response -> clientService1Response.getId())
.collect(Collectors.toSet()).containsAll(requestContext.getListId());
if (!isIdListValid) {
LOGGER.error(NOT_FOUND);
throw new BadRequestException(NOT_FOUND, INVALID_CODE);
} else { LOGGER.info("Context List validated"); }
}
}
In the LastLevelValidator I need to call other services to make the validation, for that I inject into each validator class (First.., Second..) the #Qualifier("lastLevelValidator") object, so when I need to instantiate the LastLevelValidation class I can call it like validator.validate(requestContext); instance of validator.validate(ClientService1, ClientService2 ) that it would force me to propagate the ClientServices objects through all the chain from the ServiceImpl class.
Is it this a good solution ?
Is there any concern I didn't evaluate?
I tried also declaring the services I need to call for the validation as static in the LastLevelValidation, in the way that I can call it like LastLevelValidation.methodvalidar(), but look like not a good practice declares static objects.
I tried to pass the objects I need propagating it for each Validation class, but seems to me that if I need another object for the validation I have to pass it through all the validation chain.

How to use a Spring Cloud Gateway Custom Filter to filter every request?

It's my first time at Spring Cloud Gateway implementation.
I need filter every request and apply a filter validation on some paths. Following the Baeldung Custom Filters tutorial I make a simple application to filter requests.
The application must release paths like /actuator/health and validate specific paths to backend service. So far, I've implemented a GlobalFilter and a GatewayFilterFactory. The Global filter is called every request but the GatewayFilter is called just once when application starts, that way I can't make the auth logic to every request. The auth logic is about a specific header field. So, my grained questions are:
How validate every request with a specific path?
How refuse a request and send a error message?
GlobalFilter
#Component
public class LoggingGlobalPreFilter implements GlobalFilter {
final Logger LOGGER = LoggerFactory.getLogger(LoggingGlobalPreFilter.class);
#Override
public Mono<Void> filter(
ServerWebExchange exchange,
GatewayFilterChain chain) {
LOGGER.info("Global Pre Filter executed");
return chain.filter(exchange);
}
}
GatewayFilter
#Component
public class LoggingGatewayFilterFactory extends
AbstractGatewayFilterFactory<LoggingGatewayFilterFactory.Config> {
final Logger LOGGER =
LoggerFactory.getLogger(LoggingGatewayFilterFactory.class);
public LoggingGatewayFilterFactory() {
super(Config.class);
}
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
private boolean isAuthorizationValid(String authorizationHeader) {
boolean isValid = true;
return authorizationHeader.equals("x-header");
}
#Override
public GatewayFilter apply(Config config) {
LOGGER.info("M=apply, Msg=Applying Gateway Filter....");
return ((exchange, chain) -> {
LOGGER.info("M=apply, Msg=Applying Gateway Filter...."); // APARENTELLY NEVER ENTER HERE.
ServerHttpRequest request = exchange.getRequest();
if (!request.getHeaders().containsKey(TsApiGatewayConstants.HEADER_APIKEY)) {
return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_MISSING, HttpStatus.UNAUTHORIZED);
}
String apiKey = request.getHeaders().get(TsApiGatewayConstants.HEADER_APIKEY).get(0);
String userAgent = request.getHeaders().get(TsApiGatewayConstants.HEADER_USER_AGENT).get(0);
if (!this.isAuthorizationValid(userAgent)) {
return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_INVALID, HttpStatus.UNAUTHORIZED);
}
return chain.filter(exchange);
});
}
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
public Config(String baseMessage, boolean preLogger, boolean postLogger) {
this.baseMessage = baseMessage;
this.preLogger = preLogger;
this.postLogger = postLogger;
}
public String getBaseMessage() {
return baseMessage;
}
public void setBaseMessage(String baseMessage) {
this.baseMessage = baseMessage;
}
public boolean isPreLogger() {
return preLogger;
}
public void setPreLogger(boolean preLogger) {
this.preLogger = preLogger;
}
public boolean isPostLogger() {
return postLogger;
}
public void setPostLogger(boolean postLogger) {
this.postLogger = postLogger;
}
}
}
application.yml
cloud:
gateway:
routes:
- id: service_route
uri: https://backend-url:443
predicates:
- Path=/api
filters:
- Logging
Example path to filter: https://backend-url:443/api/service1
I've found a way to solve it. I've used a RouteConfiguration component to set the routes and a GatewayFilter class. On the RouteConfiguration's Bean I've seted the specific filter to the route path. On my case I've used a filter to make an authentication.
GatewayFilter
#RefreshScope
#Component
public class AuthenticationFilter implements GatewayFilter {
final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// Make your business logic, this is a simple sample.
if (!request.getHeaders().containsKey("x-api-key")) {
return this.onError(exchange,"api-key missing",HttpStatus.FORBIDDEN);
}
return chain.filter(exchange); // Forward to route
}
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
RouteConfiguration
#RefreshScope
#Configuration
public class RouteConfiguration {
#Value("${routes.api}")
private String apiHost;
#Autowired
AuthenticationFilter authenticationFilter;
#Bean
public RouteLocator apiRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("CHOICE A ROUTE ID",p -> p
.path("/api/**")
.filters(f -> f
.filter(authenticationFilter) // You can add more filters here.
.stripPrefix(1))
.uri(apiHost))
.build();
}
}
if you want validate every request ,you should impl the Ordered interface and
and return -2;
eg:
#Component
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
#Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -2;
}
}
refuse a request and send a error message
you can see this .use MonoUtil.buildServerResponse method.
MonoUtil

Securing with roles annotations not working (Jersey)

I'm new here even though I've found many answers to my problems in here before.
Now I'm looking for help with this: I have this little example resource on my little REST API:
#Path("/greeting")
#PermitAll
public class HelloResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
#Path("all")
public String sayHelloToAll() {
return "Hello, everybody!";
}
#GET
#Produces(MediaType.TEXT_PLAIN)
#RolesAllowed("admin")
#Path("admin")
public String sayHelloToAdmin() {
return "Hello, admin!";
}
}
In order to filter roles, I have this implementation of SecurityContext:
public class Authorizer implements SecurityContext {
#Override
public String getAuthenticationScheme() {
return null;
}
#Override
public Principal getUserPrincipal() {
return null;
}
#Override
public boolean isSecure() {
return false;
}
#Override
public boolean isUserInRole(String role) {
return true;
}
}
And this implementation of ContainerRequestFilter:
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthorizationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new Authorizer());
}
}
This is my application class:
#ApplicationPath("/")
public class Application extends ResourceConfig {
public Application() {
super(HelloResource.class);
register(AuthorizationFilter.class);
register(RolesAllowedDynamicFeature.class);
}
}
With all this, when I request the URI greeting/all, everything is ok, the string "Hello, everybody!" is shown. But when I request the URI greeting/admin, which should be called when an user in admin role requests it, is never invoked, even when my isUserInRole method always returns true. In fact, my filter method is always called, but my isUserInRole method is never called.
I have followed many advices:
SecurityContext doesn't work with #RolesAllowed
Authorization with RolesAllowedDynamicFeature and Jersey
How to access Jersey resource secured by #RolesAllowed
Best practice for REST token-based authentication with JAX-RS and Jersey
But it doesn't seem to work with anything.
Can anyone please help me? I don't know is there is something I am missing
Thank you all in advance.
EDIT: When I request the URI greeting/admin I get 403 Forbiden by the way (I forgot to say that)
Take a look at the source code for the RoleAllowedRequestFilter. When a user is authenticated, it is expected that there be an associated Principal. The filter checks it here
if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
throw new ForbiddenException(LocalizationMessages.USER_NOT_AUTHORIZED());
}
...
private static boolean isAuthenticated(final ContainerRequestContext requestContext) {
return requestContext.getSecurityContext().getUserPrincipal() != null;
}
So you need to return a Principal in the getUserPrincipal of the SecurityContext
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "Some Name";
}
};
}

Injecting principal into resource method in Jersey 2

I am developing a REST API using Jersey 2 and at the moment I am trying to incorporate basic authentication by use of an annotation similar to the #Auth found in Dropwizard. With
#Path("hello")
public class HelloResource {
#GET
#Produces("application/json")
public String hello(#Auth final Principal principal) {
return principal.getUsername();
}
}
the hello resource invocation should be intercepted by some code performing basic authentication using the credentials passed in the Authorization HTTP request header and on success injecting the principal into the method principal parameter.
I have started creating an #Auth resolver, see below, but I do not see how I can access the Authorization HTTP request header from within that?
#Singleton
public class AuthResolver {
public static class AuthInjectionResolver extends ParamInjectionResolver<Auth> {
public AuthInjectionResolver() {
super(AuthValueFactoryProvider.class);
}
}
#Singleton
public static class AuthValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public AuthValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
final Class<?> classType = parameter.getRawType();
return classType == null || !classType.equals(Principal.class) ? null :
new AbstractContainerRequestValueFactory<Principal>() {
#Override
public Principal provide() {
// Authentication?
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(AuthValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(AuthInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Auth>>() {
}
).in(Singleton.class);
}
}
}
How to approach this? :)
Ah, in AbstractContainerRequestValueFactory<Principal> I can add
#Context private ResourceContext context;
and then extract the HTTP request and it's headers from there inside the provide method.

How to get resource method matched to URI before Jersey invokes it?

I'm trying to implement a ContainerRequestFilter that does custom validation of a request's parameters. I need to look up the resource method that will be matched to the URI so that I can scrape custom annotations from the method's parameters.
Based on this answer I should be able to inject ExtendedUriInfo and then use it to match the method:
public final class MyRequestFilter implements ContainerRequestFilter {
#Context private ExtendedUriInfo uriInfo;
#Override
public ContainerRequest filter(ContainerRequest containerRequest) {
System.out.println(uriInfo.getMatchedMethod());
return containerRequest;
}
}
But getMatchedMethod apparently returns null, all the way up until the method is actually invoked (at which point it's too late for me to do validation).
How can I retrieve the Method that will be matched to a given URI, before the resource method is invoked?
For those interested, I'm trying to roll my own required parameter validation, as described in JERSEY-351.
Actually, you should try to inject ResourceInfo into your custom request filter. I have tried it with RESTEasy and it works there. The advantage is that you code against the JSR interfaces and not the Jersey implementation.
public class MyFilter implements ContainerRequestFilter
{
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException
{
Method theMethod = resourceInfo.getResourceMethod();
return;
}
}
I figured out how to solve my problem using only Jersey. There's apparently no way to match a request's URI to the method that will be matched before that method is invoked, at least in Jersey 1.x. However, I was able to use a ResourceFilterFactory to create a ResourceFilter for each individual resource method - that way these filters can know about the destination method ahead of time.
Here's my solution, including the validation for required query params (uses Guava and JSR 305):
public final class ValidationFilterFactory implements ResourceFilterFactory {
#Override
public List<ResourceFilter> create(AbstractMethod abstractMethod) {
//keep track of required query param names
final ImmutableSet.Builder<String> requiredQueryParamsBuilder =
ImmutableSet.builder();
//get the list of params from the resource method
final ImmutableList<Parameter> params =
Invokable.from(abstractMethod.getMethod()).getParameters();
for (Parameter param : params) {
//if the param isn't marked as #Nullable,
if (!param.isAnnotationPresent(Nullable.class)) {
//try getting the #QueryParam value
#Nullable final QueryParam queryParam =
param.getAnnotation(QueryParam.class);
//if it's present, add its value to the set
if (queryParam != null) {
requiredQueryParamsBuilder.add(queryParam.value());
}
}
}
//return the new validation filter for this resource method
return Collections.<ResourceFilter>singletonList(
new ValidationFilter(requiredQueryParamsBuilder.build())
);
}
private static final class ValidationFilter implements ResourceFilter {
final ImmutableSet<String> requiredQueryParams;
private ValidationFilter(ImmutableSet<String> requiredQueryParams) {
this.requiredQueryParams = requiredQueryParams;
}
#Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
#Override
public ContainerRequest filter(ContainerRequest request) {
final Collection<String> missingRequiredParams =
Sets.difference(
requiredQueryParams,
request.getQueryParameters().keySet()
);
if (!missingRequiredParams.isEmpty()) {
final String message =
"Required query params missing: " +
Joiner.on(", ").join(missingRequiredParams);
final Response response = Response
.status(Status.BAD_REQUEST)
.entity(message)
.build();
throw new WebApplicationException(response);
}
return request;
}
};
}
#Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
}
}
And the ResourceFilterFactory is registered with Jersey as an init param of the servlet in web.xml:
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>my.package.name.ValidationFilterFactory</param-value>
</init-param>
At startup, ValidationFilterFactory.create gets called for each resource method detected by Jersey.
Credit goes to this post for getting me on the right track: How can I get resource annotations in a Jersey ContainerResponseFilter
I know you're looking for a Jersey only solution but here's a Guice approach that should get things working:
public class Config extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(
new JerseyServletModule() {
#Override
protected void configureServlets() {
bindInterceptor(Matchers.inSubpackage("org.example"), Matchers.any(), new ValidationInterceptor());
bind(Service.class);
Map<String, String> params = Maps.newHashMap();
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example");
serve("/*").with(GuiceContainer.class, params);
}
});
}
public static class ValidationInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation method) throws Throwable {
System.out.println("Validating: " + method.getMethod());
return method.proceed();
}
}
}
#Path("/")
public class Service {
#GET
#Path("service")
#Produces({MediaType.TEXT_PLAIN})
public String service(#QueryParam("name") String name) {
return "Service " + name;
}
}
EDIT: A performance comparison:
public class AopPerformanceTest {
#Test
public void testAopPerformance() {
Service service = Guice.createInjector(
new AbstractModule() {
#Override
protected void configure() { bindInterceptor(Matchers.inSubpackage("org.example"), Matchers.any(), new ValidationInterceptor()); }
}).getInstance(Service.class);
System.out.println("Total time with AOP: " + timeService(service) + "ns");
}
#Test
public void testNonAopPerformance() {
System.out.println("Total time without AOP: " + timeService(new Service()) + "ns");
}
public long timeService(Service service) {
long sum = 0L;
long iterations = 1000000L;
for (int i = 0; i < iterations; i++) {
long start = System.nanoTime();
service.service(null);
sum += (System.nanoTime() - start);
}
return sum / iterations;
}
}
In resteasy-jaxrs-3.0.5, you can retrieve a ResourceMethodInvoker representing the matched resource method from ContainerRequestContext.getProperty() inside a ContainerRequestFilter:
import org.jboss.resteasy.core.ResourceMethodInvoker;
public class MyRequestFilter implements ContainerRequestFilter
{
public void filter(ContainerRequestContext request) throws IOException
{
String propName = "org.jboss.resteasy.core.ResourceMethodInvoker";
ResourceMethodInvoker invoker = (ResourceMethodInvoker)request.getProperty();
invoker.getMethod().getParameterTypes()....
}
}

Categories