8 Replies Latest reply on Dec 13, 2010 3:03 AM by psramkumar.ps.ramkumar.gmail.com

    help with Identity.login() and acceptExternallyAuthenticatedPrincipal

    hardaur

      I've written an application that authenticates to a remote SSO source.  I'm getting a good Principal back, but can't seem to set up the session correctly.  The code:


      package com.mycom.myapp.session.security;
      
      import java.security.Principal;
      
      import javax.faces.context.FacesContext;
      
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.In;
      import org.jboss.seam.annotations.Install;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      import org.jboss.seam.annotations.Startup;
      import org.jboss.seam.annotations.intercept.BypassInterceptors;
      import org.jboss.seam.contexts.Contexts;
      import org.jboss.seam.log.LogProvider;
      import org.jboss.seam.log.Logging;
      import org.jboss.seam.security.Credentials;
      import org.jboss.seam.security.Identity;
      import static org.jboss.seam.annotations.Install.APPLICATION;
      
      @Name("org.jboss.seam.security.identity") 
      @Scope(ScopeType.SESSION)
      @Install(precedence = APPLICATION) 
      @BypassInterceptors 
      @Startup 
      public class CustomIdentity extends Identity 
      { 
           
           private static final LogProvider log = Logging.getLogProvider(CustomIdentity.class); 
           
           
           @Override
           public String login()
           {
                
                     try 
                     {
                          Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
                          
                          if (principal != null)
                          {
                               acceptExternallyAuthenticatedPrincipal(principal);
                          }
                          
                     }
                     catch (Exception e)
                     {
                          log.error(e,e);
                     }
                
                return "loggedIn";
           }
           
           @Override
           public boolean isLoggedIn()
           {
                if (! super.isLoggedIn())
                     login();
                
                return super.isLoggedIn();
                
           }
      }
      
      



      I AM getting the principal and login does set the session to logged in, but it's not pulling in roles and the user doesn't seem to be getting into the session.  Now, for the roles I figure I'll have to query for the users and add them to the identity manually, no big deal.  However, I'm not sure what I need to do to insure the session knows what user is actually logged in (Identity.getUsername() returns null, but Identity.getPrincipal().getName() is correct).


      Could somebody please shed some light on what I'm missing here.  I'm at the very end of this project and it's getting a little frustrating.


      Thanks!
      Gerald

        • 1. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
          jeanluc

          You haven't posted what security configuration you have. Are you using the JpaSecurityStore? Have you specified which classes represent a user and its roles?

          • 2. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
            hardaur

            Jean,


              Well, the application was originally written to authenticate against its own database using the Seam 2.1.1 identity management.  So yes, I have the following configured:


            <security:identity-manager 
                         identity-store="#{jpaIdentityStore}"
                         role-identity-store="#{jpaIdentityStore}"/>
                    
                    <security:jpa-identity-store 
                      user-class="com.mycom.myapp.entity.UserAccount"
                      role-class="com.mycom.myapp.entity.UserRole"/>
            



            I'm very confused right now as to if these will be used in the new system or not.


            The details of the situation are that I'm now authenticating against Ja-sig CAS SSO and it places a Principal in session that I'm successfully able to retrieve.


            I've tried creating an Authenticator.authenticatorMethod() and extending Identity.login() but having about the same results either way.


            I would be VERY happy to be able to continue to use my JpaSecurityStore data and functionality, but how to I get from having a known-good Principal object with the correct username to completely authenticated and set-up?


            One of the main issues I'm seeing, for instance, is that I'm not populating the authenticatedUser session object.  I look at the JpaIdentityStore.java and see that it does a LOT, but I look at the examples in ch 15 of the seam reference guide and am seeing a major mismatch. 


            I hope I'm making some sense and VERY much appreciate your response, I'm about at wit's end.  It doesn't seem like I'm doing something that should be that difficult so I have to wonder f I'm making it worse than it is. 


            If there is anything else that might help for me to provide, please let me know.


            Thanks again!


            Gerald

            • 3. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
              hardaur

              Another thought in the merry-go-round of desperation ; )


              Would an appropriate strategy be to extend JpaIdentityStore and override:


              public boolean authenticate(String username, String password)



              to basically just ignore the password?  Or is that a direction that'd get me in trouble long-term?


              Thanks!


              Gerald

              • 4. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
                jeanluc

                First look inside Seam's source at what it does during authenticate() and, perhaps more importantly, after. Since it's your code that handles the authentication itself, what matters is not that particular process, but what Seam does with it afterwards. That is, what objects it sets into the session (such as what it puts into the identity object. Remember that at the end of the day Seam adds its own JAAS LoginModule so look inside org.jboss.seam.security.jaas.SeamLoginModule for details. (read up on what JAAS lifecycle first if you're not familiar with it). In particular, look at authenticate(). the part that populates the role after a successful login is quoted below:


                boolean success = identityManager.authenticate(username, identity.getCredentials().getPassword());
                if (success){
                  for (String role : identityManager.getImpliedRoles(username)){
                       identity.addRole(role);
                    }
                }



                Then look in JpaIdentityStore, it shows what it does after authentication. With this 2 pieces and perhaps a little bit more debugging through code (generate a test app with seamgen, set it to use a JPA store and see what it does) you should unveil the mistery :) You may also find useful to listen to the org.jboss.seam.security.management.userAuthenticated event - whether it's appropriate you can tell better.



                   public boolean authenticate(String username, String password) {
                      Object user = lookupUser(username);          
                      if (user == null || (userEnabledProperty.isSet() && ((Boolean) userEnabledProperty.getValue(user) == false))) {
                         return false;
                      }
                      String passwordHash = generatePasswordHash(password, getUserAccountSalt(user)); 
                      boolean success = passwordHash.equals(userPasswordProperty.getValue(user));
                      if (success && Events.exists()){
                         if (Contexts.isEventContextActive()) {
                            Contexts.getEventContext().set(AUTHENTICATED_USER, user);
                         }
                         Events.instance().raiseEvent(EVENT_USER_AUTHENTICATED, user);
                      }
                      return success;
                   }



                • 5. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
                  hardaur

                  Jean,


                    Thanks again, that's the path I started down last night.  Here's my latest and greatest:


                  @Name("org.jboss.seam.security.identity") 
                  @Scope(ScopeType.SESSION)
                  @Install(precedence = APPLICATION) 
                  @BypassInterceptors 
                  @Startup 
                  public class CustomIdentity extends Identity 
                  { 
                       public static final String AUTHENTICATED_USER = "org.jboss.seam.security.management.authenticatedUser";
                       public static final String EVENT_USER_AUTHENTICATED = "org.jboss.seam.security.management.userAuthenticated";
                       private static final String SILENT_LOGIN = "org.jboss.seam.security.silentLogin";
                       
                       @In (create = true)
                       Credentials credentials;
                       
                       private ValueExpression<EntityManager> entityManager; 
                       
                       private static final LogProvider log = Logging.getLogProvider(CustomIdentity.class); 
                       
                       public void initEntityManager()
                       {
                            if (entityManager == null)
                             {
                                entityManager = Expressions.instance().createValueExpression("#{entityManager}", EntityManager.class);
                             }
                       }
                       
                       public EntityManager lookupEntityManager()
                       {
                            return entityManager.getValue();
                       }
                       
                       @Override
                       public String login()
                       {
                            initEntityManager();
                             System.out.println("Starting authentication");
                             
                             if (super.isLoggedIn())
                           {
                              // If authentication has already occurred during this request via a silent login,
                              // and login() is explicitly called then we still want to raise the LOGIN_SUCCESSFUL event,
                              // and then return.
                              if (Contexts.isEventContextActive() && Contexts.getEventContext().isSet(SILENT_LOGIN))
                              {
                                 if (Events.exists()) Events.instance().raiseEvent(EVENT_LOGIN_SUCCESSFUL);
                                 return "loggedIn";            
                              }            
                              
                              if (Events.exists()) Events.instance().raiseEvent(EVENT_ALREADY_LOGGED_IN);
                              return "loggedIn";           
                           }
                             
                             preAuthenticate();
                             
                             Principal casPrincipal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
                            
                            if (casPrincipal.getName() != null)
                             {
                                  String username = casPrincipal.getName();
                                  
                                  System.out.println("Found CAS principal for " + username + ": authenticated");
                                  
                                  acceptExternallyAuthenticatedPrincipal(casPrincipal);
                                  
                                  UserAccount userAccount = (UserAccount) lookupEntityManager().createQuery("from UserAccount where username = :username")
                                      .setParameter("username", username)
                                      .getSingleResult();
                                  
                                  System.out.println("userAccount for " + username + " loaded");
                                  
                                  // Ok, we're authenticated from CAS, let's load up the roles
                                  for (String role : IdentityManager.instance().getImpliedRoles(username))
                                  {
                                       System.out.println("Adding role \"" + role + "\" to " + username);
                                       addRole(role);
                                  }
                                  
                                  if (Events.exists())
                                  {
                                      if (Contexts.isEventContextActive())
                                      {
                                           Contexts.getEventContext().set(AUTHENTICATED_USER, userAccount);
                                      }
                                      
                                      Events.instance().raiseEvent(EVENT_USER_AUTHENTICATED, userAccount);
                                  }
                                  
                                  postAuthenticate();
                                  
                                  return "loggedIn";
                                  
                             }
                            
                            return null;
                       }
                       
                       @Override
                       public boolean isLoggedIn()
                       {
                            if (!super.isLoggedIn())
                            {
                                 login();
                            }
                            
                            return super.isLoggedIn();
                       }
                  }
                  


                  It's not cleaned up or commented yet, but it IS working and seemingly 100%. 


                  If you (or anybody else) wouldn't mind looking at it critically and telling me if you see something that may cause me problems down the road.  If nobody sees a problem I'll write up a quick tutorial for integrating Seam 2.1.x and ja-sig CAS 3 which is completely undocumented anywhere.


                  Thanks again!


                  Gerald

                  • 6. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
                    trind

                    Have you had time to write the tutorial, if so where can i find it ?



                    //Joachim

                    • 7. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
                      digit0815

                      Hi there,


                      as we're struggling with Kerberos SSO ans SEAM too:
                      could you please send the link to that tutorial :)
                      ...in case it already exists..
                      thanks and cheers,
                      ingo

                      • 8. Re: help with Identity.login() and acceptExternallyAuthenticatedPrincipal
                        psramkumar.ps.ramkumar.gmail.com

                        Duplicate component name: org.jboss.seam.security.identity


                        what do i do for this issue


                        i did the same type doesnt work coz of this issue


                        how do we solve this issue


                        thanks