org.kaaproject.kaa.server.verifiers.gplus.verifier.GplusUserVerifier.java Source code

Java tutorial

Introduction

Here is the source code for org.kaaproject.kaa.server.verifiers.gplus.verifier.GplusUserVerifier.java

Source

/*
 * Copyright 2014-2016 CyberVision, Inc.
 *
 * 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.kaaproject.kaa.server.verifiers.gplus.verifier;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.codehaus.jackson.map.ObjectMapper;
import org.kaaproject.kaa.server.common.verifier.AbstractKaaUserVerifier;
import org.kaaproject.kaa.server.common.verifier.UserVerifierCallback;
import org.kaaproject.kaa.server.common.verifier.UserVerifierContext;
import org.kaaproject.kaa.server.verifiers.gplus.config.gen.GplusAvroConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class GplusUserVerifier extends AbstractKaaUserVerifier<GplusAvroConfig> {
    private static final Logger LOG = LoggerFactory.getLogger(GplusUserVerifier.class);
    private static final String GOOGLE_OAUTH = "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=";
    private static final int HTTP_OK = 200;
    private static final int HTTP_BAD_REQUEST = 400;
    private static final Charset CHARSET = Charset.forName("UTF-8");
    private GplusAvroConfig configuration;
    private ExecutorService threadPool;
    private volatile CloseableHttpClient httpClient;

    @Override
    public void init(UserVerifierContext context, GplusAvroConfig configuration) {
        LOG.info("Initializing user verifier with context {} and configuration {}", context, configuration);
        this.configuration = configuration;
    }

    @Override
    public void checkAccessToken(String userExternalId, String accessToken, UserVerifierCallback callback) {
        try {
            URI uri = new URI(GOOGLE_OAUTH + accessToken);
            threadPool.submit(new RunnableVerifier(uri, callback, userExternalId));
        } catch (URISyntaxException ex) {
            LOG.warn("Internal error", ex);
            callback.onInternalError();
        }
    }

    protected String readResponse(InputStream is) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] data = new byte[2048];
        int len = 0;
        while ((len = is.read(data, 0, data.length)) >= 0) {
            bos.write(data, 0, len);
        }
        byte[] bytes = bos.toByteArray();
        bos.close();
        return new String(bytes, CHARSET);
    }

    protected CloseableHttpResponse establishConnection(URI uri) throws IOException {
        return httpClient.execute(new HttpGet(uri));
    }

    @Override
    public void start() {
        LOG.info("user verifier started");
        threadPool = new ThreadPoolExecutor(configuration.getMinParallelConnections(),
                configuration.getMaxParallelConnections(), configuration.getKeepAliveTimeMilliseconds(),
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(configuration.getMaxParallelConnections());
        httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
    }

    @Override
    public void stop() {
        threadPool.shutdown();
        if (null != httpClient) {
            try {
                httpClient.close();
            } catch (IOException ex) {
                LOG.warn("Internal error: ", ex);
            }
        }
        LOG.info("user verifier stopped");
    }

    @Override
    public Class<GplusAvroConfig> getConfigurationClass() {
        return GplusAvroConfig.class;
    }

    private class RunnableVerifier implements Runnable {

        private final URI uri;
        private final UserVerifierCallback callback;
        private final String userExternalId;

        public RunnableVerifier(URI uri, UserVerifierCallback callback, String userExternalId) {
            this.uri = uri;
            this.callback = callback;
            this.userExternalId = userExternalId;
        }

        @Override
        public void run() {
            CloseableHttpResponse closeableHttpResponse = null;
            try {
                String responseJson;
                int responseCode;
                closeableHttpResponse = establishConnection(uri);
                responseCode = closeableHttpResponse.getStatusLine().getStatusCode();
                if (responseCode == HTTP_OK) {
                    responseJson = readResponse(closeableHttpResponse.getEntity().getContent());
                    ObjectMapper mapper = new ObjectMapper();
                    Map map = mapper.readValue(responseJson, Map.class);
                    String userId = String.valueOf(map.get("user_id"));
                    if (!userExternalId.equals(userId)) {
                        LOG.trace("Input token doesn't belong to the user with {} id", userExternalId);
                        callback.onVerificationFailure("User access token doesn't belong to the user");
                    } else {
                        LOG.trace("Input token is confirmed and belongs to the user with {} id", userExternalId);
                        callback.onSuccess();
                    }
                } else if (responseCode == HTTP_BAD_REQUEST) {
                    LOG.trace("Server auth error: {}",
                            readResponse(closeableHttpResponse.getEntity().getContent()));
                    callback.onTokenInvalid();
                } else {
                    LOG.trace("Server returned the following error code: {}", responseCode);
                    callback.onInternalError();
                }
            } catch (IOException ex) {
                LOG.warn("Internal error: ", ex);
                callback.onInternalError();
            } catch (Exception ex) {
                LOG.warn("Internal error: ", ex);
                callback.onInternalError();
            } finally {
                if (null != closeableHttpResponse) {
                    try {
                        closeableHttpResponse.close();
                    } catch (IOException ex) {
                        LOG.warn("Internal error: ", ex);
                    }
                }
            }
        }
    }
}