<< Back to previous view

[GLASSFISH-16236] RunAs does not propagate Identity from Servlet.init Created: 18/Mar/11  Updated: 03/Jan/13  Resolved: 03/Jan/13

Status: Resolved
Project: glassfish
Component/s: security, web_container
Affects Version/s: 3.1_b43
Fix Version/s: 4.0_b71

Type: Improvement Priority: Major
Reporter: joelstewart Assignee: jazheng
Resolution: Fixed Votes: 0
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)


File Attachments: File runastest.ear    
Tags: 3_1_1-scrubbed 3_1_2-exclude init runas servlet
Participants: Hong Zhang, jazheng, joelstewart, scatari and Shing Wai Chan

 Description   

Calls to Injected EJB in a Servlet are not propagated when calling the EJB from init.
Per Servlet Spec Appendix 8:
"Clarification: "run-as" identity must apply to all calls from a servlet including
init() and destroy() (12.7)"

See forum discussion http://www.java.net/forum/topic/glassfish/glassfish/security-identity-propogation-servlet-runas?force=641

See attached Example Application EAR. The example application deploys an ejb and web module.

The EJB module deploys 2 beans:

package runasejb;

import javax.annotation.Resource;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.PermitAll;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

/**

  • The RunAsEJB is declares SystemRole as a used role. This is defined
  • in the application.xml and sun-application.xml. The EJB has two methods
  • to return the string value of the caller principal and boolean value
  • if the caller has SystemRole
  • @author joel@stewbeans.com
    */
    @Stateless
    @DeclareRoles("SystemRole")
    @PermitAll
    public class RunAsEJB
    {

@Resource
SessionContext context;

public String getCallerPrincipal()
{ return context.getCallerPrincipal().getName(); }

public boolean isCallerInSystemRole(){ return context.isCallerInRole("SystemRole"); }
}



package runasejb;

import javax.annotation.Resource;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.PermitAll;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

/**
* The RunAsEJB declares SystemRole as a used role. This is defined
* in the application.xml and sun-application.xml. The EJB has two methods
* to return the string value of the caller principal and boolean value
* if the caller has SystemRole
* @author joel@stewbeans.com
*/
@Stateless
@DeclareRoles("SystemRole")
@PermitAll
public class RunAsEJB
{

@Resource
SessionContext context;

public String getCallerPrincipal()
{ return context.getCallerPrincipal().getName(); } }

public boolean isCallerInSystemRole(){ return context.isCallerInRole("SystemRole"); }
}

The Web Module has servlet:

package runastest;

import java.io.IOException;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.security.RunAs;
import javax.ejb.EJB;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import runasejb.RunAsEJB;

/**

  • The StartupServlet declares runas SystemRole. The
  • injected TestRunAsEJB should be passed the SystemRole identity as
  • configured in the web.xml / sun-web.xml
  • @author joel@stewbeans.com
    */
    @RunAs("SystemRole")
    public class StartupServlet extends HttpServlet implements ServletContextListener{

Logger log = Logger.getLogger(StartupServlet.class.getName());

@EJB
RunAsEJB runAsEJB;

@Override
public void contextInitialized(ServletContextEvent sce)

{ log.info("In StartupServlet contextInitialized()"); }

@PostConstruct
public void postConstruct(){ log.info("In StartupServlet postConstruct()"); }

/**

  • Init is called after &PostConstruct and ServletContextListener Context initialized.
    */
    public void init(){
    log.info("In StartupServlet init()");

String val = runAsEJB.getCallerPrincipal();

if ("SystemUser".equals(val)){ log.info("StartupServlet propagates SystemUser Principal."); }else{ log.warning("StartupServlet did NOT propagate SystemUser Principal."); }

if (runAsEJB.isCallerInSystemRole()){ log.info("StartupServlet propogates SystemRole Role."); }else{ log.warning("StartupServlet did NOT propagate SystemRole Role"); }

}

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{

log.info("In StartupServlet doGet()");

String val = runAsEJB.getCallerPrincipal();

if ("SystemUser".equals(val)){ log.info("StartupServlet.doGet() propagates SystemUser Principal."); }else{ log.warning("StartupServlet.doGet() did NOT propagate SystemUser Principal."); }

if (runAsEJB.isCallerInSystemRole()){ log.info("StartupServlet.doGet() propogates SystemRole Role."); }else{ log.warning("StartupServlet.doGet() did NOT propagate SystemRole Role"); }
}

@Override
public void contextDestroyed(ServletContextEvent sce)
{

}
}

The Web Module has deployment descriptors:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">

<listener>
<listener-class>runastest.StartupServlet</listener-class>
</listener>

<servlet>
<servlet-name>RunAsTest</servlet-name>
<servlet-class>runastest.StartupServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>RunAsTest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

<!-- Form authentication configuration parameters -->
<login-config>
<auth-method>FORM</auth-method>
<realm-name>admin-realm</realm-name>
</login-config>

<security-role>
<description/>
<role-name>SystemRole</role-name>
</security-role>

</web-app>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
<sun-web-app error-url="">
<context-root>/RunAsTestEar-war</context-root>
<class-loader delegate="true"/>
<jsp-config>
<property name="keepgenerated" value="true">
<description>Keep a copy of the generated servlet class' java code.</description>
</property>
</jsp-config>
</sun-web-app>

(note the security-role-mapping is defined in application.xml - addign to sun-web.xml as well makes no difference)

The application has descriptors
<?xml version="1.0" encoding="UTF-8"?>
<application version="6" 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/application_6.xsd">
<display-name>RunAsTest</display-name>
<module>
<ejb>RunAsTestEar-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>RunAsTestEar-war.war</web-uri>
<context-root>runastest</context-root>
</web>
</module>
<security-role>
<role-name>SystemRole</role-name>
</security-role>
</application>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-application PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Java EE Application 6.0//EN" "http://www.sun.com/software/appserver/dtds/sun-application_6_0-0.dtd">
<sun-application>
<security-role-mapping>
<role-name>SystemRole</role-name>
<principal-name>SystemUser</principal-name>
</security-role-mapping>
<realm>admin-realm</realm>
</sun-application>

Startup Logging Output:
WARNING: Realm admin-realm: com.sun.enterprise.security.auth.realm.NoSuchUserException: No such user [SystemUser].
WARNING: Realm admin-realm: com.sun.enterprise.security.auth.realm.NoSuchUserException: No such user [SystemUser].
WARNING: DPL8019: The run-as principal SystemUser was assigned by the deployment system based on the specified role. Please consider defining an explicit run-as principal in the sun-specific deployment descriptor.
WARNING: DPL8019: The run-as principal SystemUser was assigned by the deployment system based on the specified role. Please consider defining an explicit run-as principal in the sun-specific deployment descriptor.
INFO: Portable JNDI names for EJB RunAsEJB : [java:global/RunAsTestEar/RunAsTestEar-ejb/RunAsEJB, java:global/RunAsTestEar/RunAsTestEar-ejb/RunAsEJB!runasejb.RunAsEJB]
INFO: Portable JNDI names for EJB SingletonEJB : [java:global/RunAsTestEar/RunAsTestEar-ejb/SingletonEJB, java:global/RunAsTestEar/RunAsTestEar-ejb/SingletonEJB!runasejb.SingletonEJB]
WARNING: Realm admin-realm: com.sun.enterprise.security.auth.realm.NoSuchUserException: No such user [SystemUser].
WARNING: Realm admin-realm: com.sun.enterprise.security.auth.realm.NoSuchUserException: No such user [SystemUser].
INFO: In SingletonEJB PostConstruct
INFO: SingletonEJB propagates SystemUser Principal.
INFO: SingletonEJB propogates SystemRole Role.
INFO: In StartupServlet postConstruct()
INFO: In StartupServlet contextInitialized()
INFO: In StartupServlet postConstruct()
INFO: In StartupServlet init()
WARNING: StartupServlet did NOT propagate SystemUser Principal.
WARNING: StartupServlet did NOT propagate SystemRole Role
INFO: WEB0671: Loading application RunAsTestEar#RunAsTestEar-war.war at [runastest]
INFO: RunAsTestEar was successfully deployed in 551 milliseconds.

Access the Servlet get method at /runastest and you will see output:
INFO: In StartupServlet doGet()
INFO: StartupServlet.doGet() propagates SystemUser Principal.
INFO: StartupServlet.doGet() propogates SystemRole Role.

CONCLUSION:
1_ Caller identity IS propagated from the Startup EJB post construct.
2_ The Servlet init method does NOT propagate the caller identity !!!!BUG!!!!
3_ Caller identity IS propagated from Servlet Get (telling me the configuration is correct).
4_ (also - the PostConstruct on the servlet is called twice BUG)



 Comments   
Comment by joelstewart [ 18/Mar/11 02:05 PM ]

SingletonEJB source code:

package runasejb;

import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.security.RunAs;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Stateless;

/**

  • The SingletonEJB demonstrates how StartupEJBs properly assume the
  • SystemUser identity in PostConstruct.
  • @author joel@stewbeans.com
    */
    @Singleton
    @Startup
    @RunAs("SystemRole")
    public class SingletonEJB {

Logger log = Logger.getLogger(SingletonEJB.class.getName());

@EJB
RunAsEJB runAsEJB;

@PostConstruct
public void postConstruct(){
log.info("In SingletonEJB PostConstruct");

String val = runAsEJB.getCallerPrincipal();

if ("SystemUser".equals(val)){ log.info("SingletonEJB propagates SystemUser Principal."); }else{ log.warning("SingletonEJB did NOT propagate SystemUser Principal."); }

if (runAsEJB.isCallerInSystemRole()){ log.info("SingletonEJB propogates SystemRole Role."); }else{ log.warning("SingletonEJB did NOT propagate SystemRole Role"); }

}
}

Comment by Hong Zhang [ 18/Mar/11 02:06 PM ]

assign to Shingwai for initial evaluation

Comment by Shing Wai Chan [ 21/Mar/11 09:27 AM ]

Need clarifications on Servlet spec on @RunAs for init / destroy.

Comment by joelstewart [ 25/Mar/11 10:38 AM ]

Can I assist you in clarification?

The Spec: JSR-000315 Java(tm) Servlet 3.0 Specification ("Specification")
Version: 3.0
Status: Final Release
Release: 10 December 2009

Appendix A.8 page 202.

The next to last bullet point:

  • Clarification: “run-as” identity must apply to all calls from a servlet including
    init() and destroy() (12.7)
Comment by Shing Wai Chan [ 02/Jun/11 11:02 AM ]

The following is comment from Ron Monzillo:


I took a closer look at the spec, and in "A.8 Changes Since Servlet 2.3", it states

Clarification: “run-as” identity must apply to all calls from a servlet including init() and destroy() (12.7)

I can't find any such clarification in the section 12.7 or in the security chapter, so the clarification may have been
lost, but the appendix clearly notes the intent, and thus I think it is is required that a specified run-as identity be in effect during init() and destroy().

Note that section 15.3.1 Propagation of Security Identity in EJB Calls, requires that propagation occur whenever
an ejb is called by a servlet (without consideration of the Servlet method form which the ejb call is made). That may be going too far, but it would at least support that run-as should be honored within init(); where it is has become common practice to invoke ejbs, and where (unlike the case of calls to ejbs from servlet context listeners), there is a mapping to a specific servlet on which to look for a run-as specification.

bullet 5 under section "2.3.3.3 Asynchronous processing", made some clarification wrt to Asynchronous processing,
which may have resulted in some reconsideration or simplification of the clarification for init() and destroy(), but unless there was an explicit decision to rescind the prior clarification, I think the RI must ensure that runas is set during init() and destroy().

please note that when a run-as identity is set by annotation, the annotation has no effect on init() and destory() unless those methods are declared (i.e., overridden) in the servlet implementation class.


We may like to clarify the spec and do the corresponding implementation.

Comment by scatari [ 02/Jun/11 04:16 PM ]

Needs to be clarified in the spec.

Comment by Shing Wai Chan [ 07/Aug/12 08:30 PM ]

assign to security team

Comment by Shing Wai Chan [ 23/Oct/12 04:12 PM ]

Per discussion with Michael, we will change this to "Improvement" for tracking purpose.

Comment by jazheng [ 03/Jan/13 08:40 PM ]

Sending appserver/security/webintegration/src/main/java/com/sun/web/security/RealmAdapter.java
Sending appserver/web/web-glue/src/main/java/com/sun/enterprise/web/WebComponentInvocation.java
Sending appserver/web/web-glue/src/main/java/com/sun/web/server/J2EEInstanceListener.java
Sending nucleus/common/glassfish-api/src/main/java/org/glassfish/api/invocation/ComponentInvocation.java

Committed revision 57924.

Cause of the problem:
The web security event handler needs to retrieve RunAs based on the servletName, but servletName is not available from servlet instance before servlet.init() is called.

Changes:
Add an optional instanceName field to ComponentInvocation/WebComponentInvocation, so the event handler can retrieve it later.

Generated at Wed Apr 23 17:59:23 UTC 2014 using JIRA 4.0.2#472.