6 Replies Latest reply on Mar 11, 2009 3:56 AM by joblini

    User's identity switched

      We are experiencing a problem of identity switching that occurs only occationally and for no apparent reason. A user after logging in is suddenly switched to the identity of another user already logged in. As you can imagine this is a big security problem. I have traced the login actions and do not see anything obvious that could be causing the problem.


      Has anyone else seen this problem? What caused it? What did you do the fix it?


      THANKS

        • 1. Re: User's identity switched
          swd847

          Can you post your authenticator bean?

          • 2. Re: User's identity switched
            Authenticate method: Do you need more

            public boolean authenticate() {
                    // First login will start things off, this should be late enough in the deployment process.
                    setupApplicationContext();
                    facesMessages.clear();

                    String userAgent = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap().get("user-agent");
                    String addr = ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getRemoteAddr();

                    identity.setUsername(identity.getUsername().trim());
                    identity.setPassword(identity.getPassword().trim());

                    log.info("Attempting to authenticate user #0:#1 [#2]", identity.getUsername(), addr, userAgent);
                    List results = em.createQuery(userAuthenticationQuery).getResultList();

                    if ( results.size()==0 ){
                         results = em.createQuery(temporaryUserAuthenticationQuery).getResultList();
                         if( results.size() == 0){
                              log.info("User #0:#1 failed to authenticate." , identity.getUsername(), addr);
                              invalidateSession();
                              return false;
                         }
                         else{
                              user = (Contact) results.get(0);
                              Calendar calendar = Calendar.getInstance();
                              calendar.setTime(user.getTemporaryPasswordCreated());
                              calendar.add(Calendar.MINUTE, ApplicationProperties.getInteger(ConfigurationVariableName.TempPasswordExpInMinutes));
                              if(calendar.getTime().before(new Date())){
                                   log.warn("User #0:#1 authenticated with expired temporary password.", identity.getUsername(), addr);
                                   //TODO: Figure out why this message appears twice in the UI.
                                   facesMessages.add("Password has expired.");
                                   invalidateSession();
                                   return false;
                              }
                              log.warn("User #0:#1 authenticated with temporary password.", identity.getUsername(), addr);
                              user.setTemporaryPassword(null);
                              user.setTemporaryPasswordCreated(null);
                              user.setChangePasswordNextLogin(Boolean.TRUE);
                         }
                    }
                    else{
                         user = (Contact) results.get(0);
                    }

                    if(user.getPassword() == null || user.getPassword().length() == 0) {
                         return false;
                    }
                    if(user.getNotAllowedToLogin()){
                         log.info("User #0 account deactivated.", identity.getUsername());
                         facesMessages.add("Your account has been deactivated.  Please contact us at (877) 631-9080 option 1.");
                         invalidateSession();
                         return false;
                    }
                    //if we get this far, user is authenticated...
                    log.info("User #0:#1 authenticated.", identity.getUsername(), addr);
                    user.setLastLogin(new Date());

                    if(user.getTemporaryPassword() != null){
                         log.warn("User #0:#1 authenticated normally, but has a temporary password!", identity.getUsername(), addr);
                         user.setTemporaryPassword(null);
                         user.setTemporaryPasswordCreated(null);
                    }

                    String registrationKey = (String) Contexts.getEventContext().get("reg_key");
                    int registrationId = -1;
                    try{
                         String registrationIdAsString = (String) Contexts.getEventContext().get("uid");
                         registrationId = Integer.parseInt(registrationIdAsString);
                    }
                    catch(NumberFormatException err){
                         //ignore for now - might log...
                    }

                    if(registrationKey != null
                      && registrationKey.equals(user.getRegistrationKey())
                      && registrationId == user.getId()){
                         log.info("User #0 completed registration.", identity.getUsername());
                         user.setRegistrationKey(null);
                         //user.setChangePasswordNextLogin(true);
                    }

                    for(String s : user.getApplicationRoles()){
                         identity.addRole(s);
                    }
                    editAccount = "true".equals(Contexts.getEventContext().get("editAccount"));
                    actor.setId(identity.getUsername());
                    actor.getGroupActorIds().clear();
                    actor.getGroupActorIds().addAll(user.getApplicationRoles());

                    em.persist(user);

                    return true;
               }
            • 3. Re: User's identity switched
              swd847

              Can you post the whole bean?

              • 4. Re: User's identity switched
                package com.assistgroup.sessionbeans;

                import static org.jboss.seam.ScopeType.SESSION;

                import java.security.SecureRandom;
                import java.util.Calendar;
                import java.util.Date;
                import java.util.List;
                import java.util.regex.Pattern;

                import javax.ejb.Stateless;
                import javax.faces.context.FacesContext;
                import javax.persistence.EntityManager;
                import javax.persistence.PersistenceContext;
                import javax.servlet.http.HttpServletRequest;

                import org.jboss.seam.annotations.In;
                import org.jboss.seam.annotations.Logger;
                import org.jboss.seam.annotations.Name;
                import org.jboss.seam.annotations.Out;
                import org.jboss.seam.bpm.Actor;
                import org.jboss.seam.contexts.Contexts;
                import org.jboss.seam.faces.FacesMessages;
                import org.jboss.seam.log.Log;
                import org.jboss.seam.security.Identity;
                import org.jbpm.mail.jBPMMailReplacement;

                import com.assistgroup.entitybeans.Company;
                import com.assistgroup.entitybeans.Contact;
                import com.assistgroup.entitybeans.ConfigurationVariable.ConfigurationVariableName;
                import com.assistgroup.support.ApplicationProperties;

                @Stateless
                @Name("authenticator")
                public class AuthenticatorAction implements Authenticator {

                     private static final int RANDOM_STRING_LENGTH_UPPER_LIMIT = 10;
                     private static final int RANDOM_STRING_LENGTH_LOWER_LIMIT = 8;
                     private static final Pattern allowedCharacters = Pattern.compile("[a-zA-Z0-9]");

                        private static final String userAuthenticationQuery =
                             "select " +
                             "     distinct c " +
                             "from " +
                             "     Contact c  " +
                             "     left join fetch c.emails " +
                             "     left join fetch c.company " +
                             "     left join fetch c.company.companyType " +
                             "    left join fetch c.company.defaultReferral " +
                             "    left join fetch c.address " +
                             "    left join fetch c.phones " +
                             "where " +
                             "    trim(lower(#{identity.username})) in (select trim(lower(ec.emailAddress)) from c.emails ec) " +
                             "    and c.password=md5(#{identity.password})";

                        private static final String temporaryUserAuthenticationQuery =
                             "select " +
                             "     distinct c " +
                             "from " +
                             "     Contact c  " +
                             "     left join fetch c.emails " +
                             "     left join fetch c.company " +
                             "     left join fetch c.company.companyType " +
                             "    left join fetch c.company.defaultReferral " +
                             "    left join fetch c.address " +
                             "    left join fetch c.phones " +
                             "where " +
                             "    trim(lower(#{identity.username})) in (select trim(lower(ec.emailAddress)) from c.emails ec) " +
                             "    and c.temporaryPassword=md5(#{identity.password})";

                          protected static final String userByEmailQuery =
                               "select " +
                               "     distinct c " +
                               "from " +
                               "     Contact c  " +
                               "     left join fetch c.emails " +
                               "where " +
                               "    trim(lower(#{identity.username})) in (select trim(lower(ec.emailAddress)) from c.emails ec) ";

                          private static final String companyByDomainQuery =
                               "select " +
                               "   c " +
                               "from " +
                               "     Company c  " +
                               "where " +
                               "    trim(lower(c.emailDomain)) = trim(lower(:domain)) ";

                          @Logger Log log;
                          @In Identity identity;
                          @PersistenceContext EntityManager em;
                          @In private FacesMessages facesMessages;
                          // @In(create=true) StartupManager startupManager;

                          // Outjected in method
                          private Contact user;

                          private boolean editAccount = false;

                          @In Actor actor;


                   public boolean authenticate() {
                        // First login will start things off, this should be late enough in the deployment process.
                        setupApplicationContext();
                        facesMessages.clear();

                        String userAgent = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap().get("user-agent");
                        String addr = ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getRemoteAddr();

                        identity.setUsername(identity.getUsername().trim());
                        identity.setPassword(identity.getPassword().trim());

                        log.info("Attempting to authenticate user #0:#1 [#2]", identity.getUsername(), addr, userAgent);
                        List results = em.createQuery(userAuthenticationQuery).getResultList();

                        if ( results.size()==0 ){
                             results = em.createQuery(temporaryUserAuthenticationQuery).getResultList();
                             if( results.size() == 0){
                                  log.info("User #0:#1 failed to authenticate." , identity.getUsername(), addr);
                                  invalidateSession();
                                  return false;
                             }
                             else{
                                  user = (Contact) results.get(0);
                                  Calendar calendar = Calendar.getInstance();
                                  calendar.setTime(user.getTemporaryPasswordCreated());
                                  calendar.add(Calendar.MINUTE, ApplicationProperties.getInteger(ConfigurationVariableName.TempPasswordExpInMinutes));
                                  if(calendar.getTime().before(new Date())){
                                       log.warn("User #0:#1 authenticated with expired temporary password.", identity.getUsername(), addr);
                                       //TODO: Figure out why this message appears twice in the UI.
                                       facesMessages.add("Password has expired.");
                                       invalidateSession();
                                       return false;
                                  }
                                  log.warn("User #0:#1 authenticated with temporary password.", identity.getUsername(), addr);
                                  user.setTemporaryPassword(null);
                                  user.setTemporaryPasswordCreated(null);
                                  user.setChangePasswordNextLogin(Boolean.TRUE);
                             }
                        }
                        else{
                             user = (Contact) results.get(0);
                        }

                        if(user.getPassword() == null || user.getPassword().length() == 0) {
                             return false;
                        }
                        if(user.getNotAllowedToLogin()){
                             log.info("User #0 account deactivated.", identity.getUsername());
                             facesMessages.add("Your account has been deactivated.  Please contact us at (877) 631-9080 option 1.");
                             invalidateSession();
                             return false;
                        }
                        //if we get this far, user is authenticated...
                        log.info("User #0:#1 authenticated.", identity.getUsername(), addr);
                        user.setLastLogin(new Date());

                        if(user.getTemporaryPassword() != null){
                             log.warn("User #0:#1 authenticated normally, but has a temporary password!", identity.getUsername(), addr);
                             user.setTemporaryPassword(null);
                             user.setTemporaryPasswordCreated(null);
                        }

                        String registrationKey = (String) Contexts.getEventContext().get("reg_key");
                        int registrationId = -1;
                        try{
                             String registrationIdAsString = (String) Contexts.getEventContext().get("uid");
                             registrationId = Integer.parseInt(registrationIdAsString);
                        }
                        catch(NumberFormatException err){
                             //ignore for now - might log...
                        }

                        if(registrationKey != null
                          && registrationKey.equals(user.getRegistrationKey())
                          && registrationId == user.getId()){
                             log.info("User #0 completed registration.", identity.getUsername());
                             user.setRegistrationKey(null);
                             //user.setChangePasswordNextLogin(true);
                        }

                        for(String s : user.getApplicationRoles()){
                             identity.addRole(s);
                        }
                        editAccount = "true".equals(Contexts.getEventContext().get("editAccount"));
                        actor.setId(identity.getUsername());
                        actor.getGroupActorIds().clear();
                        actor.getGroupActorIds().addAll(user.getApplicationRoles());

                        em.persist(user);

                        return true;
                   }

                     public void forgotPassword(){
                          setupApplicationContext();
                          Contact forgetfulUser = null;
                          log.info("User " + identity.getUsername() + " requested password.");
                          List results = em.createQuery(userByEmailQuery).getResultList();
                          if ( results.size() == 1 ){
                               forgetfulUser = (Contact) results.get(0);
                          }
                          else{
                               log.info("User " + identity.getUsername() + " requested password - query returned " + results.size() + " results.");
                               facesMessages.add("There is a problem with your account.  Please contact us at (877) 631-9080 option 1.");
                               invalidateSession();
                               return;
                          }

                          if(forgetfulUser != null){
                               if(forgetfulUser.getNotAllowedToLogin()){
                                    log.info("User " + identity.getUsername() + " account deactivated.");
                                    facesMessages.add("Your account has been deactivated.  Please contact us at (877) 631-9080 option 1.");
                                    invalidateSession();
                                    return;
                               }
                               if(forgetfulUser.getPassword() == null || forgetfulUser.getPassword().length() == 0 ){
                                    log.info("User " + identity.getUsername() + " exists, no password.");
                                    facesMessages.add("Please register to use the system.");
                                    invalidateSession();
                                    return;
                               }

                               try{
                                    String temporaryPassword = generateRandomString();
                                    forgetfulUser.setTemporaryPassword(temporaryPassword);
                                    forgetfulUser.setTemporaryPasswordCreated(new Date());
                                    em.persist(forgetfulUser);

                                    //Need to push forgetfulUser into Conversation Context
                                    //so that the email xhtml can use it...
                                    //Contexts.getConversationContext().set("forgetfulUser", forgetfulUser);
                                    Contexts.getEventContext().set("forgetfulUser", forgetfulUser);
                                    Contexts.getEventContext().set("temporaryPassword", temporaryPassword);
                                    Integer temporaryPasswordExpirationInMinutes = ApplicationProperties.getInteger(ConfigurationVariableName.TempPasswordExpInMinutes);
                                    if(temporaryPasswordExpirationInMinutes > 60){
                                         Contexts.getEventContext().set("temporaryPasswordExpiration", temporaryPasswordExpirationInMinutes/60 + " hours");
                                    }
                                    else{
                                         Contexts.getEventContext().set("temporaryPasswordExpiration", temporaryPasswordExpirationInMinutes + " minutes");
                                    }
                                    //Manage dev/test situations...
                                    String testersEmail = ApplicationProperties.getProperty(ConfigurationVariableName.TesterEmail);
                                    if(testersEmail != null){
                                         Contexts.getEventContext().set("testersEmail", testersEmail);
                                    }
                                    jBPMMailReplacement forgottenPasswordEmailNotification =
                                         new jBPMMailReplacement("/emailTemplates/ForgottenPasswordEmail.xhtml", null, forgetfulUser.getEmailsAsCsv(), null, null);
                                    forgottenPasswordEmailNotification.send();
                                    facesMessages.add("A temporary password has been emailed to you.");
                                    log.info("User " + identity.getUsername() + " password sent.");
                               }
                               catch (Exception e) {
                                    log.error("Error sending mail - " + identity.getUsername(), e);
                                    facesMessages.add("Email sending failed: " + e.getMessage());
                               }

                          }
                          else{
                               log.info("User " + identity.getUsername() + " not in system.");
                               facesMessages.add("We have no registration for that email address. Please register to use the system.");
                          }
                          invalidateSession();
                     }

                     public String registerContact(){
                          setupApplicationContext();
                          Contact alreadyExistingUser = null;
                          if(identity.getUsername() == null || identity.getUsername().length() == 0)
                          {
                               facesMessages.add("Please enter your email address to begin the registration process.");
                               invalidateSession();
                               return null;
                          }

                          List results = em.createQuery(userByEmailQuery).getResultList();
                          if(results.size() > 0){
                               alreadyExistingUser = (Contact) results.get(0);
                          }

                          if(alreadyExistingUser != null){
                               if(alreadyExistingUser.getNotAllowedToLogin()){
                                    log.info("User " + identity.getUsername() + " account deactivated.");
                                    facesMessages.add("Your account has been deactivated.  Please contact us at (877) 631-9080 option 1.");
                                    invalidateSession();
                                    return null;
                               }
                               if(alreadyExistingUser.getPassword() != null && alreadyExistingUser.getPassword().length() != 0){
                                    log.info("User " + identity.getUsername() + " attempting to re-register.");
                                    facesMessages.add("That email address is already registered.  Click on Forgot Password if you need password help.");
                                    facesMessages.add("Contact us at (877) 631-9080 option 1 if you continue to have difficulties logging in.");
                                    invalidateSession();
                                    return null;
                               }

                          }

                          //Figure out domain...
                          String registrantsEmail = Identity.instance().getUsername();
                          log.info("Registrants email is " +registrantsEmail);
                          int indexOfAt = registrantsEmail.indexOf('@');
                          if(indexOfAt <= 0){
                               log.info("User " + identity.getUsername() + " - invalid email address.");
                               facesMessages.add("The entered email address does not appear to be valid. Please check the email address.");
                               facesMessages.add("Contact us at (877) 631-9080 option 1 if you continue to have difficulties logging in or registering.");
                               invalidateSession();
                               return null;
                          }
                          List companies = null;
                          if(alreadyExistingUser == null || alreadyExistingUser.getCompany() == null){
                               String registrantsDomain = registrantsEmail.substring(indexOfAt + 1);
                               companies = em.createQuery(companyByDomainQuery).setParameter("domain", registrantsDomain).getResultList();
                               if(companies.size() == 0){
                                    log.info("User " + identity.getUsername() + " company is not in system.");
                                    return "newClient";
                               }
                          }

                          Contact registerContact;
                          if(alreadyExistingUser == null){
                               registerContact = new Contact();
                               registerContact.setEmail(registrantsEmail);
                               if(companies != null && companies.size() > 0){
                                    registerContact.setCompany((Company)companies.get(0));
                               }
                          }
                          else{
                               registerContact = alreadyExistingUser;
                          }

                          setupContactForRegistration(registerContact);
                          em.persist(registerContact);

                          try{
                               jBPMMailReplacement registerEmailNotification =
                                    new jBPMMailReplacement("/emailTemplates/RegistrationEmail.xhtml", null, registerContact.getEmailsAsCsv(), null, null);
                               registerEmailNotification.send();
                               facesMessages.add("An email with instructions for completing the registration process has been sent to your e-mail address.");
                               facesMessages.add("If you have any difficulties continuing with the registration process please contact us at (877) 631-9080 option 1.");
                               log.info("User " + identity.getUsername() + " registration email sent.");
                               return "emailSent";
                          }
                          catch(javax.faces.FacesException err){
                               if(err.getMessage().equalsIgnoreCase("Invalid Addresses")) {
                                    facesMessages.add("The entered email address does not appear to be valid. Please check the email address.");
                                    facesMessages.add("Contact us at (877) 631-9080 option 1 if you continue to have difficulties logging in or registering.");
                                    log.info("User " + identity.getUsername() + " invalid email.");
                               }
                               else{
                                    facesMessages.add(err.getMessage());
                               }
                          }
                          catch(Exception err) {
                               facesMessages.add("New user registration is not available at this time.");
                               facesMessages.add("Please contact us at (877) 631-9080 option 1 for assistance with registering or to make a referral.");
                               log.error("Error in self registration - " + identity.getUsername(), err);
                          }
                          invalidateSession();
                          return null;
                     }

                     static private String generateRandomString(){
                          SecureRandom randomGenerator = new SecureRandom();
                          byte[] buffer = new byte[RANDOM_STRING_LENGTH_UPPER_LIMIT * 2];
                          randomGenerator.nextBytes(buffer);
                          //mask off sign bit...
                          int stringLength = buffer[0] & 0x7F;
                          //apply scale...
                          double scaledLength = ((double)(RANDOM_STRING_LENGTH_UPPER_LIMIT - RANDOM_STRING_LENGTH_LOWER_LIMIT)/(double)0x7F)*stringLength
                                    + RANDOM_STRING_LENGTH_LOWER_LIMIT;
                          stringLength = (int) scaledLength;
                          StringBuffer returnThis = new StringBuffer();
                          int i = 1;
                          while(returnThis.length() < stringLength && i < (RANDOM_STRING_LENGTH_UPPER_LIMIT * 2)){
                               StringBuffer candidate = new StringBuffer().append((char)(buffer[i] & 0x7F));
                               if(allowedCharacters.matcher(candidate).matches()){
                                    returnThis.append(candidate);
                               }
                               i++;
                          }
                          double randomNumber = Math.random() * 10;
                          returnThis.append(Math.round(randomNumber));
                          return returnThis.toString();
                     }

                     public void invalidateSession(){
                          log.info("User " + identity.getUsername() + " session invalidated.");
                          user = null;
                          identity.logout();
                          identity.setUsername(null);
                          identity.setPassword(null);
                     }

                     private void setupApplicationContext(){
                          FacesContext context = FacesContext.getCurrentInstance();
                          StringBuffer requestURL = ((HttpServletRequest)context.getExternalContext().getRequest()).getRequestURL();
                          String page = requestURL.toString().replaceFirst("(.*/)(.*)$", "$2");

                          //HACK - the user hasn't been authenticated - but I want to make sure that external users don't get the internal look/feel...
                          String domain = null;
                          int domainSeparatorIndex = identity.getUsername().trim().indexOf('@');
                          if(domainSeparatorIndex > 0){
                               domain = identity.getUsername().trim().substring(domainSeparatorIndex + 1);
                          }

                          if(domain == null || !domain.equalsIgnoreCase("assistgroup.com") || page.equals("ClientLogin.seam") || page.equals("ClientRegistration.seam")){
                               Contexts.getApplicationContext().set("loginPage", "ClientLogin.seam");
                               Contexts.getApplicationContext().set("ApplicationLayout", "tag-");
                          }
                          else{
                               Contexts.getApplicationContext().set("loginPage", page);
                               Contexts.getApplicationContext().set("ApplicationLayout", "");
                          }
                     }

                     public boolean getEditAccount(){
                          return this.editAccount;
                     }

                     static public void setupContactForRegistration(Contact c){
                          //Always reset password...
                          c.setPassword(generateRandomString());

                          String registrationKey = generateRandomString();
                          c.setRegistrationKey(registrationKey);

                          c.setChangePasswordNextLogin(Boolean.FALSE);


                          Contexts.getEventContext().set("registrant", c);
                          //Need the URL for the email... sigh.
                          FacesContext context = FacesContext.getCurrentInstance();
                          String registrationPage = ApplicationProperties.getProperty(ConfigurationVariableName.RegistrationPage);
                          if(registrationPage == null){
                               registrationPage = "ClientRegistration.seam";
                          }
                          StringBuffer requestURL = ((HttpServletRequest)context.getExternalContext().getRequest()).getRequestURL();
                          Contexts.getEventContext().set("requestURL",
                                    requestURL.toString().replaceFirst("(.*/).*$", "$1" + registrationPage));
                          String testersEmail = ApplicationProperties.getProperty(ConfigurationVariableName.TesterEmail);
                          if(testersEmail != null){
                               Contexts.getEventContext().set("testersEmail", testersEmail);
                          }
                     }

                     // Method to monitor outjecting activity of the user object
                     @Out(required = false, scope = SESSION)
                     public Contact getUser() {
                          if(user != null) {
                               log.info("Method:getUser, Outjecting user " + user.getEmailsAsCsv());
                          } else {
                               log.error("Method:getUser, Outjecting null user");
                          }
                          return user;
                     }
                }
                • 5. Re: User's identity switched
                  swd847

                  This is due to the fact that there is no guarentee that the bean you get when you call getUser is the same bean you had when you called authenticate, due to stateless session bean pooling you are getting the wrong user. If you use a stateful bean or pojo your problems should disappear.

                  • 6. Re: User's identity switched
                    joblini

                    Place the user object into Session scope as show below:


                    //if we get this far, user is authenticated...
                    log.info("User #0:#1 authenticated.", identity.getUsername(), addr);
                    user.setLastLogin(new Date());
                    getSessionContext().aet("user", user);




                    Modify the getUser() method to obtain the user from Session scope:



                    public Contact getUser() {
                      return getSessionContext().get("user");
                    }