Hot questions for Using Enterprise JavaBeans in security

Question:

I am making a Web application using Backbone.js, Bootstrap, NetBeans IDE 8.0, Java EE 7, JDK 8, WildFly server 8.1.0, JBoss RESTEasy (resteasy-jaxrs-3.0.8), JBoss 2.2.22, JBoss EJB 3.

I am (relatively) new to Web development, and as such I have only just started to grasp a lot of basic concepts and technologies. I am trying to build a permission system with users and roles into a Web application, but I cannot seem to make the @RolesAllowed annotation to work in my RESTful Web Service. I am already working on this problem for a few days.

I have a RESTful resource (Java Enterprise/Session Bean?) called UserResource.java, in here I have a method create to create a new user for the application:

import java.net.URI;
import java.security.Principal;
import java.util.List;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import org.jboss.ejb3.annotation.SecurityDomain;

@Stateless
@SecurityDomain("other")
@Path("/user")
public class UserResource {
    @EJB(name = "UserServiceImp")
    UserService userService;

    @Context
    private UriInfo uriInfo;

    @RolesAllowed({"admin"})
    @Path("create")
    @POST
    public Response create(CreateRequest request) {        
        try {
            System.out.println("Start of create method");
            User user = userService.createUser(request);
            return getCreateResponse(user);
        }
        catch (Exception e){
            return Response.status(401).entity("Failed to create user").build();
        }
    }
}

This create method works if I use the @PermitAll annotation, but it fails with an error if I use the @RolesAllowed annotation.

I have this Backbone view CreateUserView which provides a form (in HTML) to an end user (with admin rights) for creating new users for the application. On clicking the submit button, JSON data gets send to the url 'rest/user/create' for creating a new user. Before the create method in UserResource.java gets executed, my SecurityInterceptor.java (which implements javax.ws.rs.container.ContainerRequestFilter) checks if the user has the required permissions. I have debugged this thorough and the Security Interceptor is functioning as it should. So after the Security Interceptor gives clear access, something goes wrong in UserResource.java. (As a side note, not sure if this is important, but I believe the Security Interceptor is based on this blog post about RESTEasy security. I was working on the application with another guy, he initially implemented it, so I'm not sure.. but it looks almost identical. Anyway that guy has moved on to another project a few weeks ago.)

The error I am getting (output from the server) is the following:

16:45:25,775 ERROR [org.jboss.as.ejb3.invocation] (default task-60) JBAS014134: EJB Invocation failed on component UserResource for method public javax.ws.rs.core.Response org.profit.pgb.rest.resource.UserResource.create(org.profit.pgb.rest.api.CreateRequest): javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public javax.ws.rs.core.Response org.profit.pgb.rest.resource.UserResource.create(org.profit.pgb.rest.api.CreateRequest) of bean: UserResource is not allowed
    at org.jboss.as.ejb3.security.AuthorizationInterceptor.processInvocation(AuthorizationInterceptor.java:135) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:95) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:55) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:64)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:326)
    at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:448)
    at org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:61)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:326)
    at org.jboss.invocation.PrivilegedWithCombinerInterceptor.processInvocation(PrivilegedWithCombinerInterceptor.java:80)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:185)
    at org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:182)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73)
    at org.profit.pgb.rest.resource.UserResource$$$view45.create(Unknown Source) [classes:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.8.0_11]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [rt.jar:1.8.0_11]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.8.0_11]
    at java.lang.reflect.Method.invoke(Method.java:483) [rt.jar:1.8.0_11]
    at org.jboss.weld.util.reflection.Reflections.invokeAndUnwrap(Reflections.java:401) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.bean.proxy.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:99) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.bean.proxy.InjectionPointPropagatingEnterpriseTargetBeanInstance.invoke(InjectionPointPropagatingEnterpriseTargetBeanInstance.java:65) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:100) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.profit.pgb.rest.resource.UserResource$Proxy$_$$_Weld$EnterpriseProxy$.create(Unknown Source) [classes:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.8.0_11]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [rt.jar:1.8.0_11]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.8.0_11]
    at java.lang.reflect.Method.invoke(Method.java:483) [rt.jar:1.8.0_11]
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:137) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:296) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:250) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:237) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) [resteasy-jaxrs-3.0.8.Final.jar:]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final]
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:51) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:177) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:727) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_11]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_11]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_11]

16:45:25,957 ERROR [io.undertow.request] (default task-60) UT005023: Exception handling request to /pgb/rest/user/create: org.jboss.resteasy.spi.UnhandledException: javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public javax.ws.rs.core.Response org.profit.pgb.rest.resource.UserResource.create(org.profit.pgb.rest.api.CreateRequest) of bean: UserResource is not allowed
    at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:76) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:212) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:149) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:372) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) [resteasy-jaxrs-3.0.8.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) [resteasy-jaxrs-3.0.8.Final.jar:]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final]
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:51) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:177) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:727) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_11]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_11]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_11]
Caused by: javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public javax.ws.rs.core.Response org.profit.pgb.rest.resource.UserResource.create(org.profit.pgb.rest.api.CreateRequest) of bean: UserResource is not allowed
    at org.jboss.as.ejb3.security.AuthorizationInterceptor.processInvocation(AuthorizationInterceptor.java:135) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:95) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64) [wildfly-ejb3-8.1.0.Final.jar:8.1.0.Final]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at ... etc. (not fully shown due to SO's character limit on questions..)

I've seen other people ask similar questions, some which remain unanswered to this day (e.g. jax-rs service RolesAllowed Annotation throwing exception) and others which have solutions that either do not work for me or maybe I do not apply the solution correctly (e.g. RESTEasy support for JAX-RS @RolesAllowed).

I found this: https://developer.jboss.org/thread/177728?start=0&tstart=0 (titled: "@RolesAllowed, @DenyAll require presence of org.jboss.ejb3.annotation.SecurityDomain?"), In tried the solution but I cannot get it to work for my project. Not sure if the solution is not applicable for my situation or if I'm just doing it wrong.

I found this: https://developer.jboss.org/message/720815 (titled: "is this a defect for processing org.jboss.ejb3.annotation.SecurityDomain ?"), but I do not understand where my jboss-ejb-client.properties are supposed to be. I think they set their project up quite different from me. So no luck with that.

I found a guide on EJB3 security, as suggested there, I provided the following code in my standalone.xml-file:

<security-domain name="other" cache-type="default">
    <authentication>
        <login-module code="Remoting" flag="optional">
            <module-option name="password-stacking" value="useFirstPass"/>
        </login-module>
        <login-module code="RealmDirect" flag="required">
            <module-option name="password-stacking" value="useFirstPass"/>
        </login-module>
    </authentication>
</security-domain>

But that did not solve anything at all. I am not sure if it did anything.

Finally, I found this SO question: RESTEasy support for JAX-RS @RolesAllowed (which references to the RESTEasy Documentation). Even though I also mentioned this question a few paragraphs up as containing a solution that does not work for me, it did change the error to another error. As suggested there I added a <context-param>-block to my web.xml-file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>PGB</web-resource-name>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>

        <user-data-constraint>
            <!-- use of SSL is required when CONFIDENTIAL is specified -->
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <context-param>
      <param-name>resteasy.role.based.security</param-name>
      <param-value>true</param-value>
   </context-param>
</web-app>

Which results in the following error if I try to create a new user (not completely posted here due to character limit of SO questions):

16:58:45,992 WARN  [org.jboss.resteasy.core.ExceptionHandler] (default task-61) failed to execute: javax.ws.rs.ForbiddenException: HTTP 403 Forbidden
    at org.jboss.resteasy.plugins.interceptors.RoleBasedSecurityFilter.filter(RoleBasedSecurityFilter.java:45) [resteasy-jaxrs-3.0.8.Final.jar:]
    at ... etc.

This error is not very helpful either, in fact, I find even less information when I search on that error than when I search on the previous error. So, I am not sure if that is a step into the right direction. What is better to have returned from the server? A status 500 (Internal Server Error) or a status 403 (Forbidden)? Also, if after applying that "solution" I change the annotation to @PermitAll, then the creation of a new user works just as before, so it didn't really make the situation any worse.

However, I can find the source code of RoleBasedSecurityFilter, which shows that it throws the ForbiddenException. It shows that a certain isUserInRole method must return true, but it does not do that in my application. I can't get it to return true. It makes me wonder, Is it possible to disable RoleBasedSecurityFilter.java of RESTEasy?

I also found the following SO question: @RolesAllowed cannot be resolved with Jersey, which is answered by Abhijit Sarkar, and he refers to an IBM article. Maybe the solution to my problem lies in there, but I have not found it yet.. Anyway, it is suggested there that either adding a security-role block to my web.xml-file or adding a @DeclareRoles annotation to my UserResource.java file should solve the problem, but the HTTP 403 Forbidden warning still remains after I do so. It is very frustrating.

My security-role block (in web.xml) looks as follows:

<security-role id="role_admin">
    <description>This is role 1 (admin)</description>
    <role-name>admin</role-name>
</security-role>

And I added the following security-constraint block (in web.xml): (note the auth-constraint)

<security-constraint>
    <web-resource-collection>
        <web-resource-name>PGB</web-resource-name>
        <url-pattern>/rest/user/create</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint id="AuthConstraint_createUser">
        <description> Only admin can create a new user</description>
        <role-name>admin</role-name>
    </auth-constraint>
    <user-data-constraint>
        <!-- use of SSL is required when CONFIDENTIAL is specified -->
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

I realize now that my problem lies either in the deployment descriptors (web.xml) or the server configuration (WildFly's standalone-full.xml).

I added a login-config element (with FORM authentication) to my web.xml file. Changed my login HTML page to fit the required format. I followed some of the step of this article on migrating a Java EE App from GlassFish to WildFly, but I think I still did something wrong because I always get at the login error page when trying to log in with valid user credentials.

See here the security-domain element I added to my WildFly configuration:

<security-domain name="app" cache-type="default">
    <authentication>
        <login-module code="Database" flag="required">
            <module-option name="dsJndiName" value="java:jboss/datasources/mySQL_pool_rel"/>
            <module-option name="principalsQuery" value="select hashed_password from user where email_address=?"/>
            <module-option name="rolesQuery" value="select role_name, 'Roles' from role r inner join user u on r.role_type = u.role_type where u.email_address = ?"/>
            <module-option name="hashAlgorithm" value="SHA-256"/>
            <module-option name="hashEncoding" value="BASE64"/>
            <module-option name="unauthenticatedIdentity" value="guest"/>
        </login-module>
        <login-module code="RoleMapping" flag="required">
             <module-option name="rolesProperties" value="file:${jboss.server.config.dir}/app.properties"/>
             <module-option name="replaceRole" value="false"/>
        </login-module>
    </authentication>
</security-domain>

I think something is wrong with my rolesQuery defined there, but I can't figure out what.

I would love to know how to implement role-based security for my Web application. Therefore, I also accept other approaches to role-based security, as long as it works. Suggestions are welcome.

I also placed my question on developer.jboss.org, but I also got no response there yet.

I provided one workaround solution to my problem as an answer to this question, but it is not a real solution (as explained in that answer). I am still interested in doing this the right way.


Answer:

Just had the same problem.

It's the @Stateless annotation. The marks your class as an EJB and the container attempts to enforce EJB security.

I discovered this by writing filters and my own SecurityContext, only to find that my SecurityContext was never referenced.

Removing @Stateless resulted in getUserPrincipal() being called on the SecurityContext.

Question:

The @RunAs annotation allows to access the bean by specifying role's name only. This is implies that any foreign client able to easily access my bean just pointing the role name, isn't it?

I suppose that the caller and bean locate on different servers.

So what is @RunAs needed for, if it is so easy to falsify the client? Or how to use this correctly, if i'm wrong?


Answer:

It is a bit different. See Chapter 9.6 in WebSphere Application Server V7.0 Security Guide for more details and examples.

When a bean calls a method in another bean, the identity of the caller is, by default, propagated to the next. In this way, all EJB methods in the calling chain see the same principal if they were to call the getCallerPrincipal() method.

Occasionally, however, it is desirable for one EJB to call another EJB with a previously defined identity, for instance, one that is a member of a specific role.

The @RunAs annotation defines the role that will be used for delegation. You can still protect the first bean using @RolesAllowed

This runAsRole must be mapped to actual user from user registry during application deployment.

Question:

I tried understanding the use of @RunAs annotation from the java docs but I did not understand the use of it. Can anyone please explain?

What I understand is, in some cases, if an authenticated user with a different role, wants to access an ejb method which is allowed to be accessed by users with certain roles only, then the caller ejb can annotate itself to be run as the expected role and can access the ejb method.

So I wrote the below code but my understanding is wrong.

JAX-RS Class:

package com.jee.beginner.rest;

import java.security.Principal;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.groups.ConvertGroup;
import javax.validation.groups.Default;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;

import com.jee.beginner.custom.validation.Create;
import com.jee.beginner.custom.validation.Update;
import com.jee.beginner.domain.Student;
import com.jee.beginner.service.StudentService;
import com.jee.beginner.service.proxy.StudentServiceProxy;

@Path("student")
public class StudentResource {

    @Inject
    private Principal principal;

    @Inject
    private StudentService studentService;

    @Inject
    private StudentServiceProxy studentServiceProxy;

    @GET
    @Path("details/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Student getDetails(@PathParam("id") @Min(value = 2, message = "ID cannot be less than 2") int id,
            @QueryParam("id") int qid, @Context UriInfo uriInfo) {

        return studentServiceProxy.getDetails(id);
    }

    @POST
    @Path("new")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Student addStudent(@Valid @ConvertGroup(from = Default.class, to = Create.class) final Student student) {

        return studentService.addStudent(student);
    }

    @POST
    @Path("update")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Student updateStudent(@Valid @ConvertGroup(from = Default.class, to = Update.class) final Student student) {

        return student;
    }
}

A proxy class. This class is annotated as @RunAs("admin")

package com.jee.beginner.service.proxy;

import javax.annotation.security.RunAs;
import javax.ejb.Stateless;
import javax.inject.Inject;

import com.jee.beginner.domain.Student;
import com.jee.beginner.service.StudentService;

@RunAs("admin")
@Stateless
public class StudentServiceProxy {

    @Inject
    private StudentService studentService;

    public Student getDetails(int id) {
        return studentService.getDetails(id);
    }
}

Service class:

package com.jee.beginner.service;

import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBContext;
import javax.ejb.Stateless;

import com.jee.beginner.domain.Student;

@Stateless
public class StudentService {

    @Resource
    private EJBContext context;

    @RolesAllowed({ "admin", "guest" })
    public Student addStudent(final Student student) {
        System.out.println(context.isCallerInRole("admin"));
        return student;
    }

    @RolesAllowed({ "admin" })
    public Student getDetails(int id) {
        Student student = new Student();
        student.setId(id);
        student.setName("noname");
        return student;
    }
}

I created a realm and added two users

UserA - admin, UserB - guest

Without the RunAs annotation, UserA was able to access the method as expected and UserB was not able to access the method as expected.

As soon as I added the RunAs annotation, both the users were not able to access getDetails method.

What I thought was UserB would be able to access the method now because the Proxy is annotated with RunAs admin and I thought StudentService would treat the user as admin role. But infact what happened was UserA also was not able to access the method.

Can anyone please explain me the significance of RunAs annotation?


Answer:

The @RunAs annotation can be used for the use cases that @Steve C points out, but also for exactly the use case you are describing.

Your code and your assumptions are correct. It's not working because the way some containers (Wildfly for example) implement EJB method permissions by default. When no security annotations are used at all, all methods are assumed to be unchecked, as if they where annotated with @PermitAll. But when any security annotation is used on your deployment and a method hasn't an explicit permissions (at the class level or method level), Wildfly treats it as if they had a @DenyAll.

So, your StudentService is correct, but in your StudentServiceProxy, the getDetails method hasn't any method permission. You should annotate it (at the method level or class level) with @PermitAll (any user, event unauthenicated ones can execute it), @RolesAllowed({ "**" }) (any authenticated user can execute it), or @RolesAllowed({ "admin", "guest" }) (users with admin or guest role an execute it).

If using wildfly/jboss you can also change the default behaviour of the ejb3 subsystem. Check: https://docs.jboss.org/author/display/WFLY/Securing+EJBs

Question:


Answer:

I have found the answer, the question was already asked here

SecurityContext is only for the JAX-RS bean, you need to inject an EJBContext object inplace of SecurityContext one into the other java beans. You can also use the SessionContext object but EJBContext interface resembles the SecurityContext one. Here is the working version:

@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);
    }
}

Question:

I need to expose a @remote view for an EJB so that it can be requested from other Java applications (running in different JVMs). I was wondering, what security concerns should I take care of so that the data exchanged is secure and that there is no unauthorized access.

At a minimum, I think the connection should be encrypted using SSL/TLS.


Answer:

Answering purely in terms of Security,

  1. Include auth(hashed using sha2/md5) mechanism in the EJB receiver class to address authorization.
  2. Enable https mandate for authenticity.
  3. Use hybrid cryptography(private key to share the public key, and the public key to encrypt the data in and out) this will reduce the overall time taken. This addresses confidentiality.
  4. Take care of corner case attack vulnerabilities like DOS/DDOS. Several others like MOM are taken care already by the public cryptography.

These are basic ones that I can think of. Once you implement them you can strengthen your application/system further.

Hope this helps.