9 Replies Latest reply on Oct 15, 2008 11:51 PM by shane.bryzak

    Multiple Seam Identities

    cbkihong.cbkihong.hotmail.com

      I am trying to build a webapp with Seam but am apparently faced with a need to create multiple Seam identities. This is because there are at least two types of users logging in with different sets of credentials:



      • Group I: Company ID + password

      • Group II: Company ID + Staff ID + password



      and there is likely more groups added in the future, but I don't have the details yet.


      So, the immediate thought is to create multiple identity components like this:


         <security:identity class="com.xxx.Group1Identity" name="g1Identity" authenticate-method="#{g1_authen.authenticate}" />
      
         <security:identity class="com.xxx.Group2Identity" name="g2Identity" authenticate-method="#{g2_authen.authenticate}" />
      



      The index.jsp, the login page, is just something ordinary. This is an example of login form (simplified) for Group I user:


      Company: <h:inputText value="g1Identity.companyId" />
      Password: <h:inputText value="g1Identity.password" />
      <a4j:commandButton action="#{g1Identity.login}" />
      



      Before I changed the command button action to this, I used the authenticator #{g1_authen.authenticate} directly and things appear to be fine and seeing the Hibernate SQL dumped in the console, but once I changed to the identity.login for action I am no longer able to see those and authentication always failed. I saw:


      
      No authentication method defined - please define authenticate-method for <security:identity/> in components.xml
      
      



      Appears like Seam cannot resolve the authentication method and probably not even the authenticator class at all.


      package com.xxx;
      
      import org.jboss.seam.security.Identity;
      import org.jboss.seam.annotations.*;
      
      @Name(value="g1Identity")
      @Install(false)
      public class Group1Identity extends Identity {
          // Omitted
      }
      
      



      
      @Name("g1_authen")
      @Scope(APPLICATION)
      public class Group1Authenticator {
      
      @In(create=true)
      private EntityManager entityManager;
      
      @In(value="g1Identity")
      private Group1Identity identity;
      
      public boolean authenticate() {
      java.util.List dm = entityManager.createNativeQuery("...").getResultList();
      return (dm.size() > 0);
      }
      
      }
      



      Would anyone lend me a hand? It's Seam 2.0.3 CR1 I believe. Thank you.

        • 1. Re: Multiple Seam Identities

          An option is to have a custom Identity component wrap the Seam Identity (Seam Identity is simply injected at invocation time) and aggregate the potential authentication methods.  Allow the custom component to choose the appropriate Authenticator instance based on the login method invoked.   A User interface can be implemented to hold the specific Group authentication details.  Hope that helps.

          • 2. Re: Multiple Seam Identities
            cbkihong.cbkihong.hotmail.com

            Thanks. I have skimmed through some of the classes in org.jboss.seam.security.* and that seems to suggest Seam Security was not designed for multiple identity components, because many references come from Identity.instance(), that essentially prohibits you from defining multiple classes of "Identity"s.


            Well, I have a few lines of thought:



            1. As you suggested, build an all-knowing Identity subclass that wraps all kinds of groups I ever need to entertain, but by then I need to think about whether that will break the principle of separating "identity" from "authentication mechanism" as originally envisioned by the use of JAAS.

            2. Use the Seam security roles feature to define authorization constraints for actions.

            3. Investigate more on JAAS and see if I can benefit from the JAAS "Subject" and "Principal" interfaces that are exposed by Seam security as well.



            Maybe I will try to build a simple model and let others suggest how I can decouple things a little bit better then.

            • 3. Re: Multiple Seam Identities
              pmuir

              Jacob's approach is correct, there should be a single identity for a user across your system, the fact that you have two groups of users who must authenticate differently should be handled in your authenticator (check the group they are in, then authenticate appropriately).

              • 4. Re: Multiple Seam Identities
                cbkihong.cbkihong.hotmail.com

                Earlier I have made an implementation based on a single identity model, but not sure if that was exactly as you meant. Later on, when I get more time to refactor, I will share what I came up with and invite everyone to comment how to better implement it, because in my opinion it does not look quite pretty, though it works.


                Thanks everyone for your input.

                • 5. Re: Multiple Seam Identities
                  cbkihong.cbkihong.hotmail.com

                  I am faced with a few issues related to Seam security in the same project, so may be I will hijack this thread I created a few weeks ago.


                  So now I have a working implementation of the multiple login type scenario (although the code is ugly IMO and highly confusing, but that's not the topic right now so long the boss does not complain). However, I notice that a success message Welcome, XXXX is always added to the FacesContext, but I don't really want that.


                  On tracing I understand that FacesSecurityEvent in the Seam jar implements a few observers of security events which generate the error and success messages. I understand how to change the message in the message bundle to output my customized version of the message, that works for the authentication failure case, but I would not like the success message being generated altogether rather than just changing the label. However, since it is in the Seam distribution, I can't disable it (I think it may be possible by disabling in components.xml but I'm not sure).


                  So, how can I disable the observer for success message while leaving the one that generates the failure message alone? Or should I extend FacesSecurityEvent overriding the failure observer method and @install at a higher precedence (not sure if this works at all)?


                  I'm asking because I have a <h:messages /> tag in the next screen and the welcome message is showing up there. However, I would like that messages tag be reserved for error messages  with the form in the next screen and the welcome message looks awkward there.


                  In the next screen the action bean needs to check that the two password fields have identical input and generates a FacesMessage if they do not match. So, another question is can the FacesMessage be associated with, say, the panelGrid that wraps the two inputSecret, while the <h:messages /> is given a for that points to that panelGrid, in order to essentially filter the unwanted welcome message away? However, I was unable to get the clientId that is needed by addMessage() in the action bean.


                  Any suggestions as to what I can do with this case?


                  The following gives you some idea of what I have now:


                  <h:messages id="errmsg" />
                  
                  <h:panelGrid id="pwdctls" columns="2">
                  Password <h:inputSecret id="newpass1" value="#{main.password1}" />
                  Password (re-enter) <h:inputSecret id="newpass2" value="#{main.password2}" />
                  </h:panelGrid>
                  
                  <h:commandButton action="#{main.changePassword}" value="Change Password" />
                  



                       public String changePassword() {
                            FacesContext faces = FacesContext.getCurrentInstance();
                            if (! this.password1.equals(this.password2)) {
                                 // append FacesMessage
                                 faces.addMessage(
                                      null, 
                                      new FacesMessage(FacesMessage.SEVERITY_FATAL, "Password entries do not match", null)
                                 );
                                 return null;
                            }
                            // TODO Check not equals previous password
                            entityManager.createNativeQuery("UPDATE accounts_staff SET status = 'A', password = MD5(:password) WHERE company_id = :company AND staff_id = :staffid")
                                 .setParameter("company", companyID)
                                 .setParameter("staffid", staffID)
                                 .setParameter("password", this.getPassword1())
                                 .executeUpdate();
                            return "passwordUpdated";
                       }
                  
                  

                  • 6. Re: Multiple Seam Identities
                    shane.bryzak

                    In response to your first issue, the easiest solution would be to extend FacesSecurityEvents and override the addLoginSuccessfulMessage() method.  As for your second issue, why don't you just put the error message on the password confirmation field (newpass2) and make it a validation error?

                    • 7. Re: Multiple Seam Identities
                      cbkihong.cbkihong.hotmail.com

                      Shane Bryzak wrote on Aug 13, 2008 02:31:


                      In response to your first issue, the easiest solution would be to extend FacesSecurityEvents and override the addLoginSuccessfulMessage() method.  As for your second issue, why don't you just put the error message on the password confirmation field (newpass2) and make it a validation error?


                      Thanks for confirming my thoughts. So, will extending FacesSecurityEvents automatically prevent it from being installed at runtime? Or do I need to disable it separately?


                      Hmm ... just because I read somewhere that validators should not be used for validating business logic as a matter of style, and that JSF resource (may be JSF in Action?) advised those be delegated to action beans and invoking FacesContext.addMessage() from there. Though I understand associating the h:message with any of the two inputSecrets should work technically, I was thinking about whether a more generalized approach is feasible because I'm experimenting with Seam and JSF at an early stage, and certainly I would not like to pollute (or abuse) the glorious architecture of Seam just because I need to get something working ... and I am going to have other validations unstated on the password that really belongs to business logic (say, the new password is different from the existing one in the database) and checking equality is just the first step.


                      But if this is a good choice of handling it I will certainly be pleased to follow that.


                      Right now I have a major part of the site barely working but with most of the needed validations being left out because I do not yet have an idea about how to handle this sort of issues. I think Seam is really a neat way to Java EE development and as always I would like to know if there is a better way to do something. :-)

                      • 8. Re: Multiple Seam Identities
                        sarats

                        Where is the location of FacesSecurityEvent for me to override?  I don't seam to have this in my jboss-seam.jar.  Is this in a newer version than what I have?  I am using 1.2.1 GA.


                        Thanks.

                        • 9. Re: Multiple Seam Identities
                          shane.bryzak

                          In Seam 1.2.1, the authentication-related messages are added directly by the Identity class.