#Context injection in Stateless EJB used by JAX-RS - java

I have something like this setup below. This is a simplified version but I think it gets the basic idea across. I am using Jersey 2.16, Java 1.8, and Glassfish Open Source 4.1
public interface IReportService {
String addNewReport(Report report);
}
#Path("reports")
public class ReportResource implements IReportService {
/**
* Service layer.
*/
#EJB
private transient ReportService service;
#POST
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Produces(MediaType.TEXT_PLAIN)
#Override
public String addNewReport(final Report report) {
return service.addNewReport(report);
}
}
#Stateless
#LocalBean
public class ReportService implements IReportService {
#EJB
private IReportPersistence reportPersistence;
#Context
SecurityContext secContext;
public String addNewReport(final Report report) {
report.setUserName(secContext.getUserPrincipal().getName());
reportPersistence.persist(report);
}
}
But when I deploy and try to hit the web-service I get a NullPointer exception from the security context. It just seems that the Context is not being injected at all. I checked and it's the secContext variable itself, not just the return from getUserPrincipal() that is null. There are no warning or exceptions in the Glassfish log besides my NullPointer (which results in a 500 error returned to the web client).

The problem is that you are using the SecurityContext in the wrong place. You have to use it inside your REST resource class.
You can try the following:
#POST
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Produces(MediaType.TEXT_PLAIN)
#Override
public String addNewReport(final Report report, #Context SecurityContext sc) {
report.setUserName(sC.getUserPrincipal().getName());
return service.addNewReport(report);
}
For more details have a look at the Jersey Documentation - Chapter 16. Security.
Inside of EJBs you have to use the EJBContext (or the SessionContext).

Thank you, I have solved using the EJBContext inside the EJBs, as pointed by unwichtich.
In conclusion, SecurityContext is only for the JAX-RS bean, I have used the EJBContext object inplace of SecurityContext into the other java beans.
You can also use the SessionContext object but EJBContext interface resembles the SecurityContext one. Here is an usage example:
#DeclareRoles({"administrator","operator","user"})
#PermitAll
#Stateless
public class myFacade {
#PersistenceContext(unitName = "myPersistencePU")
private EntityManager em;
#Resource EJBContext securityContext;
public DataStuff find(Object id) {
//Now the securityContext is != null :-D
String username = securityContext.getCallerPrincipal().getName();
if(username.equals("gino"){
return null;
}
return getEntityManager().find(entityClass, id);
}
}
It works auto-magically as expected, the EJB sees the same Principal(s) as the JAX-RS servlet do.

Related

Providing the SecurityIdentity directly to a Quarkus/RESTEasy web service method

I am using Quarkus with RESTEasy to make a web service and I need access to the SecurityIdentity in some of my methods.
It is possible to get it injected by making the service RequestScoped:
#RequestScoped
#Path("/foo")
public class FooResource {
#Inject
public SecurityIdentity securityIdentity;
#GET
public Foos getFoos() {
// use securityIdentity
}
}
But I would prefer to have the class ApplicationScoped and have the SecurityIdentity provided to the method instead. Something like this:
#ApplicationScoped
#Path("/foo")
public class FooResource {
#GET
// This does not work, Quarkus tries to convert the request body to a SecurityIdentity.
public Foos getFoos(SecurityIdentity securityIdentity) {
// use securityIdentity
}
}
Is this possible? Is there a magic annotation I can put on to make Quarkus inject the SecurityIdentity?
Keeping it injected into a field will still work for ApplicationScoped beans and be thread safe

JaxRS + RestEasy - How do you create your own #Context injected field?

Question about RestEASY 3.6.2 on JBoss 7.1.0.
I have the following working JaxRS service:
#Path("my-service")
public class MyResource {
#Context
HttpServletRequest request;
#GET
#Path("get-stuff")
#Produces(MediaType.APPLICATION_JSON)
public Response doStuff() {
MyCustomContext customContext = new MyCustomContext(request);
// ... use the customContext here.
}
}
With the way this is currently set up, every rest method requires a MyCustomContext customContext = new MyCustomContext(request);. That is annoying.
Is there some way to inject the MyCustomContext?
#Path("my-service")
public class MyResource {
#Context
MyCustomContext context;
#GET
#Path("get-stuff")
#Produces(MediaType.APPLICATION_JSON)
public Response doStuff() {
// ... use the customContext here.
}
}
#Producer // ???
public class MyCustomContext {
#Context
HttpServletRequest request;
public MyCustomContext() {
// construct with request object.
}
}
I have found a ton of links hinted towards a way to do this, but I am coming up empty.
I do not know any way of injecting a custom class instance/bean with #Context. I would like to outline alternative approaches that depend on the concrete requirement.
A) No injection needed at all.
Make your custom context a class member of your JAX-RS resource class (instead of local variable within each method). Utilize #PostConstruct to instantiate your custom context once the container has created an initialized your resource class instance. The resource class must be a CDI-bean with request scope for this to work.
#Path("my-service")
#RequestScoped // CDI-bean with request scope only
public class MyResource {
#Context
private HttpServletRequest request;
private MyCustomContext customContext;
#PostConstruct
public void initialize() {
this.customContext = new MyCustomContext(this.request); // request is initialized by the container already at this point
}
#GET
#Path("get-stuff")
#Produces(MediaType.APPLICATION_JSON)
public Response doStuff() {
// ... use the customContext here.
}
}
B) Your custom context requires an HttpServletRequest instance only
Beside JAX-RS via #Context, CDI also provides a predefined bean for HttpServletRequest via #Inject. You can make your custom context a CDI-bean also and inject that predefined CDI-bean. Afterwards you are able to inject your custom context into your JAX-RS resource (regardless of whether it is an EJB or CDI-bean).
#Dependent // make your custom context a CDI-bean
public class MyCustomContext {
#Inject // inject predefined CDI-bean
private HttpServletRequest request;
}
#Path("my-service")
#RequestScoped // either CDI-bean
//#Stateless // or EJB
public class MyResource {
#Inject // inject custom context via CDI
private MyCustomContext customContext;
#GET
#Path("get-stuff")
#Produces(MediaType.APPLICATION_JSON)
public Response doStuff() {
// ... use the customContext here.
}
}
C) Your custom context requires an instance exclusiveley provided via provider specific #Context e.g. Request
If you inject an instance via #Context into your non JAX-RS custom context CDI-bean it will be null. You need some mechanism to provide the injected instance from your JAX-RS resource. Making CDI responsible for the injection via #Inject on your custom context and adding a producer method via #Produces to your JAX-RS resource will do the job.
#Dependent // make your custom context a CDI-bean
public class MyCustomContext {
//#Context // in non JAX-RS context the instance will be null
#Inject // instead inject the JAX-RS context instance via CDI
private Request request;
}
#Path("my-service")
#RequestScoped // either CDI-bean
//#Stateless // or EJB
public class MyResource {
#Context // in JAX-RS context the instance will not be null
private Request request;
#Inject
private MyCustomContext customContext;
#Produces // provide the JAX-RS context instance for injection via CDI
#RequestScoped
public Request getContextRequest() {
return this.request;
}
#GET
#Path("get-stuff")
#Produces(MediaType.APPLICATION_JSON)
public Response doStuff() {
// ... use the customContext here.
}
}

Using Spring Data Rest RepositoryEntityLinks outside of Controller

I would like to use the RepositoryEntityLinks class to get the link to a resource at various places in my code as per section 12.1 of the current Spring Data Rest manual
12.1. Programmatic Links Sometimes you need to add links to exported resources in your own custom built Spring MVC controllers. There are
three basic levels of linking available:
...
3 Using Spring Data REST’s implementation of RepositoryEntityLinks.
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#_programmatic_links
I note the docs refer explicitly to "...your own custom built Spring MVC controllers" and it would seem that is the only place it is available. I would like to use the configured instance in a Spring Security AuthenticationSuccessHandler however the application fails to start with the error:
No qualifying bean of type[org.springframework.data.rest.webmvc.support.RepositoryEntityLinks] found
I have been able to successfully inject it to a controller as expected.
Can I use the RepositoryEntityLinks class outside of a Spring MVC Controller?
public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
#Autowired
private RepositoryEntityLinks entityLinks;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException
{
//do something with entityLinks
}
}
Yes, You can. I have successfully used it in Assembler which generates links from HATEOAS model. Altough there may be some restrictions on where RepositoryEntityLinks class can be injected, for sure it can be used outside of Controllers.
Below you can see my working example. If anyone wnders this class extends ResourceAssemblerSupport which is part of spring-hateoas module. Maybe that's the thing that enables injection here.
#Component
public class UserAssembler extends ResourceAssemblerSupport<UserEntity, UserResource> {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
public UserAssembler() {
super(UserController.class, UserResource.class);
}
#Override
public UserResource toResource(UserEntity userEntity) {
Link userLink = repositoryEntityLinks.linkToSingleResource(UserEntity.class, userEntity.getId());
Link self = new Link(entryLink.getHref(), Link.REL_SELF);
return new UserResource(userEntity, self);
}
}
The following works for me:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RestApiIntegrationTests {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
#BeforeEach
public void initServletRequestAttributes() {
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
}
#Test
void test() {
System.out.println(repositoryEntityLinks.linkToCollectionResource(SomeClass.class));
}
}
The code is based on spring-data-rest-tests-core: AbstractControllerIntegrationTests, TestMvcClient.

HttpServletRequest injection on RequestScoped bean CDI

I'm looking for a way in order to inject a #RequestScoped custom class into my #Stateless JAX-RS endpoint:
I want each time the application receives a request my custom class is injected in my JAX-RS endpoint.
Custom class:
#RequestScoped
public class CurrentTransaction {
private String user;
private String token;
#PersistenceContext(name="mysql")
protected EntityManager em;
#Inject HttpServletRequest request;
public CurrentTransaction() {
this.user = request.getHeader("user");
this.token = request.getHeader("token");
}
//getters and setters ...
}
So, I declare my CurrentTransaction class as #RequestScoped in order to be initialized each time a request is received.
In order to do this, I need to access to HttpServletResquest in order to get header parameters.
JAX-RS endpoint:
#Stateless
#Path("/areas")
public class AreasEndpoint {
#PersistenceContext(unitName = "mysql")
protected EntityManager em;
#Inject
protected CurrentTransaction current_user_service;
#POST
#Path("postman")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Authentication
public Response create(AreaRequest request) {
if (this.current_user_service.getUser() == null) {
System.out.println("Go!!!");
return Response.status(Status.FORBIDDEN).build();
} else {
System.out.println("---- user: " + this.current_user_service.getUser());
System.out.println("---- token: " + this.current_user_service.getToken());
}
}
// ...
}
CDI arrive to perform the constructor of CurrentTransaction class. However, HttpServletRequest request field is not initialized (injected).
What am I doing wrong?
A late answer on this one--maybe useful to other readers: dependency injection in CDI is done in the following order:
the constructor is invoked
fields are injected
#PostConstruct annotated method is invoked
The last point is where you want to step in for further initialization that needs access on the injected fields:
#Inject HttpServletRequest request;
public CurrentTransaction() {
// field injection has not yet taken place here
}
#PostConstruct
public void init() {
// the injected request is now available
this.user = request.getHeader("user");
this.token = request.getHeader("token");
}

How can I get resource annotations in a Jersey 2.4 filter?

My question is essentially the same as this one: How can I get resource annotations in a Jersey ContainerResponseFilter.
But I'm using Java Jersey 2.4 and can't find any sign of the ResourceFilterFactory or ResourceFilter classes. The documentation also doesn't mention them. Have they been deprecated or are they just really well hidden? If they've been deprecated, what can I use instead? Is there now a way with Jersey 2.4 and 2.5 to get the resource annotations from a ContainerRequestFilter?
Thanks
If you want to modify processing of a request based on annotations available on a resource method/class then I'd recommend using DynamicFeature from JAX-RS 2.0. Using DynamicFeatures you can assign specific providers for a subset of available resource methods. For example, consider I have a resource class like:
#Path("helloworld")
public class HelloWorldResource {
#GET
#Produces("text/plain")
public String getHello() {
return "Hello World!";
}
}
And I'd like to assign a ContainerRequestFilter to it. I'll create:
#Provider
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
if ("HelloWorldResource".equals(resourceInfo.getResourceClass().getSimpleName())
&& "getHello".equals(resourceInfo.getResourceMethod().getName())) {
context.register(MyContainerRequestFilter.class);
}
}
}
And after registration (if you're using package scanning then you don't need to register it in case it has #Provider annotation on it) MyContainerRequestFilter will be associated with your resource method.
On the other hand you can always inject ResourceInfo in your filter (it can't be annotated with #PreMatching) and obtain the annotations from it:
#Provider
public class MyContainerRequestFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
resourceInfo.getResourceMethod().getDeclaredAnnotations();
}
}

Categories