1
votes

I am doing a service automatic generate file and upload it to google drive using Spring boot and java 8. This is an external service of another bigger service.

I can't found any instruction about create credential using service account key for Google drive API so I wrote this code myself base on this instruction: https://cloud.google.com/docs/authentication/production. The code basically perform a simple GET request to get all file and folder in my Google drive. It run but got the exception

enter image description here

Here are the code

package com.example.demo.controller;

import com.example.demo.dto.FileItemDTO;
import com.example.demo.service.GoogleDriveService;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@RestController
public class GoogleDriveController {
    @Value("${google.service.account.key}")
    private static Resource serviceAccountKey;

    @Value("${external.address}")
    private String externalAddress;

    private final GoogleDriveService googleDriveService;
    private static HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
    private static final List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE_METADATA_READONLY);
    private static final String TOKENS_DIRECTORY_PATH = "tokens";

    @Autowired
    public GoogleDriveController(GoogleDriveService googleDriveService) throws IOException {
        this.googleDriveService = googleDriveService;
    }

    private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
        // Load client secrets.
        java.io.File file = new java.io.File("path-to-credential-file");
        InputStream inputStream = new FileInputStream(file);
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(inputStream));

        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
                .setAccessType("offline")
                .build();
        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8868).build();

        return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
    }

    @GetMapping(value = {"/static-page/get"})
    public @ResponseBody
    List<FileItemDTO> getStaticPage() throws Exception {
        String pageToken = null;

        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        Drive drive = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT)).build();
        List<FileItemDTO> responseList = new ArrayList<>();

        FileList fileList = drive.files().list().setFields("files(id,name,thumbnailLink)").execute();
        for (File file : fileList.getFiles()) {
            FileItemDTO item = new FileItemDTO();
            item.setId(file.getId());
            item.setName(file.getName());
            item.setThumbnailLink(file.getThumbnailLink());
            responseList.add(item);
        }

        return responseList;
    }
}

The service account got the owner role. Can somebody please help me about this exception?

As I understand the service account key with owner role have the highest permission.

Edit: After follow @DalmTo answer, I have delete getCredential() method and the code to create Drive object become like this and every thing work fine now:

// file 1 is the JSON credential file
// GoogleCredential have been deprecated so instead I use HttpRequestInitializer 
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(ServiceAccountCredentials.fromStream(new FileInputStream(file1))
        .createScoped(DriveScopes.all()));
Drive drive = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName(ApplicationName).build();
1

1 Answers

2
votes

The code you are using is designed for use with an installed application hence the AuthorizationCodeInstalledApp. You cant use the code for an installed app with a service account.

    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    GoogleCredential credential = GoogleCredential
        .fromStream(new FileInputStream(KEY_FILE_LOCATION))
        .createScoped(DriveScopes.all());

    // Construct the drive service object.
    return new Drive.Builder(httpTransport, JSON_FACTORY, credential)
        .setApplicationName(APPLICATION_NAME).build();
  }

The code basically perform a simple GET request to get all file and folder in my Google drive. It run but got the exception

Remember that a service account is not you it is a dummy user, it has its own google drive account. You can share a folder on your personal google drive account with the service account and grant it access to this folder. You can not share the root folder with it you will have to share everything with it yourself.