1 2 Previous Next 16 Replies Latest reply on Oct 21, 2009 5:04 PM by ghjboss

    PortalIdentity.getPrincipal() returns null

      Here is what I try to achieve. A user logs into the portal and see some portlets. When the user click on one of the portlets tab, I would like to dynamically add specific user roles for this particular portlet.
      In order to do this, I have a Session scope pojo defined as:

      @Name("someAction ")
      @Scope(ScopeType.SESSION)
      @Startup
      @Install(precedence=Install.APPLICATION, dependencies="org.jboss.seam.security.identity")
      public class someAction implements Serializable {
      
       //@In
       private Identity identity;
      
       @Create
       public void init() {
      
       // Set Identity
       identity = PortalIdentity.instance();
      
       if ( identity == null || identity.getPrincipal() == null ) {
       return;
       }
      
       ...
      }
      


      The init() method is call as expected when the user clicks on the portlet tab.
      Now note that I cannot use @In to inject the Identity object, since I get a null value for some reason...

      PortalIdentity.instance() returns a non null value but unfortunately identity.getPrincipal() is null. The problem comes from the PortalIdentity.isPortletPhase(). This method returns false when FacesContext.getCurrentInstance() returns null which is normal in this case since we are just initializing seam components at this point.

      Now I believe that what I'm doing is legitimate and even if FacesContext.getCurrentInstance() returns null, PortalIdentity should be able to correctly detect that we are initializing seam components of a portlet and return the principal.

      Is this a bug in the portlet bridge?

      A workaround would be to call the init method using an action attached to the page to render. But I don't really like this as I would have to do this for all the portlets.

      Richard


        • 1. Re: PortalIdentity.getPrincipal() returns null
          wesleyhales

          Are you trying to share/inject this Identity object across multiple portlets?

          • 2. Re: PortalIdentity.getPrincipal() returns null

            No, I don't.
            I just want to add some specific roles for the portlet the user just clicked on.

            • 3. Re: PortalIdentity.getPrincipal() returns null

              Each portlet needs to have its own Identity containing a list of roles for that particular user and portlet.
              Now having an identity object per portlet session comes out of the box.
              My problem is that at the time I try to add the roles, FacesContext.getCurrentInstance() is null and PortalIdentity.isPortletPhase() returns false. So I can't access the principal and find out about the user name using Identity.

              • 4. Re: PortalIdentity.getPrincipal() returns null

                Also I notice that PortalIdentity overrides hasRole but not addRole.
                So existing code from a standalone application may still add roles using the Identity object, but these roles are "lost" because when we try to read them back, the method hasRole will not find them when the application is a portlet.

                I believe it would be nice to override addRole to either:
                - Properly add roles to portal (as a complement of hasRole)
                - or throw an UnsupportedOperationException so that existing code will fail, making it easy to know what's going on.

                Richard

                • 5. Re: PortalIdentity.getPrincipal() returns null

                  I looked at the Portlet Bridge code and found out what the problem is.

                  PortalIdenty extends the Seam Identity class and overrides the getPrincipal() methods as below:

                   public Principal getPrincipal() {
                   if (isPortletPhase()) {
                   Principal bridgePrincipal = FacesContext.getCurrentInstance()
                   .getExternalContext().getUserPrincipal();
                   return bridgePrincipal;
                   } else {
                   return super.getPrincipal();
                   }
                   }
                  


                  Thus the PortalIdentity.getPrincipal relies on FacesContext.getCurrentInstance() to be defined (where Identity does not).

                  Now if I have a Session scope component defined as in my first post, when a user click on the portlet tab in the portal, the AjaxPortletBridge.doFacesRequest(RenderRequest request, RenderResponse response) is called. This method calls AjaxPortletBridge.initRequest(PortletRequest request, PortletResponse response, PortletPhase actionPhase)
                  In this step, Seam will instantiate all the session scope component before the bridge propagate the FacesContext to the portlet environment.
                  Therefore, the Seam portlet application has no access to the Principal object even though the user is already logged in.

                  I believe this is a bug since the portlet should have a way to access the principal object at this time.
                  I can think of 2 way to fix this.
                  - The PortalIdentity doesn't depend on FacesContext to retrieve the Principal.
                  - The portlet bridge ensures the Faces environment is setup properly (ie FacesContext.getCurrentInstance() is not null) before Seam instantiates the session scope components.

                  Let me know what you think?

                  Richard



                  • 6. Re: PortalIdentity.getPrincipal() returns null
                    alexsmirnov

                    I'll check PortalIdentity class to proper integration between Seam and portal sequrity, but I don't see a way to manage user roles from portlet because Portlet API does not allows that; only declarative authentication is supported.

                    • 7. Re: PortalIdentity.getPrincipal() returns null

                      Yes regarding the roles, this is clearly not part of the spec.

                      Now regarding FacesContext.getCurrentInstance() being null, I believe this is due to the fact that the Session scoped component are
                      instanciated before the FacesContext is setup.

                      In org.jboss.portletbridge.AjaxPortletBridge, the doFacesRequest() methods calls
                      initRequest(request, response, Bridge.PortletPhase.ActionPhase) where the Session component will be instanciated.

                      Only after this, the code deals with the FacesContext.

                      Maybe windowState.restoreRequest(facesContext, true) needs to be called earlier? (I'm not quite sure this is what need to be fixed as I haven't looked deeper in the code yet).


                      • 8. Re: PortalIdentity.getPrincipal() returns null
                        alexsmirnov

                        I've moved PortalIdentity to core bridge project, so I instantiated by default if Seam framework is presented. In the code snippet that you mentioned, isPortletPhase() checks for FacesContex too, so that should not cause an error.
                        I do not see any case there Seam can call Identity component without FacesContext except Seam Filter that is supposed to check user credentials for all, even non-faces requests. Do you have Seam Filter in your web.xml with /* mapping ?
                        For protlets, it would be even better to remove all JSF stuff, even Faces Servlet from there to block direct access to JSF pages.

                        • 9. Re: PortalIdentity.getPrincipal() returns null

                          I'm not sure what you mean by:

                          it would be even better to remove all JSF stuff, even Faces Servlet


                          I have the following web.xml

                          <?xml version="1.0" encoding="UTF-8"?>
                          <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                           xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
                           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
                           version="2.5">
                           <display-name>uarp</display-name>
                          
                           <session-config>
                           <session-timeout>15</session-timeout>
                           </session-config>
                          
                           <!-- JSF -->
                           <servlet>
                           <servlet-name>Faces Servlet</servlet-name>
                           <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
                           <load-on-startup>1</load-on-startup>
                           </servlet>
                           <servlet-mapping>
                           <servlet-name>Faces Servlet</servlet-name>
                           <url-pattern>/faces/*</url-pattern>
                           </servlet-mapping>
                           <servlet-mapping>
                           <servlet-name>Faces Servlet</servlet-name>
                           <url-pattern>*.seam</url-pattern>
                           </servlet-mapping>
                          
                           <!-- Seam -->
                           <listener>
                           <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
                           </listener>
                           <servlet>
                           <servlet-name>Seam Resource Servlet</servlet-name>
                           <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
                           </servlet>
                           <servlet-mapping>
                           <servlet-name>Seam Resource Servlet</servlet-name>
                           <url-pattern>/seam/resource/*</url-pattern>
                           </servlet-mapping>
                           <filter>
                           <filter-name>Seam Filter</filter-name>
                           <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
                           </filter>
                           <filter-mapping>
                           <filter-name>Seam Filter</filter-name>
                           <servlet-name>Faces Servlet</servlet-name>
                           <dispatcher>FORWARD</dispatcher>
                           <dispatcher>REQUEST</dispatcher>
                           <dispatcher>INCLUDE</dispatcher>
                           </filter-mapping>
                           <filter>
                           <display-name>Ajax4jsf Filter</display-name>
                           <filter-name>ajax4jsf</filter-name>
                           <filter-class>org.ajax4jsf.Filter</filter-class>
                           <init-param>
                           <param-name>createTempFiles</param-name>
                           <param-value>true</param-value>
                           </init-param>
                           </filter>
                           <filter-mapping>
                           <filter-name>ajax4jsf</filter-name>
                           <servlet-name>Faces Servlet</servlet-name>
                           <dispatcher>REQUEST</dispatcher>
                           <dispatcher>FORWARD</dispatcher>
                           <dispatcher>INCLUDE</dispatcher>
                           </filter-mapping>
                          
                           <!-- Parameters -->
                          
                           <!-- Facelets -->
                           <context-param>
                           <param-name>facelets.DEVELOPMENT</param-name>
                           <param-value>false</param-value>
                           </context-param>
                           <context-param>
                           <param-name>facelets.SKIP_COMMENTS</param-name>
                           <param-value>false</param-value>
                           </context-param>
                          
                           <!-- JSF -->
                           <context-param>
                           <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
                           <param-value>.xhtml</param-value>
                           </context-param>
                           <context-param>
                           <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
                           <param-value>server</param-value>
                           </context-param>
                           <context-param>
                           <param-name>javax.portlet.faces.renderPolicy</param-name>
                           <param-value>ALWAYS_DELEGATE</param-value>
                           </context-param>
                          
                           <!-- RichFaces -->
                           <context-param>
                           <param-name>org.richfaces.SKIN</param-name>
                           <param-value>blueSky</param-value>
                           </context-param>
                           <context-param>
                           <param-name>org.richfaces.CONTROL_SKINNING</param-name>
                           <param-value>enable</param-value>
                           </context-param>
                           <context-param>
                           <param-name>org.richfaces.LoadStyleStrategy</param-name>
                           <param-value>DEFAULT</param-value>
                           </context-param>
                           <context-param>
                           <param-name>org.richfaces.LoadScriptStrategy</param-name>
                           <param-value>NONE</param-value>
                           </context-param>
                           <context-param>
                           <param-name>org.ajax4jsf.RESOURCE_URI_PREFIX</param-name>
                           <param-value>rfRes</param-value>
                           </context-param>
                           <context-param>
                           <param-name>org.ajax4jsf.COMPRESS_SCRIPT</param-name>
                           <param-value>false</param-value>
                           </context-param>
                           <context-param>
                           <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
                           <param-value>org.jboss.portletbridge.application.FaceletPortletViewHandler</param-value>
                           </context-param>
                          
                           <!-- PortletBridge -->
                           <context-param>
                           <param-name>org.jboss.portletbridge.ExceptionHandler</param-name>
                           <param-value>org.jboss.portletbridge.SeamExceptionHandlerImpl</param-value>
                           </context-param>
                          
                           <!-- The documentation says it is not needed, but it is! -->
                           <context-param>
                           <param-name>javax.faces.LIFECYCLE_ID</param-name>
                           <param-value>SEAM_PORTLET</param-value>
                           </context-param>
                          
                          </web-app>
                          


                          When a component is scoped session and has the @Startup annotation, Seam will instantiate such a component only when the user click on the portal tab for the given portlet. If this component tries to access PortalIdentity, FacesContext should be defined since it is a faces resquest, but it is not (therefore the problem).

                          • 10. Re: PortalIdentity.getPrincipal() returns null

                            I got 1.0.0.SNAPSHOT but it doesn't seem your changes made it in yet. Is that correct?

                            • 13. Re: PortalIdentity.getPrincipal() returns null

                              This build didn't fix my problem and after reading again the previous posts, I believe we have a little misunderstanding :)

                              I have a Session scoped bean annotated @Startup (see first post)
                              The flow is the following:
                              - A user logs into the portal. Then MyApp portlet tab appears on the portal page.
                              - The user clicks on the portlet tab (at this point all the seam session scoped bean are instantiated when the AjaxPortletBridge.doFacesRequest(RenderRequest request, RenderResponse response) is called.

                              More precisely the following code is ran when the user clicks on the portlet tab:

                              public void doFacesRequest(RenderRequest request, RenderResponse response) throws BridgeException {
                              ...
                              // Amongs other things, create the Seam Session Scoped beans
                              PortletBridgeContext bridgeContext = initRequest(request, wrappedResponse, Bridge.PortletPhase.RENDER_PHASE);
                              ...
                              // Instantiate the facesContext
                              FacesContext facesContext = createFacesContext(request, wrappedResponse);
                              ...
                              }
                              


                              And

                              PortletBridgeContext initRequest(PortletRequest request, PortletResponse response, PortletPhase currentPhase) throws BridgeException {
                              ...
                              // Create the Seam Session Scoped beans
                              PortletSession portletSession = request.getPortletSession();
                              ...
                              }
                              


                              We can see that when the session scoped components are created, FacesContext hasn't been created yet.

                              Thus in my Session Scope bean @Create method, PortalIdentity.getPrincipal() returns null because PortalIdentity.isPortletPhase() return false.
                              So I can't rely on PortalIdentity to get the principal in the @Create method of the Session Scope bean with the current implementation of the portlet bridge.
                              At the time the bean is instantiated, the user is logged in already, so I believe the principal should be available at the portlet level through PortalIdentity.
                              The problem is that the PortalIdentity relies on FacesContext to retrieve the principal.
                              Either PortalIdentity should not get the Principal from the FacesContext or if it should then the PortelBridge should make sure the FacesContext is created before instantiating the Session scoped components.

                              In another end, in the @Create method of my bean, I can also use another way to retrieve the principal. How would I get access to it without using PortalIdentity.getPrincipal()?





                              • 14. Re: PortalIdentity.getPrincipal() returns null
                                wesleyhales

                                can you use something like:

                                @Observer(PortalIdentity.EVENT_LOGIN_SUCCESSFUL)
                                 public void addLoginSuccessfulMessage()
                                 {
                                 //do your role magic here
                                 }
                                


                                If not, why are you trying to get the principal in the @Create method - I mean at that specific point in processing? I understand the UC, and there are a few different ways to do this.

                                Also, you should compare your web.xml to this reference (http://anonsvn.jboss.org/repos/portletbridge/trunk/examples/seam/booking/seamBookingPortlet/src/main/webapp/WEB-INF/web.xml) Seam automatically does the setup for your Ajax4JSf filters, so you don't need that config.



                                1 2 Previous Next