11 Replies Latest reply on Jan 24, 2008 4:45 AM by terryb

    authenticator.authenticate called several times

    terryb

      Since upgrading to seam 2.0.0GA, my authenticator.authenticate is invoked several times rather than just once. There are no error. wondering if anyone else had similar problem or got any clues?


      login.xhtml
       <h:form id="login" onsubmit="return validateForm(document.forms['login'])">
       <div class="dialogtable">
       <rich:separator height="1"/>
      
       <h:panelGrid columns="2" rowClasses="prop" columnClasses="name,value">
      
       <h:outputLabel for="username">Username<span class="required">*</span></h:outputLabel>
       <h:inputText id="username" value="#{identity.username}" required="true" requiredMessage="Username is requierd." size="20"></h:inputText>
      
       <h:outputLabel for="password">Password<span class="required">*</span></h:outputLabel>
       <h:inputSecret id="password" value="#{identity.password}" required="true" requiredMessage="Password is requierd." size="20"></h:inputSecret>
      
       <h:outputLabel for="rememberMe">Remember me</h:outputLabel>
       <h:selectBooleanCheckbox id="rememberMe" value="#{identity.rememberMe}"/>
      
       </h:panelGrid>
       </div>
       <rich:separator height="1"/>
       <div class="actionButtons">
       <h:commandButton styleClass="button" value="Login" action="#{identity.login}"/>
       <s:button styleClass="button" value="Cancel" view="/home.xhtml"/>
       </div>
       </h:form>
      
      
      --------------------------------------------------------------------
      login.page.xml
      <!DOCTYPE page PUBLIC
       "-//JBoss/Seam Pages Configuration DTD 2.0//EN"
       "http://jboss.com/products/seam/pages-2.0.dtd">
      
      <page conversation-required="false">
       <navigation from-action="#{identity.login}">
      
       <rule if="#{not identity.loggedIn and orgUserAuthenticate.systemError}">
       <redirect view-id="/error.xhtml"/>
       </rule>
      
       <rule if="#{not identity.loggedIn and orgUserAuthenticate.accountSuspended}">
       <redirect view-id="/account-suspended.xhtml"/>
       </rule>
      
       <rule if="#{not identity.loggedIn and orgUserAuthenticate.accountLocked}">
       <redirect view-id="/account-locked.xhtml"/>
       </rule>
      
       <rule if="#{identity.loggedIn}">
       <redirect view-id="/member/welcome.xhtml"/>
       </rule>
       </navigation>
      </page>
      
      --------------------------------------------------------------------
      
      
      Authenticator.java
      @Name("authenticator")
      public class Authenticator {
       @Logger
       Log log;
      
       @In
       Identity identity;
      
       @In(value = "orgUserAuthenticate", required = false, create = true)
       private OrgUserAuthenticate orgUserAuthenticate;
      
       @In(value = "orgUserAuthenticated", required = false)
       private OrgUserAuthenticated orgUserAuthenticated;
      
       @In(value = "activityLogger", required = false, create = true)
       private ActivityLogger activityLog;
      
       public boolean authenticate() {
      
       log.info("INFO: authenticating #0", identity.getUsername());
       return orgUserAuthenticate.authenticate();
       }
      
      
      
      
      


        • 1. Re: authenticator.authenticate called several times
          msystems

          Yes, I have the same problem !

          When authenticate fails it is invoked two times - must be a bug?
          When authenticate is successful it is invoked one time.

          • 2. Re: authenticator.authenticate called several times
            terryb

            in my case, when authenticate fails it is invoked 3 times; and when authenticate is successful it is invokved 2 times.

            it seems like a bug, hopefully we get some reply on this soon.

            • 3. Re: authenticator.authenticate called several times
              nickarls

              There has been some threads on this and it's clarified in the 2.0.1CR1 manual:

              13.4.2.2. Special Considerations

              When writing an authenticator method, it is important that it is kept minimal and free from any side-effects. This is because there is no guarantee as to how many times the authenticator method will be called by the security API, and as such it may be invoked multiple times during a single request. Because of this, any special code that should execute upon a successful or failed authentication should be written by implementing an event observer. See the section on Security Events further down in this chapter for more information about which events are raised by Seam Security.

              • 4. Re: authenticator.authenticate called several times

                Hi!

                I had same "issue" some time ago.
                Have a look at the following thread in this forum:

                http://www.jboss.com/index.html?module=bb&op=viewtopic&t=122033

                Cheers, K.

                • 5. Re: authenticator.authenticate called several times
                  terryb

                  Thanks you, I have upgraded seam 2.0.1CR1 and implementing security event approach to handle login pre/post processes.

                  However, it appears that in my case identity.logout is not raising loggedOut event?

                  I don't think I am supposed to do anything other than the code below to raise/capture events.

                  X.xhtml
                  ...
                  <s:link view="/home.xhtml" action="#{identity.logout}" value="Logout" rendered="#{identity.loggedIn}"/>
                  ...
                  
                  
                  Authenticator.java
                  
                  ..
                  import org.jboss.seam.annotations.Observer;
                  ...
                  ...
                  
                  @Observer("org.jboss.seam.security.loggedOut")
                  public void logout() {
                   activityLog.logOrgUser(orgUserAuthenticated.getUser(), ActivityLogger.Code.LOGOUT, null);
                  }
                  
                  
                  


                  • 6. Re: authenticator.authenticate called several times
                    nickarls

                    How about org.jboss.seam.loggedOut?

                    • 7. Re: authenticator.authenticate called several times
                      pmuir

                      It certainly should do. You can try placing a breakpoint on Identity.logout() and see if the event is raised correctly there.

                      • 8. Re: authenticator.authenticate called several times
                        terryb

                        I tried org.jboss.seam.loggedOut and org.jboss.seam.security.loggedOut and also @Observer(Identity.EVENT_LOGGED_OUT) but no success.

                        Pete, I used debugger and found that Identity.logout() does attempt to raise event but there is no event to be raise. Event.raiseEvent() method shows List variable 'observers' being null.

                        Seems for some reason my @Observer(Identity.EVENT_LOGGED_OUT) is being ignored?

                        Seam Events.java
                         public void raiseEvent(String type, Object... parameters)
                         {
                        ...
                        List<Init.ObserverMethod> observers = Init.instance().getObserverMethods(type);
                         List<Init.ObserverMethod> observers = Init.instance().getObserverMethods(type);
                         if (observers!=null)
                         {
                        ...
                        
                         }
                        }
                        
                        
                        My Authenticator.java
                        @Observer(Identity.EVENT_LOGGED_OUT)
                        public void logout() {
                         activityLog.logOrgUser(orgUserAuthenticated.getUser(), ActivityLogger.Code.LOGOUT, null);
                        }
                        
                        
                        note that in my case RuleBasedIdentity.logout() is invoked first then Identity.logout().


                        • 9. Re: authenticator.authenticate called several times
                          pmuir

                          Lets see the whole class MyAuthenticator

                          • 10. Re: authenticator.authenticate called several times
                            terryb

                            I have removed some code for clarity. also this will change further as I use other Identity events.

                            MyAuthenticator.java
                            --------------------
                            
                            package au.edu.tisc.session;
                            
                            import org.jboss.seam.annotations.In;
                            import org.jboss.seam.annotations.Logger;
                            import org.jboss.seam.annotations.Name;
                            import org.jboss.seam.annotations.Observer;
                            import org.jboss.seam.log.Log;
                            import org.jboss.seam.security.Identity;
                            
                            import au.edu.tisc.exception.ActivityLoggerException;
                            
                            @Name("authenticator")
                            public class Authenticator {
                             @Logger
                             Log log;
                            
                             @In
                             Identity identity;
                            
                             @In(value = "orgUserAuthenticate", required = false, create = true)
                             private OrgUserAuthenticate orgUserAuthenticate;
                            
                             @In(value = "orgUserAuthenticated", required = false)
                             private OrgUserAuthenticated orgUserAuthenticated;
                            
                             @In(value = "activityLogger", required = false, create = true)
                             private ActivityLogger activityLog;
                            
                             public boolean authenticate() {
                            
                             log.info("INFO: authenticating #0", identity.getUsername());
                             return orgUserAuthenticate.authenticate();
                             }
                            
                             @Observer(Identity.EVENT_LOGGED_OUT)
                             public void logout() {
                             try {
                             activityLog.logOrgUser(orgUserAuthenticated.getUser(), ActivityLogger.Code.LOGOUT, null);
                             } catch (ActivityLoggerException e) {
                             //do nothing
                             }
                             }
                            }
                            
                            
                            ------------------------------------------------------------------------------------------------
                            package au.edu.tisc.session;
                            
                            import java.util.Calendar;
                            import java.util.List;
                            
                            import javax.faces.application.FacesMessage;
                            
                            import org.jboss.seam.Component;
                            import org.jboss.seam.ScopeType;
                            import org.jboss.seam.annotations.In;
                            import org.jboss.seam.annotations.Logger;
                            import org.jboss.seam.annotations.Name;
                            import org.jboss.seam.annotations.Observer;
                            import org.jboss.seam.annotations.Out;
                            import org.jboss.seam.faces.FacesMessages;
                            import org.jboss.seam.log.Log;
                            import org.jboss.seam.security.Identity;
                            
                            import au.edu.tisc.entity.OrganisationUser;
                            import au.edu.tisc.exception.ActivityLoggerException;
                            import au.edu.tisc.home.OrganisationUserHome;
                            import au.edu.tisc.util.JCrypt;
                            import au.edu.tisc.util.Strings;
                            
                            @Name("orgUserAuthenticate")
                            public class OrgUserAuthenticate {
                            
                             //TODO auto unlock check, change to configuration parameter
                             boolean autoUnlock = true;
                            
                             @Logger
                             Log log;
                            
                             @In
                             Identity identity;
                            
                             @In(value = "activityLogger", required = false, create = true)
                             private ActivityLogger activityLog;
                            
                             @In(value="orgUserService", required=false, create=true)
                             private OrgUserService orgUserService;
                            
                             @In(value = "orgUserAuthenticated", required = false, create = true)
                             @Out(value = "orgUserAuthenticated", required = false, scope = ScopeType.SESSION)
                             private OrgUserAuthenticated orgUserAuthenticated;
                            
                             OrganisationUser organisationUser = null;
                            
                             private boolean isAutoLocked = false;
                             private boolean isAccountLocked = false;
                             private boolean isAccountSuspended = false;
                             private boolean isSystemError = false;
                            
                             public boolean authenticate() {
                            
                             boolean isAuthenticated = false;
                             try {
                             isAuthenticated = _authenticate();
                             } catch (ActivityLoggerException e) {
                            
                             this.isSystemError = true;
                             FacesMessages.instance().getCurrentMessages().clear();
                             FacesMessages.instance().addFromResourceBundle(FacesMessage.SEVERITY_ERROR, "au.edu.tisc.SystemErrorWhileLoggingIn", e.getMessage());
                             } finally {
                             ...
                             }
                            
                             return isAuthenticated;
                             }
                            
                             private boolean _authenticate() {
                            
                             if (Strings.isNull(identity.getUsername()) || Strings.isNull(identity.getPassword())) {
                            
                             FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Please enter username and password.");
                             return false;
                             }
                            
                             //validate username
                             if (organisationUser == null) {
                            
                             activityLog.logOrgUser(organisationUser, ActivityLogger.Code.LOGIN_FAILED, String.format(
                             ActivityLogger.Code.Desc.INVALID_USERNAME, identity.getUsername()));
                            
                             FacesMessages.instance().addFromResourceBundle(FacesMessage.SEVERITY_ERROR, "au.edu.tisc.InvalidUsername", identity.getUsername());
                             return false;
                             } else {
                             this.isAccountLocked = (Constant.User.AccountStatus.LOCKED.equalsIgnoreCase(organisationUser.getStatus()));
                             this.isAccountSuspended = (Constant.User.AccountStatus.SUSPENDED.equalsIgnoreCase(organisationUser.getStatus()));
                             }
                            
                             //validate password
                             if (!JCrypt.match(identity.getPassword(), organisationUser.getPassword())) {
                            
                             ...
                             return false;
                             }
                             ...
                             ...
                             orgUserService.loadSecurityRolesForUser(organisationUser);
                            
                             return true;
                             }
                            
                             public boolean isAccountLocked() {
                             return this.isAccountLocked;
                             }
                            
                             public boolean isAccountSuspended() {
                             return this.isAccountSuspended;
                             }
                            
                             public boolean isSystemError() {
                             return this.isSystemError;
                             }
                            
                             public void setIsAutoLocked(boolean value) {
                             this.isAutoLocked = value;
                             }
                            
                             public void setIsSystemError(boolean value) {
                             this.isSystemError = value;
                             }
                            
                            
                             @Observer(Identity.EVENT_LOGIN_SUCCESSFUL)
                             public void loginSuccessful() {
                            
                             log.info("************ loginSuccessful");
                            
                             if (orgUserService.setLoginSuccessParams(organisationUser)) {
                            
                             orgUserAuthenticated.setUser(organisationUser);
                            
                             try {
                             activityLog.logOrgUser(organisationUser, ActivityLogger.Code.LOGIN_SUCCESSFUL);
                             } catch (ActivityLoggerException e) {
                            
                             identity.logout();
                             this.isSystemError = true;
                             FacesMessages.instance().getCurrentMessages().clear();
                             FacesMessages.instance().addFromResourceBundle(FacesMessage.SEVERITY_ERROR, "au.edu.tisc.SystemErrorWhileLoggingIn", e.getMessage());
                             }
                            
                             } else {
                             identity.logout();
                             this.isSystemError = true;
                             FacesMessages.instance().addFromResourceBundleOrDefault(FacesMessage.SEVERITY_ERROR,"au.edu.tisc.SystemErrorWhileLoggingIn", "Unable to set user login success parameters.", "default msgs");
                             List<FacesMessage> msgs = FacesMessages.instance().getCurrentMessages();
                             log.info(msgs.size());
                             }
                             }
                            
                            }
                            


                            • 11. Re: authenticator.authenticate called several times
                              terryb

                              Pete, I have resolved this issue now by implementing Identity based approach. For some reason, two posts have gone missing from this thread...