com.k42b3.neodym.oauth.Oauth.java Source code

Java tutorial

Introduction

Here is the source code for com.k42b3.neodym.oauth.Oauth.java

Source

/**
 * $Id$
 * 
 * neodym
 * A java library to access the REST API of amun
 * 
 * Copyright (c) 2011 Christoph Kappestein <k42b3.x@gmail.com>
 * 
 * This file is part of neodym. neodym is free software: you can 
 * redistribute it and/or modify it under the terms of the GNU 
 * General Public License as published by the Free Software Foundation, 
 * either version 3 of the License, or at any later version.
 * 
 * neodym 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with neodym. If not, see <http://www.gnu.org/licenses/>.
 */

package com.k42b3.neodym.oauth;

import java.awt.Desktop;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.logging.Logger;

import javax.swing.JOptionPane;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import com.k42b3.neodym.Http;
import com.k42b3.neodym.TrafficItem;
import com.k42b3.neodym.TrafficListenerInterface;

/**
 * Oauth
 *
 * @author     Christoph Kappestein <k42b3.x@gmail.com>
 * @license    http://www.gnu.org/licenses/gpl.html GPLv3
 * @link       http://code.google.com/p/delta-quadrant
 * @version    $Revision$
 */
public class Oauth {
    private Http http;
    private OauthProvider provider;

    private String token;
    private String tokenSecret;
    private boolean callbackConfirmed;
    private String verificationCode;

    private boolean authed = false;
    private TrafficListenerInterface trafficListener;

    private Logger logger = Logger.getLogger("com.k42b3.neodym");

    public Oauth(Http http, OauthProvider provider) {
        this.http = http;
        this.provider = provider;

        if ((provider.getToken() != null && !provider.getToken().isEmpty())
                && (provider.getTokenSecret() != null && !provider.getTokenSecret().isEmpty())) {
            this.auth(provider.getToken(), provider.getTokenSecret());
        }
    }

    public void auth(String token, String tokenSecret) {
        this.setToken(token);
        this.setTokenSecret(tokenSecret);

        this.authed = true;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public void setTokenSecret(String tokenSecret) {
        this.tokenSecret = tokenSecret;
    }

    public String getToken() {
        return token;
    }

    public String getTokenSecret() {
        return tokenSecret;
    }

    public boolean isAuthed() {
        return authed;
    }

    public boolean requestToken() throws Exception {
        // add values
        HashMap<String, String> values = new HashMap<String, String>();

        String requestMethod = "GET";

        values.put("oauth_consumer_key", this.provider.getConsumerKey());
        values.put("oauth_signature_method", provider.getMethod());
        values.put("oauth_timestamp", this.getTimestamp());
        values.put("oauth_nonce", this.getNonce());
        values.put("oauth_version", this.getVersion());
        values.put("oauth_callback", "oob");

        // add get vars to values
        URL requestUrl = new URL(provider.getRequestUrl());
        values.putAll(parseQuery(requestUrl.getQuery()));

        // build base string
        String baseString = this.buildBaseString(requestMethod, provider.getRequestUrl(), values);

        // get signature
        SignatureInterface signature = this.getSignature();

        if (signature == null) {
            throw new Exception("Invalid signature method");
        }

        // build signature
        values.put("oauth_signature", signature.build(baseString, provider.getConsumerSecret(), ""));

        // add header to request
        HashMap<String, String> header = new HashMap<String, String>();
        header.put("Authorization", "OAuth realm=\"neodym\", " + this.buildAuthString(values));

        String responseContent = http.request(Http.GET, provider.getRequestUrl(), header);

        // parse response
        this.token = null;
        this.tokenSecret = null;
        this.callbackConfirmed = false;

        HashMap<String, String> response = parseQuery(responseContent);
        Set<String> keys = response.keySet();

        for (String key : keys) {
            if (key.equals("oauth_token")) {
                this.token = response.get(key);

                logger.info("Received token: " + this.token);
            }

            if (key.equals("oauth_token_secret")) {
                this.tokenSecret = response.get(key);

                logger.info("Received token secret: " + this.tokenSecret);
            }

            if (key.equals("oauth_callback_confirmed")) {
                this.tokenSecret = response.get(key);

                this.callbackConfirmed = response.get(key).equals("1");
            }
        }

        if (this.token == null) {
            throw new Exception("No oauth token received");
        }

        if (this.tokenSecret == null) {
            throw new Exception("No oauth token secret received");
        }

        if (this.callbackConfirmed != true) {
            throw new Exception("Callback was not confirmed");
        }

        return true;
    }

    public boolean authorizeToken() throws Exception {
        String url;

        if (this.provider.getAuthorizationUrl().indexOf('?') == -1) {
            url = this.provider.getAuthorizationUrl() + "?oauth_token=" + this.token;
        } else {
            url = this.provider.getAuthorizationUrl() + "&oauth_token=" + this.token;
        }

        URI authUrl = new URI(url);

        if (Desktop.isDesktopSupported()) {
            Desktop desktop = Desktop.getDesktop();

            if (desktop.isSupported(Desktop.Action.BROWSE)) {
                desktop.browse(authUrl);
            } else {
                JOptionPane.showMessageDialog(null, "Visit the following URL: " + authUrl);
            }
        } else {
            JOptionPane.showMessageDialog(null, "Visit the following URL: " + authUrl);
        }

        verificationCode = JOptionPane.showInputDialog("Please enter the verification Code");

        return true;
    }

    public boolean accessToken() throws Exception {
        // add values
        HashMap<String, String> values = new HashMap<String, String>();

        String requestMethod = "GET";

        values.put("oauth_consumer_key", this.provider.getConsumerKey());
        values.put("oauth_token", this.token);
        values.put("oauth_signature_method", provider.getMethod());
        values.put("oauth_timestamp", this.getTimestamp());
        values.put("oauth_nonce", this.getNonce());
        values.put("oauth_verifier", this.verificationCode);

        // add get vars to values
        URL accessUrl = new URL(provider.getAccessUrl());
        values.putAll(parseQuery(accessUrl.getQuery()));

        // build base string
        String baseString = this.buildBaseString(requestMethod, provider.getAccessUrl(), values);

        // get signature
        SignatureInterface signature = this.getSignature();

        if (signature == null) {
            throw new Exception("Invalid signature method");
        }

        // build signature
        values.put("oauth_signature", signature.build(baseString, provider.getConsumerSecret(), this.tokenSecret));

        // add header to request
        HashMap<String, String> header = new HashMap<String, String>();
        header.put("Authorization", "OAuth realm=\"neodym\", " + this.buildAuthString(values));

        String responseContent = http.request(Http.GET, provider.getAccessUrl(), header);

        // parse response
        this.token = null;
        this.tokenSecret = null;

        HashMap<String, String> response = parseQuery(responseContent);
        Set<String> keys = response.keySet();

        for (String key : keys) {
            if (key.equals("oauth_token")) {
                this.token = response.get(key);

                logger.info("Received token: " + this.token);
            }

            if (key.equals("oauth_token_secret")) {
                this.tokenSecret = response.get(key);

                logger.info("Received token secret: " + this.tokenSecret);
            }
        }

        if (this.token == null) {
            throw new Exception("No oauth token received");
        }

        if (this.tokenSecret == null) {
            throw new Exception("No oauth token secret received");
        }

        return true;
    }

    @SuppressWarnings("unchecked")
    public void signRequest(HttpRequestBase request) throws Exception {
        // add values
        HashMap<String, String> values = new HashMap<String, String>();
        HashMap<String, String> auth;

        values.put("oauth_consumer_key", this.provider.getConsumerKey());
        values.put("oauth_token", this.token);
        values.put("oauth_signature_method", provider.getMethod());
        values.put("oauth_timestamp", this.getTimestamp());
        values.put("oauth_nonce", this.getNonce());

        auth = (HashMap<String, String>) values.clone();

        // add get vars to values
        values.putAll(parseQuery(request.getURI().getQuery()));

        // build base string
        String baseString = this.buildBaseString(request.getMethod(), request.getURI().toString(), values);

        // get signature
        SignatureInterface signature = this.getSignature();

        if (signature == null) {
            throw new Exception("Invalid signature method");
        }

        // build signature
        auth.put("oauth_signature", signature.build(baseString, provider.getConsumerSecret(), this.tokenSecret));

        // add header to request
        request.addHeader("Authorization", "OAuth realm=\"neodym\", " + this.buildAuthString(auth));
    }

    private String buildAuthString(HashMap<String, String> values) {
        StringBuilder authString = new StringBuilder();

        Iterator<Entry<String, String>> it = values.entrySet().iterator();

        while (it.hasNext()) {
            Entry<String, String> e = it.next();

            authString.append(urlEncode(e.getKey()) + "=\"" + urlEncode(e.getValue()) + "\", ");
        }

        String str = authString.toString();

        // remove ", " from string
        str = str.substring(0, str.length() - 2);

        return str;
    }

    private String buildBaseString(String requestMethod, String url, HashMap<String, String> params)
            throws Exception {
        StringBuilder base = new StringBuilder();

        base.append(urlEncode(this.getNormalizedMethod(requestMethod)));

        base.append('&');

        base.append(urlEncode(this.getNormalizedUrl(url)));

        base.append('&');

        base.append(urlEncode(this.getNormalizedParameters(params)));

        logger.fine("BaseString: " + base.toString());

        return base.toString();
    }

    private String getNormalizedParameters(HashMap<String, String> params) {
        Iterator<Entry<String, String>> it = params.entrySet().iterator();

        List<String> keys = new ArrayList<String>();

        while (it.hasNext()) {
            Entry<String, String> e = it.next();

            keys.add(e.getKey());
        }

        // sort params
        Collections.sort(keys);

        // build normalized params
        StringBuilder normalizedParams = new StringBuilder();

        for (int i = 0; i < keys.size(); i++) {
            normalizedParams.append(urlEncode(keys.get(i)) + "=" + urlEncode(params.get(keys.get(i))) + "&");
        }

        String str = normalizedParams.toString();

        // remove trailing &
        str = str.substring(0, str.length() - 1);

        return str;
    }

    private String getNormalizedUrl(String rawUrl) throws Exception {
        rawUrl = rawUrl.toLowerCase();

        URL url = new URL(rawUrl);

        int port = url.getPort();

        if (port == -1 || port == 80 || port == 443) {
            return url.getProtocol() + "://" + url.getHost() + url.getPath();
        } else {
            return url.getProtocol() + "://" + url.getHost() + ":" + port + url.getPath();
        }
    }

    private String getNormalizedMethod(String method) {
        return method.toUpperCase();
    }

    private String getTimestamp() {
        return "" + (System.currentTimeMillis() / 1000);
    }

    private String getNonce() {
        try {
            byte[] nonce = new byte[32];

            Random rand;

            rand = SecureRandom.getInstance("SHA1PRNG");

            rand.nextBytes(nonce);

            return DigestUtils.md5Hex(rand.toString());
        } catch (Exception e) {
            return DigestUtils.md5Hex("" + System.currentTimeMillis());
        }
    }

    private String getVersion() {
        return "1.0";
    }

    @SuppressWarnings("unused")
    private HttpEntity httpRequest(String method, String url, Map<String, String> header, String body)
            throws Exception {
        // build request
        HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 6000);
        DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);

        HttpRequestBase request;

        if (method.equals("GET")) {
            request = new HttpGet(url);
        } else if (method.equals("POST")) {
            MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
            entity.addPart("text", new StringBody(body));

            request = new HttpPost(url);

            ((HttpPost) request).setEntity(entity);
        } else {
            throw new Exception("Invalid request method");
        }

        // header
        Set<String> keys = header.keySet();

        for (String key : keys) {
            request.setHeader(key, header.get(key));
        }

        // execute HTTP Get Request
        logger.info("Request: " + request.getRequestLine());

        HttpResponse httpResponse = httpClient.execute(request);

        HttpEntity entity = httpResponse.getEntity();

        String responseContent = EntityUtils.toString(entity);

        // log traffic
        if (trafficListener != null) {
            TrafficItem trafficItem = new TrafficItem();

            trafficItem.setRequest(request);
            trafficItem.setResponse(httpResponse);
            trafficItem.setResponseContent(responseContent);

            trafficListener.handleRequest(trafficItem);
        }

        // check status code
        int statusCode = httpResponse.getStatusLine().getStatusCode();

        if (!(statusCode >= 200 && statusCode < 300)) {
            JOptionPane.showMessageDialog(null, responseContent);

            throw new Exception("No successful status code");
        }

        return entity;
    }

    private SignatureInterface getSignature() throws Exception {
        String cls;

        if (provider.getMethod().equals("HMAC-SHA1")) {
            cls = "com.k42b3.neodym.oauth.HMACSHA1";
        } else if (provider.getMethod().equals("PLAINTEXT")) {
            cls = "com.k42b3.neodym.oauth.PLAINTEXT";
        } else {
            throw new Exception("Invalid signature method");
        }

        return (SignatureInterface) Class.forName(cls).newInstance();
    }

    public static String urlEncode(String content) {
        try {
            if (!content.isEmpty()) {
                String encoded = URLEncoder.encode(content, "UTF8");

                encoded = encoded.replaceAll("%7E", "~");

                return encoded;
            } else {
                return "";
            }
        } catch (Exception e) {
            return "";
        }
    }

    public static HashMap<String, String> parseQuery(String query) throws Exception {
        HashMap<String, String> map = new HashMap<String, String>();

        if (query != null) {
            String[] params = query.split("&");

            for (int i = 0; i < params.length; i++) {
                String[] pair = params[i].split("=");

                if (pair.length >= 1) {
                    String name = URLDecoder.decode(pair[0], "UTF-8");
                    String value = pair.length == 2 ? URLDecoder.decode(pair[1], "UTF-8") : "";

                    map.put(name, value);
                }
            }
        }

        return map;
    }
}