org.jboss.aerogear.android.authentication.digest.DigestAuthenticationModuleRunner.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.aerogear.android.authentication.digest.DigestAuthenticationModuleRunner.java

Source

/**
 * JBoss, Home of Professional Open Source
 * Copyright Red Hat, Inc., and individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.aerogear.android.authentication.digest;

import android.util.Log;

import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.jboss.aerogear.android.authentication.AbstractAuthenticationModuleRunner;
import org.jboss.aerogear.android.pipe.http.HeaderAndBody;
import org.jboss.aerogear.android.pipe.http.HttpException;
import org.jboss.aerogear.android.pipe.http.HttpProvider;
import org.json.JSONObject;

public class DigestAuthenticationModuleRunner extends AbstractAuthenticationModuleRunner {

    private final String TAG = DigestAuthenticationModuleRunner.class.getSimpleName();

    private static String WWW_AUTHENTICATE_HEADER = "WWW-Authenticate";
    private static final String REALM = "realm";
    private static final String DOMAIN = "domain";
    private static final String NONCE = "nonce";
    private static final String STALE = "stale";
    private static final String ALGORITHM = "algorithm";
    private static final String QOP_OPTIONS = "qop";
    private static final String OPAQUE = "opaque";
    private String cnonce = UUID.randomUUID().toString();
    private int nonce_count = 0;
    private String nonce;
    private String qop;
    private String realm;
    private String domain;
    private String algorithm;
    private String stale;
    private String opaque;
    private String username;
    private String password;

    /**
     * @param baseURL the baseURL that all urls (login, enroll, etc) will be
     *            appended to.
     * 
     * @param loginEndpoint the login Endpoint
     * @param logoutEndpoint the logout Endpoint
     * @param timeout the timeout
     * 
     * @throws IllegalArgumentException if an endpoint can not be appended to
     *             baseURL
     */
    public DigestAuthenticationModuleRunner(URL baseURL, String loginEndpoint, String logoutEndpoint,
            Integer timeout) {
        super(baseURL, loginEndpoint, logoutEndpoint, "", timeout);
    }

    @Override
    public HeaderAndBody onEnroll(final Map<String, String> userData) {
        HttpProvider provider = httpProviderFactory.get(enrollURL, timeout);
        String enrollData = new JSONObject(userData).toString();
        return provider.post(enrollData);
    }

    @Override
    public HeaderAndBody onLogin(final String username, final String password) {
        HttpProvider provider = httpProviderFactory.get(loginURL, timeout);
        try {
            provider.get();// Should not be logged in and throw an exception
            throw new IllegalStateException("Login Called on service which was already logged in.");
        } catch (HttpException exception) {
            // If an exception occured that was not a failed login
            if (exception.getStatusCode() != HttpURLConnection.HTTP_UNAUTHORIZED) {
                throw exception;
            }

            Map<String, String> authenticateHeaders = DigestHeaderUtils
                    .extractValues(exception.getHeaders().get(WWW_AUTHENTICATE_HEADER));
            realm = authenticateHeaders.get(REALM);
            domain = authenticateHeaders.get(DOMAIN);
            nonce = authenticateHeaders.get(NONCE);
            algorithm = authenticateHeaders.get(ALGORITHM);
            qop = authenticateHeaders.get(QOP_OPTIONS);
            stale = authenticateHeaders.get(STALE);
            opaque = authenticateHeaders.get(OPAQUE);
            this.username = username;
            this.password = password;

            checkQop(qop);
            checkAlgorithm(algorithm);
            try {
                provider.setDefaultHeader("Authorization",
                        getAuthorizationHeader(loginURL.toURI(), "GET", new byte[] {}));
            } catch (URISyntaxException ex) {
                Log.e(TAG, ex.getMessage(), ex);
                throw new RuntimeException(ex);
            }

            return provider.get();
        }

    }

    @Override
    public void onLogout() {
        HttpProvider provider = httpProviderFactory.get(logoutURL, timeout);

        clear();

        CookieStore store = ((CookieManager) CookieManager.getDefault()).getCookieStore();
        List<HttpCookie> cookies = store.get(getBaseURI());

        for (HttpCookie cookie : cookies) {
            store.remove(getBaseURI(), cookie);
        }

        provider.post("");
    }

    private void clear() {
        realm = null;
        domain = null;
        nonce = null;
        algorithm = null;
        qop = null;
        stale = null;
        opaque = null;
        this.username = null;
        this.password = null;

    }

    /*
     * Currently only supports auth.
     */
    private void checkQop(String qop) {

        if (qop == null) {
            return;
        } else {
            for (String option : qop.split(",")) {
                if ("auth".equals(option)) {
                    this.qop = "auth";
                    return;
                }
            }
        }

        throw new IllegalArgumentException(String.format("%s is not a supported qop type.", qop));

    }

    public String getAuthorizationHeader(URI uri, String method, byte[] entityBody) {
        nonce_count++;
        StringBuilder sb = new StringBuilder();
        String digestResponse;
        String HA1 = calculateHA1();
        String HA2 = calculateHA2(method, uri, entityBody);

        if (qop == null) {
            StringBuilder responseBuilder = new StringBuilder();
            responseBuilder.append(HA1).append(":").append(nonce).append(":").append(HA2);
            digestResponse = DigestHeaderUtils.computeMD5Hash(responseBuilder.toString().getBytes());
        } else {
            StringBuilder responseBuilder = new StringBuilder();
            responseBuilder.append(HA1).append(":").append(nonce).append(":").append(nonce_count).append(":")
                    .append(cnonce).append(":").append(qop).append(":").append(HA2);
            digestResponse = DigestHeaderUtils.computeMD5Hash(responseBuilder.toString().getBytes());
        }

        sb.append("Digest ").append("username=\"").append(username).append('"').append(",realm=\"").append(realm)
                .append('"').append(",nonce=\"").append(nonce).append('"').append(",uri=\"").append(uri.toString())
                .append('"').append(",response=\"").append(digestResponse).append('"');
        if (!(qop == null || qop.isEmpty())) {
            sb.append(",qop=").append(qop).append(",nc=").append(nonce_count).append(",cnonce=\"").append(cnonce)
                    .append('"').append(",opaque=\"").append(opaque).append('"');
        }

        return sb.toString();
    }

    private void checkAlgorithm(String algorithm) {
        if (algorithm == null) {
            return;
        } else {
            for (String option : algorithm.split(",")) {
                if ("MD5".equals(option) || "MD5-sess".equals(option)) {
                    this.algorithm = option;
                    return;
                }
            }
        }

        throw new IllegalArgumentException(String.format("%s is not a supported algorithm type.", algorithm));
    }

    private String calculateHA1() {
        StringBuilder a1Builder = new StringBuilder();
        a1Builder.append(username).append(":").append(realm).append(":").append(password);
        if ("MD5-sess".equals(algorithm)) {
            String tempA1 = DigestHeaderUtils.computeMD5Hash(a1Builder.toString().getBytes());
            a1Builder = new StringBuilder();
            a1Builder.append(tempA1).append(":").append(nonce).append(":").append(cnonce);
        }
        return DigestHeaderUtils.computeMD5Hash(a1Builder.toString().getBytes());
    }

    private String calculateHA2(String method, URI uri, byte[] entityBody) {
        StringBuilder a2Builder = new StringBuilder();
        if ("auth-int".equals(qop)) {
            a2Builder.append(method).append(":").append(uri).append(":")
                    .append(DigestHeaderUtils.computeMD5Hash(entityBody));
        } else {
            a2Builder.append(method).append(":").append(uri);
        }

        return DigestHeaderUtils.computeMD5Hash(a2Builder.toString().getBytes());

    }

    boolean retryLogin() {
        onLogin(username, password);

        return true;
    }
}