ICEfaces
  1. ICEfaces
  2. ICE-2816

Propagate Spring SecurityContextHolder to pooled threads

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Invalid
    • Affects Version/s: 1.7Beta1
    • Fix Version/s: 1.8RC2, 1.8
    • Component/s: Framework
    • Labels:
      None
    • Environment:
      ICEfaces, acegi, Spring Security

      Description


      Acegi applies ThreadLocal security contexts (likely set in filters). When work is handed off to a thread in a thread pool, this context is not propagated. This needs to be addressed specifically for the acegi ThreadLocals.

      object was not found in the SecurityContext
      org.acegisecurity.AuthenticationCredentialsNotFoundException: VoterResource.authenticationNotFound: An Authentication object was n

      ot found in the SecurityContext
              at org.acegisecurity.vote.ResourceAccessBean.credentialsNotFound(ResourceAccessBean.java:181)
              at org.acegisecurity.vote.ResourceAccessBean.get(ResourceAccessBean.java:115)
              at com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:79)
              at org.springframework.faces.webflow.el.AbstractFlowExecutionPropertyResolver.getValue(AbstractFlowExecutionPropertyResolv

      er.java:77)
              at org.springframework.faces.webflow.el.WebFlowPropertyResolver.getValue(WebFlowPropertyResolver.java:70)
              at com.sun.facelets.el.LegacyELContext$LegacyELResolver.getValue(LegacyELContext.java:141)
              at com.sun.el.parser.AstValue.getValue(AstValue.java:96)
              at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:183)
              at com.sun.facelets.el.TagValueExpression.getValue(TagValueExpression.java:71)
              at com.sun.facelets.el.LegacyValueBinding.getValue(LegacyValueBinding.java:56)
              at javax.faces.component.UIComponentBase.isRendered(UIComponentBase.java:335)
              at com.icesoft.faces.component.paneltabset.PanelTab.isRendered(PanelTab.java:310)
              at com.icesoft.faces.component.paneltabset.PanelTabSetRenderer.encodeEnd(PanelTabSetRenderer.java:368)
              at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:740)
              at com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer.encodeParentAndChildren(DomBasicRenderer.java:362)

              at com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer.encodeParentAndChildren(DomBasicRenderer.java:358)

              at com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer.encodeParentAndChildren(DomBasicRenderer.java:358)

              at com.icesoft.faces.renderkit.dom_html_basic.GridRenderer.encodeChildren(GridRenderer.java:196)
              at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:721)
              at com.icesoft.faces.application.D2DViewHandler.renderResponse(D2DViewHandler.java:532)
              at com.icesoft.faces.application.D2DViewHandler.renderResponse(D2DViewHandler.java:536)
              at com.icesoft.faces.application.D2DViewHandler.renderResponse(D2DViewHandler.java:536)
              at com.icesoft.faces.application.D2DViewHandler.renderResponse(D2DViewHandler.java:536)
              at com.icesoft.faces.facelets.D2DFaceletViewHandler.renderResponse(D2DFaceletViewHandler.java:286)
              at com.icesoft.faces.application.D2DViewHandler.renderView(D2DViewHandler.java:154)
              at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:107)
              at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:245)
              at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:137)
              at com.icesoft.faces.webapp.xmlhttp.PersistentFacesState.render(PersistentFacesState.java:151)
              at com.icesoft.faces.webapp.xmlhttp.PersistentFacesState.executeAndRender(PersistentFacesState.java:293)
              at com.icesoft.faces.webapp.xmlhttp.PersistentFacesState$RenderRunner.run(PersistentFacesState.java:330)
              at edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:643)

              at edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:668)
              at java.lang.Thread.run(Thread.java:534)

        Issue Links

          Activity

          Hide
          Tyler Johnson added a comment -

          Loss of acegi security context also happens with our FileUpload component.

          09:56:02,885 ERROR D2DFaceletViewHandler:296 - Problem in renderResponse:
          //secure/template/facelet/mainTemplate.xhtml @40,94 test="#

          {authenticationController.currentUser.class.name eq 'java.lang.String'}"
          //secure/template/facelet/mainTemplate.xhtml @40,94 test="#{authenticationController.currentUser.class.name eq 'java.lang.String'}

          ": Error getting property 'currentUser' from bean of type org.acegisecurity.ui.webapp.jsf.AccessManagerAuthenticationController: java.lang.NullPointerException
          com.sun.facelets.tag.TagAttributeException:
          //secure/template/facelet/mainTemplate.xhtml @40,94 test="#

          {authenticationController.currentUser.class.name eq 'java.lang.String'}

          " //secure/template/facelet/mainTemplate.xhtml @40,94 test="#

          {authenticationController .currentUser.class.name eq 'java.lang.String'}

          ": Error getting property 'currentUser' from bean of type org.acegisecurity.ui.webap p.jsf.AccessManagerAuthenticationController: java.lang.NullPointerException
          at com.sun.facelets.tag.TagAttribute.getObject(TagAttribute.java:235)
          at com.sun.facelets.tag.TagAttribute.getBoolean(TagAttribute.java:79)
          at com.sun.facelets.tag.jstl.core.ChooseWhenHandler.isTestTrue(ChooseWhenHandler.java:49)
          at com.sun.facelets.tag.jstl.core.ChooseHandler.apply(ChooseHandler.java:67)
          at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47)
          at com.sun.facelets.tag.jsf.ComponentHandler.applyNextHandler(ComponentHandler.java:314)
          at com.sun.facelets.tag.jsf.ComponentHandler.apply(ComponentHandler.java:169)
          at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47

          Show
          Tyler Johnson added a comment - Loss of acegi security context also happens with our FileUpload component. 09:56:02,885 ERROR D2DFaceletViewHandler:296 - Problem in renderResponse: //secure/template/facelet/mainTemplate.xhtml @40,94 test="# {authenticationController.currentUser.class.name eq 'java.lang.String'}" //secure/template/facelet/mainTemplate.xhtml @40,94 test="#{authenticationController.currentUser.class.name eq 'java.lang.String'} ": Error getting property 'currentUser' from bean of type org.acegisecurity.ui.webapp.jsf.AccessManagerAuthenticationController: java.lang.NullPointerException com.sun.facelets.tag.TagAttributeException: //secure/template/facelet/mainTemplate.xhtml @40,94 test="# {authenticationController.currentUser.class.name eq 'java.lang.String'} " //secure/template/facelet/mainTemplate.xhtml @40,94 test="# {authenticationController .currentUser.class.name eq 'java.lang.String'} ": Error getting property 'currentUser' from bean of type org.acegisecurity.ui.webap p.jsf.AccessManagerAuthenticationController: java.lang.NullPointerException at com.sun.facelets.tag.TagAttribute.getObject(TagAttribute.java:235) at com.sun.facelets.tag.TagAttribute.getBoolean(TagAttribute.java:79) at com.sun.facelets.tag.jstl.core.ChooseWhenHandler.isTestTrue(ChooseWhenHandler.java:49) at com.sun.facelets.tag.jstl.core.ChooseHandler.apply(ChooseHandler.java:67) at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47) at com.sun.facelets.tag.jsf.ComponentHandler.applyNextHandler(ComponentHandler.java:314) at com.sun.facelets.tag.jsf.ComponentHandler.apply(ComponentHandler.java:169) at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47
          Hide
          Ted Goddard added a comment -

          > We have an ICEFaces page which has an icon in the top, and a table
          > in the middle. The table is populated by calling
          > facade.getAllUsers(), while the icon is updated by server push.
          > Also, the facade method is protected by Spring Security:
          >
          > @RolesAllowed("ROLE_USER_MANAGEMENT")
          > public List<? extends UserSummary> getAllUsers()

          { > ... > }

          >
          > So, in order to call this method, the user might be logged in, and
          > have the ROLE_USER_MANAGEMENT role. This works fine; in fact, if the
          > user didn't have the role, he would not even be able to open that
          > page (because the link that heads to it is protected by
          > enableOnUserRole/renderOnUserRole attribute that you fixed last week).
          >
          > Now, the problem happens when the user is accessing that page (i.e.,
          > it is open in a browser) and a server push happens in the icon. More
          > specifically, an agent sends information to the server, which then
          > updates the icon through server push, and that update triggers a
          > page refresh. When the page is refresh, its backing bean calls
          > facade.getAllUsers() , but the Spring Security context is not set,
          > so it throws a RuntimeException (and then the server push fails to
          > refresh the page).
          >
          > For now, we found an workaround, which is exactly the same trick we
          > used a couple months ago to solve the Spring JSF integration (the
          > one I blogged onhttp://weblogs.java.net/blog/felipeal/archive/2008/03/spring_icefaces.html)
          > .
          > We created a custom phase listener (see attachment), added it to
          > faces-config.xml, and also changed the render() method on our beans
          > to release the context after ICEFaces' render:
          >
          > protected void render()

          { > SessionRenderer.render(ICEFACES_GROUP_NAME); > SpringICEFacesIntegrationPhaseListener.releaseContext(); > }

          > }
          >
          > This 'hack' is fine for now, but it would be nice if ICEFaces took
          > care of that (as they do now with the previous listener). In fact, I
          > think you guys are half-way there, as the releaseContext() call
          > above is not necessary (the security context's authentication object
          > is already null at the point - see comments in the attached file).

          Show
          Ted Goddard added a comment - > We have an ICEFaces page which has an icon in the top, and a table > in the middle. The table is populated by calling > facade.getAllUsers(), while the icon is updated by server push. > Also, the facade method is protected by Spring Security: > > @RolesAllowed("ROLE_USER_MANAGEMENT") > public List<? extends UserSummary> getAllUsers() { > ... > } > > So, in order to call this method, the user might be logged in, and > have the ROLE_USER_MANAGEMENT role. This works fine; in fact, if the > user didn't have the role, he would not even be able to open that > page (because the link that heads to it is protected by > enableOnUserRole/renderOnUserRole attribute that you fixed last week). > > Now, the problem happens when the user is accessing that page (i.e., > it is open in a browser) and a server push happens in the icon. More > specifically, an agent sends information to the server, which then > updates the icon through server push, and that update triggers a > page refresh. When the page is refresh, its backing bean calls > facade.getAllUsers() , but the Spring Security context is not set, > so it throws a RuntimeException (and then the server push fails to > refresh the page). > > For now, we found an workaround, which is exactly the same trick we > used a couple months ago to solve the Spring JSF integration (the > one I blogged onhttp://weblogs.java.net/blog/felipeal/archive/2008/03/spring_icefaces.html) > . > We created a custom phase listener (see attachment), added it to > faces-config.xml, and also changed the render() method on our beans > to release the context after ICEFaces' render: > > protected void render() { > SessionRenderer.render(ICEFACES_GROUP_NAME); > SpringICEFacesIntegrationPhaseListener.releaseContext(); > } > } > > This 'hack' is fine for now, but it would be nice if ICEFaces took > care of that (as they do now with the previous listener). In fact, I > think you guys are half-way there, as the releaseContext() call > above is not necessary (the security context's authentication object > is already null at the point - see comments in the attached file).
          Hide
          Mircea Toma added a comment -

          Client mentions in his blog that this issue was fixed since version 1.7.2.

          Show
          Mircea Toma added a comment - Client mentions in his blog that this issue was fixed since version 1.7.2.
          Hide
          Mircea Toma added a comment -

          Client said:
          "The blog was about integration between ICEFaces and Spring JSF support (which allows to use Spring Beans as JSF's managed beans, instead of defining the managed beans on faces-config.xml). That integration issue has been fixed on 1.7.2 (i.e., it's not necessary to add a PhaseListener as workaround)."

          Show
          Mircea Toma added a comment - Client said: "The blog was about integration between ICEFaces and Spring JSF support (which allows to use Spring Beans as JSF's managed beans, instead of defining the managed beans on faces-config.xml). That integration issue has been fixed on 1.7.2 (i.e., it's not necessary to add a PhaseListener as workaround)."
          Hide
          Mircea Toma added a comment -

          After running the provided test case with the latest ICEfaces code I can no longer see any security related exceptions. It is true that the page is not updated when the agent is started up but that is because the SessionRenderer.render method is called from a non-JSF thread. Currently that is not possible, see ICE-3843.
          The phase listener, used as a workaround, is invoked on the JSF thread running the lifecycle. The asynchronous render is properly executed because SessionRenderer.render method can then retrieve the necessary information from the thread locals.

          Using an IntervalRenderer will possibly make more sense until we will fix ICE-3843.

          Show
          Mircea Toma added a comment - After running the provided test case with the latest ICEfaces code I can no longer see any security related exceptions. It is true that the page is not updated when the agent is started up but that is because the SessionRenderer.render method is called from a non-JSF thread. Currently that is not possible, see ICE-3843 . The phase listener, used as a workaround, is invoked on the JSF thread running the lifecycle. The asynchronous render is properly executed because SessionRenderer.render method can then retrieve the necessary information from the thread locals. Using an IntervalRenderer will possibly make more sense until we will fix ICE-3843 .
          Hide
          Felipe Leme added a comment -

          Hi Mircea,

          Could you please provide links to documentation that explain how the RenderManager, SessionRenderer and/or IntervalRenderer should be properly used?

          More specifically, is there some sort of utility method we should use to delegate render() to be called by the proper JSF thread (similar to Swing's invokeLater())?

          TIA,

          – Felipe

          Show
          Felipe Leme added a comment - Hi Mircea, Could you please provide links to documentation that explain how the RenderManager, SessionRenderer and/or IntervalRenderer should be properly used? More specifically, is there some sort of utility method we should use to delegate render() to be called by the proper JSF thread (similar to Swing's invokeLater())? TIA, – Felipe
          Hide
          Ted Goddard added a comment -

          The difficulty was that the render groups were being referenced statically, and this turned out not to be valid when multiple .war files are contained in a single .ear (they share ClassLoaders but do not share
          ServletContext objects).

          What are the circumstances for your non-JSF thread invocation of render()? Would the SessionRenderer static singleton scope be enough?

          Show
          Ted Goddard added a comment - The difficulty was that the render groups were being referenced statically, and this turned out not to be valid when multiple .war files are contained in a single .ear (they share ClassLoaders but do not share ServletContext objects). What are the circumstances for your non-JSF thread invocation of render()? Would the SessionRenderer static singleton scope be enough?
          Hide
          Felipe Leme added a comment -

          > The difficulty was that the render groups were being referenced statically,

          So, should I have a reference to the RenderManager instead? I think I could change our code to be something like this:

          @Autowired
          RenderManager renderManager

          protected void render() {
          renderManager.getOnDemandRenderer(ICEFACES_GROUP_NAME).requestRender();
          // or if ICE-3843 is an issue
          // renderManager.getIntervalRenderer(ICEFACES_GROUP_NAME).requestRender();

          Would that be better?

          > and this turned out not to be valid when multiple .war files are contained in a single .ear (they share ClassLoaders but do not share

          I don't think that would matter for us, as there is just one WAR. But I will remove the static reference anyways, just to be safer.

          > What are the circumstances for your non-JSF thread invocation of render()?

          When our server receives a notification from the agent, we call a listener, which in turn is a JSF backing bean. So, that render() method belong to a backing bean, but it is called from a JMX-initiated thread.

          > Would the SessionRenderer static singleton scope be enough?

          I guess it would, for now (as we just have 1 WAR file).

          Show
          Felipe Leme added a comment - > The difficulty was that the render groups were being referenced statically, So, should I have a reference to the RenderManager instead? I think I could change our code to be something like this: @Autowired RenderManager renderManager protected void render() { renderManager.getOnDemandRenderer(ICEFACES_GROUP_NAME).requestRender(); // or if ICE-3843 is an issue // renderManager.getIntervalRenderer(ICEFACES_GROUP_NAME).requestRender(); Would that be better? > and this turned out not to be valid when multiple .war files are contained in a single .ear (they share ClassLoaders but do not share I don't think that would matter for us, as there is just one WAR. But I will remove the static reference anyways, just to be safer. > What are the circumstances for your non-JSF thread invocation of render()? When our server receives a notification from the agent, we call a listener, which in turn is a JSF backing bean. So, that render() method belong to a backing bean, but it is called from a JMX-initiated thread. > Would the SessionRenderer static singleton scope be enough? I guess it would, for now (as we just have 1 WAR file).

            People

            • Assignee:
              Unassigned
              Reporter:
              Ted Goddard
            • Votes:
              1 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: