glassfish
  1. glassfish
  2. GLASSFISH-16236

RunAs does not propagate Identity from Servlet.init

    Details

    • Type: Improvement Improvement
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 3.1_b43
    • Fix Version/s: 4.0_b71
    • Component/s: security, web_container
    • Labels:
      None
    • 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)

      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)

        Activity

        Hide
        joelstewart added a comment -

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

        }
        }

        Show
        joelstewart added a comment - 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"); } } }
        Hide
        Hong Zhang added a comment -

        assign to Shingwai for initial evaluation

        Show
        Hong Zhang added a comment - assign to Shingwai for initial evaluation
        Hide
        Shing Wai Chan added a comment -

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

        Show
        Shing Wai Chan added a comment - Need clarifications on Servlet spec on @RunAs for init / destroy.
        Hide
        joelstewart added a comment - - edited

        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)
        Show
        joelstewart added a comment - - edited 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)
        Hide
        Shing Wai Chan added a comment -

        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.

        Show
        Shing Wai Chan added a comment - 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.
        Hide
        scatari added a comment -

        Needs to be clarified in the spec.

        Show
        scatari added a comment - Needs to be clarified in the spec.
        Hide
        Shing Wai Chan added a comment -

        assign to security team

        Show
        Shing Wai Chan added a comment - assign to security team
        Hide
        Shing Wai Chan added a comment -

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

        Show
        Shing Wai Chan added a comment - Per discussion with Michael, we will change this to "Improvement" for tracking purpose.
        Hide
        jazheng added a comment -

        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.

        Show
        jazheng added a comment - 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.

          People

          • Assignee:
            jazheng
            Reporter:
            joelstewart
          • Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: