When a user logs in, I need to insert a message (the host name, just as an example) into the database. Since it is just a text message, injecting an EJB to a client (Servlets, JSP, JSF or something else) is quite unnecessary.
The client, in this case, it an authentication Filter through which I send the host name to a message-driven bean. With the help of a message-driven bean, the message is stored in a queue (not topic) which is then submitted to an EJB by injecting the EJB into this messaging bean.
The strategy mentioned here works fine. The problem arises when the EJB is enforced a security constraint. In which case, it throws an exception regarding the security.
The message-driven bean is as follows.
@MessageDriven(mappedName = "jms/destination", activationConfig = {
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class UserStatusMessageBean implements MessageListener
{
@Resource
private MessageDrivenContext messageDrivenContext;
@EJB
private UserStatusBeanRemote userStatusBeanRemote;
public UserStatusMessageBean() {}
@Override
public void onMessage(Message message)
{
TextMessage textMessage;
try
{
if(message instanceof TextMessage)
{
textMessage = (TextMessage) message;
userStatusBeanRemote.addHost(textMessage.getText());
//This EJB method causes the exception as given below.
}
else
{
System.out.println("Message is of wrong type : " +message.getClass().getName());
}
}
catch (JMSException e)
{
messageDrivenContext.setRollbackOnly();
System.out.println(e);
}
catch (Throwable e)
{
System.out.println(e);
}
}
}
The stateless EJB has only one method until now which is responsible for inserting the message into the database using the JPA criteria API:
@Stateless
@DeclareRoles(value={"ROLE_ADMIN", "ROLE_USER"})
@RolesAllowed(value={"ROLE_ADMIN"})
public class UserStatusBean implements UserStatusBeanRemote
{
@Override
public void addHost(String hostName)
{
//Business logic to add the host name to the database.
}
}
And the filter which authenticates the user is shown below (in case, it is needed to be reviewed).
@WebFilter(filterName = "SecurityCheck", urlPatterns = {"/jass/*"})
public final class SecurityCheck implements Filter
{
@Resource(mappedName="jms/destinationFactory")
private ConnectionFactory connectionFactory;
@Resource(mappedName="jms/destination")
private Queue queue;
@EJB
private final UserBeanLocal userService=null;
public SecurityCheck() {}
private void sendJMSMessageToDestination(String message) throws JMSException
{
Connection connection = null;
Session session = null;
try
{
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(queue);
TextMessage textMessage = session.createTextMessage();
textMessage.setText(message);
messageProducer.send(textMessage);
}
finally
{
if(session!=null){session.close();}
if(connection!=null){connection.close();}
}
}
private void doBeforeProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException
{
HttpServletRequest httpServletRequest=(HttpServletRequest)request;
httpServletRequest.login(httpServletRequest.getParameter("userName"), httpServletRequest.getParameter("password"));
}
private void doAfterProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException, JMSException
{
HttpServletRequest httpServletRequest=(HttpServletRequest)request;
HttpServletResponse httpServletResponse=(HttpServletResponse)response;
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, Object> sessionMap = externalContext.getSessionMap();
if(httpServletRequest.isUserInRole("ROLE_USER"))
{
sendJMSMessageToDestination(httpServletRequest.getLocalName());//Send a text message through a message-driven bean.
String userName = httpServletRequest.getParameter("userName");
UserTable userTable = userService.setLastLogin(userName);
userTable.setPassword(null);
sessionMap.put("userName", userTable!=null?userTable.getFirstName():"Unknown");
sessionMap.put("user", userTable);
httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.sendRedirect("../user_side/Home.jsf");
}
else if(httpServletRequest.isUserInRole("ROLE_ADMIN"))
{
sendJMSMessageToDestination(httpServletRequest.getLocalName());//Send a text message through a message-driven bean.
String userName = httpServletRequest.getParameter("userName");
UserTable userTable = userService.setLastLogin(userName);
userTable.setPassword(null);
sessionMap.put("adminName", userTable!=null?userTable.getFirstName():"Unknown");
sessionMap.put("user", userTable);
httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.sendRedirect("../admin_side/Home.jsf");
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
try
{
doBeforeProcessing(request, response);
}
catch (Exception e)
{
HttpServletResponse httpServletResponse=(HttpServletResponse)response;
//FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Incorrect user name and/or password. Access denied."));
httpServletResponse.sendRedirect("../utility/Login.jsf");
return;
}
chain.doFilter(request, response);
try
{
doAfterProcessing(request, response);
}
catch (JMSException ex)
{
Logger.getLogger(SecurityCheck.class.getName()).log(Level.SEVERE, null, ex);
}
}
//The rest of this filter.
}
The security applied here works fine elsewhere. The annotation @RolesAllowed(value={"ROLE_ADMIN"})
before the EJB UserStatusBean
causes the following exception to be thrown.
WARNING: EJB5184:A system exception occurred during an invocation on EJB UserStatusBean, method: public void ejb.message.UserStatusBean.addHost(java.lang.String)
WARNING: javax.ejb.AccessLocalException: Client not authorized for this invocation
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1895)
at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:204)
at com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:79)
at $Proxy366.addHost(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:239)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
at ejb.message.__UserStatusBeanRemote_Remote_DynamicStub.addHost(ejb/message/__UserStatusBeanRemote_Remote_DynamicStub.java)
at ejb.message._UserStatusBeanRemote_Wrapper.addHost(ejb/message/_UserStatusBeanRemote_Wrapper.java)
at bean.message.UserStatusMessageBean.onMessage(UserStatusMessageBean.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1219)
at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
at $Proxy406.onMessage(Unknown Source)
at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:283)
at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:107)
at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)
INFO: javax.ejb.EJBAccessException
at ejb.message._UserStatusBeanRemote_Wrapper.addHost(ejb/message/_UserStatusBeanRemote_Wrapper.java)
at bean.message.UserStatusMessageBean.onMessage(UserStatusMessageBean.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1219)
at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
at $Proxy406.onMessage(Unknown Source)
at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:283)
at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:107)
at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)
Caused by: java.rmi.AccessException: CORBA NO_PERMISSION 9998 Maybe; nested exception is:
org.omg.CORBA.NO_PERMISSION: vmcid: 0x2000 minor code: 1806 completed: Maybe
at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.mapSystemException(Util.java:264)
at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.wrapException(Util.java:695)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:249)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
at ejb.message.__UserStatusBeanRemote_Remote_DynamicStub.addHost(ejb/message/__UserStatusBeanRemote_Remote_DynamicStub.java)
... 30 more
Caused by: org.omg.CORBA.NO_PERMISSION: vmcid: 0x2000 minor code: 1806 completed: Maybe
Caused by: javax.ejb.AccessLocalException: Client not authorized for this invocation
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1895)
at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:204)
at com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:79)
at $Proxy366.addHost(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:239)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
at ejb.message.__UserStatusBeanRemote_Remote_DynamicStub.addHost(ejb/message/__UserStatusBeanRemote_Remote_DynamicStub.java)
at ejb.message._UserStatusBeanRemote_Wrapper.addHost(ejb/message/_UserStatusBeanRemote_Wrapper.java)
at bean.message.UserStatusMessageBean.onMessage(UserStatusMessageBean.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1219)
at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
at $Proxy406.onMessage(Unknown Source)
at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:283)
at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:107)
at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)
It works when this annotation @RolesAllowed(value={"ROLE_ADMIN"})
before the EJB is removed.
Is it possible to invoke such methods through a message-driven bean?
I'm using GlassFish 4.0.
@RunAs
annotation only worked (but not as expected), when I declared this list of roles -ROLE_USER,ROLE_ADMIN
in a file ream -Configurations->server-config->Security->Realms->file
in the Assign Groups field in this file realm. The EJB method is invoked by any user authenticated with any role specified in the file realm. This should not happen. The EJB method should only be invoked by a user whose authority isROLE_ADMIN
- because of@RolesAllowed(value={"ROLE_ADMIN"})
. The EJB method shouldn't be invoked, when MDB is annotated by@RunAs(value="ROLE_USER")
but this happens – Inlay