com.google.cloud.dataflow.sdk.util.Credentials.java Source code

Java tutorial

Introduction

Here is the source code for com.google.cloud.dataflow.sdk.util.Credentials.java

Source

/*
 * Copyright (C) 2015 Google 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 com.google.cloud.dataflow.sdk.util;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AbstractPromptReceiver;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleOAuthConstants;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.cloud.dataflow.sdk.options.GcpOptions;
import com.google.common.base.Preconditions;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Provides support for loading credentials.
 */
public class Credentials {

    private static final Logger LOG = LoggerFactory.getLogger(Credentials.class);

    /**
     * OAuth 2.0 scopes used by a local worker (not on GCE).
     * The scope cloud-platform provides access to all Cloud Platform resources.
     * cloud-platform isn't sufficient yet for talking to datastore so we request
     * those resources separately.
     *
     * <p>Note that trusted scope relationships don't apply to OAuth tokens, so for
     * services we access directly (GCS) as opposed to through the backend
     * (BigQuery, GCE), we need to explicitly request that scope.
     */
    private static final List<String> SCOPES = Arrays.asList("https://www.googleapis.com/auth/cloud-platform",
            "https://www.googleapis.com/auth/devstorage.full_control",
            "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/datastore");

    private static class PromptReceiver extends AbstractPromptReceiver {
        @Override
        public String getRedirectUri() {
            return GoogleOAuthConstants.OOB_REDIRECT_URI;
        }
    }

    /**
     * Initializes OAuth2 credentials.
     *
     * <p>This can use 3 different mechanisms for obtaining a credential:
     * <ol>
     *   <li>
     *     It can fetch the
     *     <a href="https://developers.google.com/accounts/docs/application-default-credentials">
     *     application default credentials</a>.
     *   </li>
     *   <li>
     *     The user can specify a client secrets file and go through the OAuth2
     *     webflow. The credential will then be cached in the user's home
     *     directory for reuse. Provide the property "secrets_file" to use this
     *     mechanism.
     *   </li>
     *   <li>
     *     The user can specify a file containing a service account.
     *     Provide the properties "service_account_keyfile" and
     *     "service_account_name" to use this mechanism.
     *   </li>
     * </ol>
     * The default mechanism is to use the
     * <a href="https://developers.google.com/accounts/docs/application-default-credentials">
     * application default credentials</a>. The other options can be used by providing the
     * corresponding properties.
     */
    public static Credential getCredential(GcpOptions options) throws IOException, GeneralSecurityException {
        String keyFile = options.getServiceAccountKeyfile();
        String accountName = options.getServiceAccountName();

        if (keyFile != null && accountName != null) {
            try {
                return getCredentialFromFile(keyFile, accountName, SCOPES);
            } catch (GeneralSecurityException e) {
                throw new IOException("Unable to obtain credentials from file", e);
            }
        }

        if (options.getSecretsFile() != null) {
            return getCredentialFromClientSecrets(options, SCOPES);
        }

        try {
            return GoogleCredential.getApplicationDefault().createScoped(SCOPES);
        } catch (IOException e) {
            throw new RuntimeException("Unable to get application default credentials. Please see "
                    + "https://developers.google.com/accounts/docs/application-default-credentials "
                    + "for details on how to specify credentials. This version of the SDK is "
                    + "dependent on the gcloud core component version 2015.02.05 or newer to "
                    + "be able to get credentials from the currently authorized user via gcloud auth.", e);
        }
    }

    /**
     * Loads OAuth2 credential from a local file.
     */
    private static Credential getCredentialFromFile(String keyFile, String accountId, Collection<String> scopes)
            throws IOException, GeneralSecurityException {
        GoogleCredential credential = new GoogleCredential.Builder().setTransport(Transport.getTransport())
                .setJsonFactory(Transport.getJsonFactory()).setServiceAccountId(accountId)
                .setServiceAccountScopes(scopes).setServiceAccountPrivateKeyFromP12File(new File(keyFile)).build();

        LOG.info("Created credential from file {}", keyFile);
        return credential;
    }

    /**
     * Loads OAuth2 credential from client secrets, which may require an
     * interactive authorization prompt.
     */
    private static Credential getCredentialFromClientSecrets(GcpOptions options, Collection<String> scopes)
            throws IOException, GeneralSecurityException {
        String clientSecretsFile = options.getSecretsFile();

        Preconditions.checkArgument(clientSecretsFile != null);
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleClientSecrets clientSecrets;

        try {
            clientSecrets = GoogleClientSecrets.load(jsonFactory, new FileReader(clientSecretsFile));
        } catch (IOException e) {
            throw new RuntimeException("Could not read the client secrets from file: " + clientSecretsFile, e);
        }

        FileDataStoreFactory dataStoreFactory = new FileDataStoreFactory(
                new java.io.File(options.getCredentialDir()));

        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory,
                clientSecrets, scopes).setDataStoreFactory(dataStoreFactory)
                        .setTokenServerUrl(new GenericUrl(options.getTokenServerUrl()))
                        .setAuthorizationServerEncodedUrl(options.getAuthorizationServerEncodedUrl()).build();

        // The credentialId identifies the credential if we're using a persistent
        // credential store.
        Credential credential = new AuthorizationCodeInstalledApp(flow, new PromptReceiver())
                .authorize(options.getCredentialId());

        LOG.info("Got credential from client secret");
        return credential;
    }
}