Google Calendar APIにサービスアカウントでアクセスする。

サービスアカウントの使い方を知りたくて試したメモ。

OAuth サービス アカウント - AdWords API — Google Developers

Adwordsの説明だけどこれと同じ方法でGoogle Appsのカレンダーにもアクセスできる。

注意点など

  • ServiceAccountを使用してGoogle Appsに接続する場合はユーザーによる認可をドメイン管理のManage API client accessに登録することで代替できる。
    • 認可を代替できると書いたが、ドメインに所属しているユーザーに対してブラウザを使用した認可フローをしなくてよいという意味。しかしユーザーの指定は必要。コードでいうとsetServiceAccountUser("who@example.com")しないとcredential.refreshTokenできない。
  • 会議室リソースは実はドメインがresource.calendar.google.comなので、Google Appsの契約ドメインではないため、会議室アカウントになりすましたりもできない。
  • 今回は会議室一覧をsetServiceAccountUserなしでできないかなとおもったのだけど、Google Calendar APIだけだとそれはできなかった。

ということで一度特権管理者的な人にOAuth2のフローで認可してもらう方がよい。もしくは一度アカウントを取得するためにアカウント(メールアドレス)を入力してもらって、Serviceアカウントではそのアカウントを使用するかということになる。


ちなみに夜間バッチ的なプログラムであれば大変よい。

package example;

import java.io.File;
import java.io.FileReader;
import java.util.Arrays;

import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
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.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;

public class ServiceAccountAccess {
	public static void main(String[]  args) throws Exception{
		JsonFactory jsonfactory = new GsonFactory();
		HttpTransport transport = new NetHttpTransport();
		
		GoogleClientSecrets secret = GoogleClientSecrets.load(jsonfactory, new FileReader("client_secret.json"));

		String service_account = (String)secret.getWeb().get("client_email");
		String client_id = secret.getWeb().getClientId();
		
		
		GoogleCredential credential = new GoogleCredential.Builder()
		.setTransport(transport)
		.setJsonFactory(jsonfactory)
		.setClientSecrets(secret)
		.setServiceAccountId(service_account)
		.setServiceAccountPrivateKeyFromP12File(new File("service_account.p12"))
		.setServiceAccountScopes(Arrays.asList(CalendarScopes.CALENDAR))
		.setServiceAccountUser("who@example.com") // ドメインのユーザーを指定する必要あり
		.build();
		
		if(needRefresh(credential) && !credential.refreshToken()){
			return ;
		}
		
		Calendar service = new Calendar.Builder(transport, jsonfactory, credential)
		.setApplicationName(client_id)
		.build();
		
		System.out.println(service.events().list("room_resource@resource.calendar.google.com").execute().toPrettyString());
	}
	
	public static boolean needRefresh(GoogleCredential credential){
		return credential.getExpiresInSeconds() == null || credential.getExpiresInSeconds() < 60;
	}
}