Package com.apress.progwt.server.web.controllers.facebook

Source Code of com.apress.progwt.server.web.controllers.facebook.Facebook

package com.apress.progwt.server.web.controllers.facebook;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Map.Entry;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.facebook.api.FacebookParam;
import com.facebook.api.FacebookRestClient;

/**
* Utility class to handle authorization and authentication of requests. Objects
* of this class are meant to be created for every request. They are stateless
* and are not supposed to be kept in the session.
*
* @author yoni
*
*/
public class Facebook {

  private HttpServletRequest request;

  private HttpServletResponse response;

  protected FacebookRestClient apiClient;

  protected String apiKey;

  protected String secret;

  protected Map<String, String> fbParams;

  protected Long user;

  private static String FACEBOOK_URL_PATTERN = "^https?://([^/]*\\.)?facebook\\.com(:\\d+)?/.*";

  public Facebook(HttpServletRequest request, HttpServletResponse response,
      String apiKey, String secret) {
    this.request = request;
    this.response = response;
    this.apiKey = apiKey;
    this.secret = secret;
    this.apiClient = new FacebookRestClient(this.apiKey, this.secret);
    validateFbParams();
    // caching of friends
    String friends = fbParams.get("friends");
    if (friends!=null && !friends.equals("")) {
      List<Long> friendsList = new ArrayList<Long> ();
      for (String friend : friends.split(",")) {
        friendsList.add(Long.parseLong(friend));
      }
      apiClient._setFriendsList(friendsList);
    }
    // caching of the "added" value
    String added = fbParams.get("added");
    if (added != null) {
      apiClient.added = new Boolean (added.equals("1"));
    }
  }

  /**
   * Returns the internal FacebookRestClient object.
   *
   * @return
   */
  public FacebookRestClient getFacebookRestClient() {
    return apiClient;
  }

  /**
   * Synonym for {@link #getFacebookRestClient()}
   *
   * @return
   */
  public FacebookRestClient get_api_client() {
    return getFacebookRestClient();
  }

  /**
   * Returns the secret key used to initialize this object.
   *
   * @return
   */
  public String getSecret() {
    return secret;
  }

  /**
   * Returns the api key used to initialize this object.
   *
   * @return
   */
  public String getApiKey() {
    return apiKey;
  }

  private void validateFbParams() {
    // first we analyze the request parameters
    fbParams = getValidFbParams(_getRequestParams(), 48 * 3600,
        FacebookParam.SIGNATURE.toString());
    if (fbParams != null && !fbParams.isEmpty()) {
      // this comment block is copied from the official php client,
      // it explains a lot :)
      //
      // If we got any fb_params passed in at all, then either:
      // - they included an fb_user / fb_session_key, which we should
      // assume
      // to be correct
      // - they didn't include an fb_user / fb_session_key, which means
      // the
      // user doesn't have a valid session and if we want to get one we'll
      // need to use require_login(). (Calling set_user with null values
      // for user/session_key will work properly.)
      // - Note that we should *not* use our cookies in this scenario,
      // since
      // they may be referring to the wrong user.
      //

      // parsing the user, session, and expiry info
      String tmpSt = fbParams.get(FacebookParam.USER.getSignatureName());
      Long user_id = tmpSt != null ? Long.valueOf(tmpSt) : null;
      String session_key = fbParams.get(FacebookParam.SESSION_KEY
          .getSignatureName());
      tmpSt = fbParams.get(FacebookParam.EXPIRES.getSignatureName());
      Long expires = tmpSt != null ? Long.valueOf(tmpSt) : null;
      setUser(user_id, session_key, expires);
    } else {
      // fallback to checking cookies
      Map<String, String> cookieParams = _getCookiesParams();
      fbParams = getValidFbParams(cookieParams, null, this.apiKey);
      if (fbParams != null && !fbParams.isEmpty()) {
        // parsing the user and session
        String tmpSt = fbParams.get(FacebookParam.USER
            .getSignatureName());
        Long user_id = tmpSt != null ? Long.valueOf(tmpSt) : null;
        String session_key = fbParams.get(FacebookParam.SESSION_KEY
            .getSignatureName());
        setUser(user_id, session_key, null);
      }
      // finally we check the auth_token for a round-trip from the
      // facebook login page
      else if (request.getParameter("auth_token") != null) {
        try {
          doGetSession(request
              .getParameter("auth_token"));
       
          setUser(apiClient._getUserId(), apiClient._getSessionKey(), apiClient._getExpires());
        } catch (Exception e) {
          // if auth_token is stale (browser url doesn't change,
          // server is restarted), then auth_getSession throws
          // an exception. This happens a lot during development. To
          // recover, we do nothing. Then when requireLogin or
          // requireAdd
          // kick in, a new auth_token is created by redirecting the
          // user.
          // e.printStackTrace(System.err);
        }
      }
    }
  }

  public String doGetSession (String authToken) {
    try {
      return apiClient.auth_getSession(authToken);
    } catch (Exception e) {
      throw new RuntimeException (e);
    }
  }
  /**
   * Sets the user. This method also saves the user and session information in
   * the HttpSession
   *
   * @param user_id
   * @param session_key
   * @param expires
   */
  private void setUser(Long user_id, String session_key, Long expires) {
    // place the data in the session for future requests that may not have
    // the
    // facebook parameters
    if (!inFbCanvas()) {
      Map<String, String> cookiesInfo = _getCookiesParams();
      String cookieUser = cookiesInfo.get(this.apiKey + "_user");
      if (cookieUser == null || !cookieUser.equals(user_id + "")) {
        // map of parameters, but without the api_key prefix
        Map<String, String> cookies = new HashMap<String, String> ();
        cookies.put("user", user_id + "");
        cookies.put("session_key", session_key);
        String sig = generateSig(cookies, this.secret);
        int age = 0;
        if (expires!=null) {
          age = (int) (expires.longValue() - (System.currentTimeMillis()/1000));
        }
        for (Map.Entry<String, String> entry : cookies.entrySet()) {
          addCookie (this.apiKey + "_" + entry.getKey(), entry.getValue(), age);
        }
        addCookie (this.apiKey, sig, age);
      }
    }

    this.user = user_id;
    this.apiClient._setSessionKey(session_key);
  }
 
  private void addCookie (String key, String value, int age) {
    Cookie cookie = new Cookie (key, value);
    if (age > 0) {
      cookie.setMaxAge(age);
    }
    cookie.setPath(request.getContextPath());
    response.addCookie(cookie);
  }

  private Map<String, String> getValidFbParams(Map<String, String> params,
      Integer timeout, String namespace) {
    if (namespace == null)
      namespace = "fb_sig";
    String prefix = namespace + "_";
    int prefix_len = prefix.length();
    Map<String, String> fb_params = new HashMap<String, String>();
    for (Entry<String, String> requestParam : params.entrySet()) {
      if (requestParam.getKey().indexOf(prefix) == 0) {
        fb_params.put(requestParam.getKey().substring(prefix_len),
            requestParam.getValue());
      }
    }
    if (timeout != null) {
      if (!fb_params.containsKey(FacebookParam.TIME.getSignatureName())) {
        return new HashMap<String, String>();
      }
      String tmpTime = fb_params.get(FacebookParam.TIME
          .getSignatureName());
      if (tmpTime.indexOf('.') > 0)
        tmpTime = tmpTime.substring(0, tmpTime.indexOf('.'));
      long time = Long.parseLong(tmpTime);
      if (System.currentTimeMillis() / 1000 - time > timeout) {
        return new HashMap<String, String>();
      }
    }
    if (!params.containsKey(namespace)
        || !verifySignature(fb_params, params.get(namespace))) {
      return new HashMap<String, String>();
    }
    return fb_params;
  }

  private void redirect(String url) {
 
      try {
      // fbml redirect
      if (inFbCanvas()) {
        String out = "<fb:redirect url=\"" + url + "\"/>";
        response.getWriter().print(out);
        response.flushBuffer();
      }
      // javascript "frame-bypassing" redirect
      else if (url.matches(FACEBOOK_URL_PATTERN)) {
        String out = "<html><script type=\"text/javascript\">\ntop.location.href = \""
            + url + "\";\n</script></html>";
        response.setHeader("Content-Type", "text/html");
        response.getWriter().print(out);
        response.flushBuffer();
      } else {
        // last fallback
        response.sendRedirect(url);
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
   
  }

  /**
   * Returns true if the application is in a frame or a canvas.
   *
   * @return
   */
  public boolean inFrame() {
    return fbParams.containsKey(FacebookParam.IN_CANVAS.getSignatureName())
        || fbParams.containsKey(FacebookParam.IN_IFRAME
            .getSignatureName());
  }

  /**
   * Returns true if the application is in a canvas.
   *
   * @return
   */
  public boolean inFbCanvas() {
    return fbParams.containsKey(FacebookParam.IN_CANVAS.getSignatureName());
  }

  public boolean isAdded() {
    return "1".equals(fbParams.get(FacebookParam.ADDED.getSignatureName()));
  }

  public boolean isLogin() {
    return getUser() != null;
  }

  /**
   * Synonym for {@link #getUser()}
   *
   * @return
   */
  public Long get_loggedin_user() {
    return getUser();
  }

  /**
   * Returns the user id of the logged in user associated with this object
   *
   * @return
   */
  public Long getUser() {
    return this.user;
  }

  /**
   * Returns the url of the currently requested page
   *
   * @return
   */
  private String currentUrl() {
    String url = request.getScheme() + "://" + request.getServerName();
    int port = request.getServerPort();
    if (port != 80) {
      url += ":" + port;
    }
    url += request.getRequestURI();
    return url;
  }

  /**
   * Forces the user to log in to this application. If the user hasn't logged
   * in yet, this method issues a url redirect.
   *
   * @param next
   *            the value for the 'next' request paramater that is appended to
   *            facebook's login screen.
   * @return true if the user hasn't logged in yet and a redirect was issued.
   */
  public boolean requireLogin(String next) {
    if (getUser() != null)
      return false;
    redirect(getLoginUrl(next, inFrame()));
    return true;
  }

  /**
   * Forces the user to add this application. If the user hasn't added it yet,
   * this method issues a url redirect.
   *
   * @param next
   *            the value for the 'next' request paramater that is appended to
   *            facebook's add screen.
   * @return true if the user hasn't added the application yet and a redirect
   *         was issued.
   */
  public boolean requireAdd(String next) {
    if (getUser() != null && isAdded())
      return false;
    redirect(getAddUrl(next));
    return true;
  }

  /**
   * Forces the application to be in a frame. If it is not in a frame, this
   * method issues a url redirect.
   *
   * @param next
   *            the value for the 'next' request paramater that is appended to
   *            facebook's login screen.
   * @return true if a redirect was issued, false otherwise.
   */
  public boolean requireFrame(String next) {
    if (!inFrame()) {
      redirect(getLoginUrl(next, true));
      return true;
    }
    return false;
  }

  /**
   * Returns the url that facebook uses to prompt the user to login to this
   * application.
   *
   * @param next
   *            indicates the page to which facebook should redirect the user
   *            has logged in.
   * @return
   */
  public String getLoginUrl(String next, boolean canvas) {
    String url = getFacebookUrl(null) + "/login.php?v=1.0&api_key="
        + apiKey;
    try {
      url += next != null ? "&next=" + URLEncoder.encode(next, "UTF-8")
          : "";
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
    url += canvas ? "&canvas" : "";
    return url;
  }

  /**
   * Returns the url that facebook uses to prompt the user to add this
   * application.
   *
   * @param next
   *            indicates the page to which facebook should redirect the user
   *            after the application is added.
   * @return
   */
  public String getAddUrl(String next) {
    String url = getFacebookUrl(null) + "/add.php?api_key=" + apiKey;
    try {
      url += next != null ? "&next=" + URLEncoder.encode(next, "UTF-8")
          : "";
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
    return url;
  }

  /**
   * Returns a url to a facebook sub-domain
   *
   * @param subDomain
   * @return
   */
  public static String getFacebookUrl(String subDomain) {
    if (subDomain == null || subDomain.equals(""))
      subDomain = "www";
    return "http://" + subDomain + ".facebook.com";
  }

  public static String generateSig(Map<String, String> params, String secret) {
    SortedSet<String> keys = new TreeSet<String>(params.keySet());
    // make sure that the signature paramater is not included
    keys.remove(FacebookParam.SIGNATURE.toString());
    String str = "";
    for (String key : keys) {
      str += key + "=" + params.get(key);
    }
    str += secret;
    try {
      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(str.getBytes("UTF-8"));
      StringBuilder result = new StringBuilder();
      for (byte b : md.digest()) {
        result.append(Integer.toHexString((b & 0xf0) >>> 4));
        result.append(Integer.toHexString(b & 0x0f));
      }
      return result.toString();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Verifies that the signature of the parameters is valid
   *
   * @param params
   *            a map of the parameters. Typically these are the request
   *            parameters that start with "fb_sig"
   * @param expected_sig
   *            the expected signature
   * @return
   */
  public boolean verifySignature(Map<String, String> params,
      String expected_sig) {
    return generateSig(params, secret).equals(expected_sig);
  }

  /**
   * returns a String->String map of the request parameters. It doesn't matter
   * if the request method is GET or POST.
   *
   * @return
   */
  private Map<String, String> _getRequestParams() {
    Map<String, String> results = new HashMap<String, String>();
    Map<String, String[]> map = request.getParameterMap();
    for (Entry<String, String[]> entry : map.entrySet()) {
      results.put(entry.getKey(), entry.getValue()[0]);
    }
    return results;
  }

  private Map<String, String> _getCookiesParams() {
    Map<String, String> results = new HashMap<String, String> ();
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
      for (Cookie cookie : cookies) {
        results.put(cookie.getName(), cookie.getValue());
      }
    }
    return results;
  }
 
}
TOP

Related Classes of com.apress.progwt.server.web.controllers.facebook.Facebook

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.