org.picketlink.social.facebook.FacebookProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.picketlink.social.facebook.FacebookProcessor.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 2.1 of
 * the License, or (at your option) any later version.
 *
 * This 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 this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.picketlink.social.facebook;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

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

import org.apache.catalina.Realm;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Processor to perform Facebook interaction
 *
 * @author Anil Saldhana
 * @since Sep 22, 2011
 */
public class FacebookProcessor {
    protected static Logger log = Logger.getLogger(FacebookProcessor.class);
    protected boolean trace = log.isTraceEnabled();

    protected FacebookUtil util = new FacebookUtil(FacebookConstants.SERVICE_URL);

    public static ThreadLocal<Principal> cachedPrincipal = new ThreadLocal<Principal>();

    public static ThreadLocal<List<String>> cachedRoles = new ThreadLocal<List<String>>();
    public static String EMPTY_PASSWORD = "EMPTY";

    protected List<String> roles = new ArrayList<String>();

    public enum STATES {
        AUTH, AUTHZ, FINISH
    };

    protected String clientID;
    protected String clientSecret;
    protected String scope;
    private String returnURL;

    public FacebookProcessor(String clientID, String clientSecret, String scope, String returnURL,
            List<String> requiredRoles) {
        super();
        this.clientID = clientID;
        this.clientSecret = clientSecret;
        this.scope = scope;
        this.returnURL = returnURL;
        this.roles.addAll(requiredRoles);
    }

    public void setRoleString(String roleStr) {
        if (roleStr == null)
            throw new RuntimeException("Role String is null in configuration");
        StringTokenizer st = new StringTokenizer(roleStr, ",");
        while (st.hasMoreElements()) {
            roles.add(st.nextToken());
        }
    }

    public boolean initialInteraction(Request request, Response response) throws IOException {
        HttpSession session = request.getSession();
        Map<String, String> params = new HashMap<String, String>();
        params.put(OAuthConstants.REDIRECT_URI_PARAMETER, returnURL);
        params.put(OAuthConstants.CLIENT_ID_PARAMETER, clientID);

        if (scope != null) {
            params.put(OAuthConstants.SCOPE_PARAMETER, scope);
        }

        String location = new StringBuilder(FacebookConstants.SERVICE_URL).append("?")
                .append(util.createQueryString(params)).toString();
        try {
            session.setAttribute("STATE", STATES.AUTH.name());
            if (trace)
                log.trace("Redirect:" + location);
            response.sendRedirect(location);
            return false;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public boolean handleAuthStage(HttpServletRequest request, HttpServletResponse response) {
        request.getSession().setAttribute("STATE", STATES.AUTHZ.name());
        sendAuthorizeRequest(this.returnURL, response);
        return false;
    }

    protected void sendAuthorizeRequest(String returnUrl, HttpServletResponse response) {
        String returnUri = returnUrl;

        Map<String, String> params = new HashMap<String, String>();
        params.put(OAuthConstants.REDIRECT_URI_PARAMETER, returnUri);
        params.put(OAuthConstants.CLIENT_ID_PARAMETER, clientID);
        if (scope != null) {
            params.put(OAuthConstants.SCOPE_PARAMETER, scope);
        }
        String location = new StringBuilder(FacebookConstants.AUTHENTICATION_ENDPOINT_URL).append("?")
                .append(util.createQueryString(params)).toString();
        try {
            response.sendRedirect(location);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public Principal getPrincipal(Request request, Response response, Realm realm) {
        Principal principal = null;
        Principal facebookPrincipal = handleAuthenticationResponse(request, response);
        if (facebookPrincipal == null)
            return null;

        request.getSession().setAttribute("PRINCIPAL", facebookPrincipal);
        cachedPrincipal.set(facebookPrincipal);

        if (isJBossEnv()) {
            cachedRoles.set(roles);
            principal = realm.authenticate(facebookPrincipal.getName(), EMPTY_PASSWORD);
        } else {
            // Create a Tomcat Generic Principal
            principal = new GenericPrincipal(realm, facebookPrincipal.getName(), null, roles, facebookPrincipal);
        }
        return principal;
    }

    protected Principal handleAuthenticationResponse(HttpServletRequest request, HttpServletResponse response) {
        String error = request.getParameter(OAuthConstants.ERROR_PARAMETER);
        if (error != null) {
            throw new RuntimeException("error:" + error);
        } else {
            String returnUrl = returnURL;
            String authorizationCode = request.getParameter(OAuthConstants.CODE_PARAMETER);
            if (authorizationCode == null) {
                log.error("Authorization code parameter not found");
                return null;
            }

            URLConnection connection = sendAccessTokenRequest(returnUrl, authorizationCode, response);

            Map<String, String> params = formUrlDecode(readUrlContent(connection));
            String accessToken = params.get(OAuthConstants.ACCESS_TOKEN_PARAMETER);
            String expires = params.get(FacebookConstants.EXPIRES);

            if (trace)
                log.trace("Access Token=" + accessToken + " :: Expires=" + expires);

            if (accessToken == null) {
                throw new RuntimeException("No access token found");
            }

            return readInIdentity(request, response, accessToken, returnUrl);
        }
    }

    protected URLConnection sendAccessTokenRequest(String returnUrl, String authorizationCode,
            HttpServletResponse response) {
        String returnUri = returnURL;

        Map<String, String> params = new HashMap<String, String>();
        params.put(OAuthConstants.REDIRECT_URI_PARAMETER, returnUri);
        params.put(OAuthConstants.CLIENT_ID_PARAMETER, clientID);
        params.put(OAuthConstants.CLIENT_SECRET_PARAMETER, clientSecret);
        params.put(OAuthConstants.CODE_PARAMETER, authorizationCode);

        String location = new StringBuilder(FacebookConstants.ACCESS_TOKEN_ENDPOINT_URL).append("?")
                .append(util.createQueryString(params)).toString();

        try {
            if (trace)
                log.trace("AccessToken Request=" + location);
            URL url = new URL(location);
            URLConnection connection = url.openConnection();
            return connection;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean isJBossEnv() {
        Class<?> clazz = SecurityActions.loadClass(getClass(), "org.jboss.as.web.WebServer");
        if (clazz == null)
            clazz = SecurityActions.loadClass(getClass(), "org.jboss.system.Service");
        if (clazz != null)
            return true;
        return false;
    }

    private Principal readInIdentity(HttpServletRequest request, HttpServletResponse response, String accessToken,
            String returnUrl) {
        FacebookPrincipal facebookPrincipal = null;
        try {
            String urlString = new StringBuilder(FacebookConstants.PROFILE_ENDPOINT_URL).append("?access_token=")
                    .append(URLEncoder.encode(accessToken, "UTF-8")).toString();
            if (trace)
                log.trace("Profile read:" + urlString);

            URL profileUrl = new URL(urlString);
            String profileContent = readUrlContent(profileUrl.openConnection());
            JSONObject jsonObject = new JSONObject(profileContent);

            facebookPrincipal = new FacebookPrincipal();
            facebookPrincipal.setAccessToken(accessToken);
            facebookPrincipal.setId(jsonObject.getString("id"));
            facebookPrincipal.setName(jsonObject.getString("name"));
            facebookPrincipal.setFirstName(jsonObject.getString("first_name"));
            facebookPrincipal.setLastName(jsonObject.getString("last_name"));
            facebookPrincipal.setGender(jsonObject.getString("gender"));
            facebookPrincipal.setTimezone(jsonObject.getString("timezone"));
            facebookPrincipal.setLocale(jsonObject.getString("locale"));
            if (jsonObject.getString("email") != null) {
                facebookPrincipal.setEmail(jsonObject.getString("email"));
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return facebookPrincipal;
    }

    private String readUrlContent(URLConnection connection) {
        StringBuilder result = new StringBuilder();
        try {
            Reader reader = new InputStreamReader(connection.getInputStream());
            char[] buffer = new char[50];
            int nrOfChars;
            while ((nrOfChars = reader.read(buffer)) != -1) {
                result.append(buffer, 0, nrOfChars);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result.toString();
    }

    private Map<String, String> formUrlDecode(String encodedData) {
        Map<String, String> params = new HashMap<String, String>();
        String[] elements = encodedData.split("&");
        for (String element : elements) {
            String[] pair = element.split("=");
            if (pair.length == 2) {
                String paramName = pair[0];
                String paramValue;
                try {
                    paramValue = URLDecoder.decode(pair[1], "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
                params.put(paramName, paramValue);
            } else {
                throw new RuntimeException("Unexpected name-value pair in response: " + element);
            }
        }
        return params;
    }
}