I am using Dropwizard and wrote a Security Provider that should check the session. If a user is available, it should return it. Otherwise it should throw an exception. How can I get the Session from an HttpRequestContext?
public class SecurityProvider<T> implements InjectableProvider<Auth, Parameter> {
private static class Injectable<T> extends AbstractHttpContextInjectable<T> {
#Override
public T getValue(HttpContext c) {
//Here, get somehow the session,
//then check if user is in session,
//if so, proceed
return null;
}
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public com.sun.jersey.spi.inject.Injectable getInjectable(ComponentContext ic, Auth auth, Parameter parameter) {
return new Injectable<T>();
}
}
Related
I have a Quarkus application in which I implemented the ContainerRequestFilter interface to save a header from incoming requests:
#PreMatching
public class SecurityFilter implements ContainerRequestFilter {
private static final String HEADER_EMAIL = "HD-Email";
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String email = requestContext.getHeaders().getFirst(HEADER_EMAIL);
if (email == null) {
throw new AuthenticationFailedException("Email header is required");
}
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return () -> email;
}
#Override
public boolean isUserInRole(String role) {
return false;
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return null;
}
});
}
}
In a class annotated with ApplicationScoped I injected the context as follows:
#ApplicationScoped
public class ProjectService {
#Context
SecurityContext context;
...
}
The problem is that the context attribute is actually never injected, as it is always null.
What am I doing wrong? What should I do to be able to retrieve the SecurityContext throughout the application's code?
I like to abstract this problem, so that the business logic does not depend on JAX-RS-specific constructs. So, I create a class to describe my user, say User, and another interface, the AuthenticationContext, that holds the current user and any other authentication-related information I need, e.g.:
public interface AuthenticationContext {
User getCurrentUser();
}
I create a RequestScoped implementation of this class, that also has the relevant setter(s):
#RequestScoped
public class AuthenticationContextImpl implements AuthenticationContext {
private User user;
#Override
public User getCurrentUser() {
return user;
}
public void setCurrentUser(User user) {
this.user = user;
}
}
Now, I inject this bean and the JAX-RS SecurityContext in a filter, that knows how to create the User and set it into my application-specific AuthenticationContext:
#PreMatching
public class SecurityFilter implements ContainerRequestFilter {
#Inject AuthenticationContextImpl authCtx; // Injecting the implementation,
// not the interface!!!
#Context SecurityContext securityCtx;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User user = ...// translate the securityCtx into a User
authCtx.setCurrentUser(user);
}
}
And then, any business bean that needs the user data, injects the environment-neutral, application-specific AuthenticationContext.
#Context can only be used in JAX-RS classes - i.e. classes annotated with #Path.
In your case, ProjectService is a CDI bean, not a JAX-RS class.
The canonical way to do what you want is to inject the SecurityContext into a JAX-RS resource and then pass that as a method parameter to your ProjectService
I'm trying with no success to integrate Opentelemetry with Zuul using Spring Boot. What I'm trying to do is to pass Opentelemetry context from Zuul to other microservices in the chain and when the response came into Zuul close the trace
(Create trace) Zuul --call--> (new Span) Microservice A --call--> (new Span) Microservice B
(Close trace ) Zuul <--response-- Microservice A <-response-- Microservice B
Anyone has made something similar to this?
I solved this way:
#Component
public class HeaderRequestFilter extends ZuulFilter {
#Autowired
private Tracer tracer;
#Override
public int filterOrder() {
// run before PreDecoration
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
Span span = tracer.spanBuilder(ctx.getRequest().getRequestURI()).startSpan();
span.setAttribute("eurekaInstanceId", eurekaInstanceId);
tracer.withSpan(span);
OpenTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), ctx, new Setter<RequestContext>() {
#Override
public void set(RequestContext carrier, String key, String value) {
carrier.addZuulRequestHeader(key, value);
}
});
return null;
}
}
and the response, where the span is closed
#Component
public class HeaderResponseFilter extends ZuulFilter {
#Autowired
private Tracer tracer;
#Override
public int filterOrder() {
// Run before PreDecoration
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
#Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
// Close span
tracer.getCurrentSpan().end();
return null;
}
}
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";
}
};
}
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;
}
}
I've created the action OnlyOwner with action composition that gets two users and has to return them to the controller.
Here the code explained:
Controller
#With(OnlyOwner.class) // Call to the action
public static Result profile(Long id) {
return ok(profile.render(user, userLogged));
}
Action
public class OnlyOwner extends Action.Simple{
#Override
public Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
// Here I'm trying to get the Long id passed to che controller
Long id = (Long)ctx.args.get("id"); // but this retrieves a null
User user = User.findById(id);
User userLogged = // Here I get another user
// Now I want to return both the users to the controller
}
}
What is the code to do that?
You have to put the objects into the args of the HTTP context:
http://www.playframework.com/documentation/2.2.x/api/java/play/mvc/Http.Context.html#args
public class Application extends Controller {
#With(OnlyOwner.class)
public static Result profile(Long id) {
return ok(profile.render(user(), userLogged()));//method calls
}
private static User user() {
return getUserFromContext("userObject");
}
private static User userLogged() {
return getUserFromContext("userLoggedObject");
}
private static User getUserFromContext(String key) {
return (User) Http.Context.current().args.get(key);
}
}
public class OnlyOwner extends Action.Simple {
#Override
public Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
//if you have id not as query parameter (http://localhost:9000/?id=43443)
//but as URL part (http://localhost:9000/users/43443) you will have to parse the URL yourself
Long id = Long.parseLong(ctx.request().getQueryString("id"));
ctx.args.put("userObject", User.findById(id));
ctx.args.put("userLoggedObject", User.findById(2L));
return delegate.call(ctx);
}
}