CurrentUser.java

/*
 * Copyright (C) 2011-2014 eXo Platform SAS.
 *
 * This file is part of eXo Acceptance Webapp.
 *
 * eXo Acceptance Webapp is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * eXo Acceptance Webapp software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with eXo Acceptance Webapp; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see <http://www.gnu.org/licenses/>.
 */
package org.exoplatform.acceptance.ui.model;

import org.exoplatform.acceptance.security.AppAuthority;
import org.exoplatform.acceptance.security.ICrowdUserDetails;

import com.google.common.base.Objects;
import com.google.common.base.Strings;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Named;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * <p>CurrentUser class.</p>
 *
 * @author Arnaud Héritier ( aheritier@exoplatform.com )
 * @since 2.0.0
 */
@Named("user")
public class CurrentUser {
  @Inject
  @Named("userDetailsService")
  private UserDetailsService userDetailsService;

  private ICrowdUserDetails currentUser;

  /**
   * Returns the user's first name
   *
   * @return a {@link java.lang.String} object.
   */
  public String getFirstName() {
    return getCurrentUser().getFirstName();
  }

  /**
   * Returns the user's last name
   *
   * @return a {@link java.lang.String} object.
   */
  public String getLastName() {
    return getCurrentUser().getLastName();
  }

  /**
   * Returns the user's fullname
   *
   * @return a {@link java.lang.String} object.
   */
  public String getFullName() {
    return getCurrentUser().getFullName();
  }

  /**
   * Returns the user's email
   *
   * @return a {@link java.lang.String} object.
   */
  public String getEmail() {
    return getCurrentUser().getEmail();
  }

  /**
   * Returns the authorities granted to the user. Cannot return <code>null</code>.
   *
   * @return the authorities, sorted by natural key (never <code>null</code>)
   */
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return getCurrentUser().getAuthorities();
  }

  /**
   * Returns the password used to authenticate the user.
   *
   * @return the password
   */
  public String getPassword() {
    return getCurrentUser().getPassword();
  }

  /**
   * Indicates whether the user's account has expired. An expired account cannot be authenticated.
   *
   * @return <code>true</code> if the user's account is valid (ie non-expired), <code>false</code> if no longer valid
   * (ie expired)
   */
  public boolean isAccountNonExpired() {
    return getCurrentUser().isAccountNonExpired();
  }

  /**
   * Indicates whether the user is locked or unlocked. A locked user cannot be authenticated.
   *
   * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
   */
  public boolean isAccountNonLocked() {
    return getCurrentUser().isAccountNonLocked();
  }

  /**
   * Indicates whether the user's credentials (password) has expired. Expired credentials prevent
   * authentication.
   *
   * @return <code>true</code> if the user's credentials are valid (ie non-expired), <code>false</code> if no longer
   * valid (ie expired)
   */
  public boolean isCredentialsNonExpired() {
    return getCurrentUser().isCredentialsNonExpired();
  }

  /**
   * Indicates whether the user is enabled or disabled. A disabled user cannot be authenticated.
   *
   * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
   */
  public boolean isEnabled() {
    return getCurrentUser().isEnabled();
  }

  /**
   * Is the user authenticated ?
   *
   * @return true if the user is authenticated
   */
  public boolean isAuthenticated() {
    return SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated();
  }

  /**
   * Returns the username used to authenticate the user. Cannot return <code>null</code>.
   *
   * @return the username (never <code>null</code>)
   */
  public String getUsername() {
    if (isAuthenticated()) {
      Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      if (principal instanceof UserDetails) {
        return ((UserDetails) principal).getUsername();
      } else {
        return principal.toString();
      }
    } else {
      throw new UsernameNotFoundException("User not authenticated");
    }
  }

  /**
   * Simple searches for an exactly matching {@link org.springframework.security.core.GrantedAuthority#getAuthority()}.
   * Will always return false if the SecurityContextHolder contains an Authentication with nullprincipal and/or GrantedAuthority[] objects.
   *
   * @param role the GrantedAuthorityString representation to check for
   * @return true if an exact (case sensitive) matching granted authority is located, false otherwise
   */
  public boolean hasRole(String role) {
    if (isAuthenticated()) {
      for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
        if (authority.getAuthority().equals(role)) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Checks if the current user is anonymous.
   *
   * @return true only if the user isn't authenticated or has the role ANONYMOUS
   */
  public boolean isAnonymous() {
    return !isAuthenticated() || hasRole(AppAuthority.ROLE_ANONYMOUS.getAuthority());
  }

  /**
   * Checks if the current user has the application USER role.
   *
   * @return true only if the user has the application USER role.
   */
  public boolean isUser() {
    return hasRole(AppAuthority.ROLE_USER.getAuthority());
  }

  /**
   * Checks if the current user has the application ADMIN role.
   *
   * @return true only if the user has the application ADMIN role.
   */
  public boolean isAdmin() {
    return hasRole(AppAuthority.ROLE_ADMIN.getAuthority());
  }

  /**
   * Computes the gravatar URL associated to the user email
   *
   * @param size  The size (width) of the image to generate
   * @param https If the URL must be in HTTPs or no
   * @return The URL of the gravatar
   * @throws java.security.NoSuchAlgorithmException If MD5 Algorithm isn't available
   */
  public String getGravatarUrl(int size, boolean https) throws NoSuchAlgorithmException {
    MessageDigest digest = MessageDigest.getInstance("MD5");
    digest.update(getCurrentUser().getEmail().trim().toLowerCase().getBytes(Charset.defaultCharset()));
    String hash = Strings.padStart(new BigInteger(1, digest.digest()).toString(16), 32, '0');
    if (https) {
      return "https://secure.gravatar.com/avatar/" + hash + "?s=" + size + "&d=mm";
    } else {
      return "http://www.gravatar.com/avatar/" + hash + "?s=" + size + "&d=mm";
    }
  }

  /**
   * Retrieves the current crowd user.
   *
   * @return a {@link org.exoplatform.acceptance.security.ICrowdUserDetails} object.
   * @throws org.springframework.security.core.userdetails.UsernameNotFoundException if any.
   */
  private ICrowdUserDetails getCurrentUser() throws UsernameNotFoundException {
    if (currentUser == null) {
      currentUser = (ICrowdUserDetails) userDetailsService.loadUserByUsername(getUsername());
    }
    return currentUser;
  }

  /**
   * {@inheritDoc}
   * Returns a string representation of the object. In general, the
   * {@code toString} method returns a string that
   * "textually represents" this object. The result should
   * be a concise but informative representation that is easy for a
   * person to read.
   * It is recommended that all subclasses override this method.
   * The {@code toString} method for class {@code Object}
   * returns a string consisting of the name of the class of which the
   * object is an instance, the at-sign character `{@code @}', and
   * the unsigned hexadecimal representation of the hash code of the
   * object. In other words, this method returns a string equal to the
   * value of:
   * <blockquote>
   * <pre>
   * getClass().getName() + '@' + Integer.toHexString(hashCode())
   * </pre></blockquote>
   */
  @Override
  public String toString() {
    return Objects.toStringHelper(this).add("username", getUsername()).add("roles", getAuthorities()).toString();
  }

}