Google Apps ScriptでBox Webhookを受取りkintoneへ流してみた【GAS】

前回、Box APIを叩くための準備を行いました。一方で自分の所属組織は、Microsoft365 EnterpriseとBoxを利用しているにも関わらず、Power AutomateからBoxへはBox側でアクセス制限が掛けられていて利用できないという謎の設定になっているため、Boxからの追加・変更・削除の通知が拾えない状況にあります。

誰かが勝手にフォルダを削除しただの移動しただので問い合わせがかなり多く、その度にBoxヘルプデスクにファイルの探索を依頼するという非生産的な問い合わせが多い。

そこで今回Box側にWebhookを設定し、通知内容をkintoneへ記録するまでの手順を作ってみました。ただし、BoxのWebhookで直接kintone APIを叩け無いので、間にGoogle Apps Scriptで作成したAPIを経由させて記録させています。

今回使用するファイルとライブラリ

前回紹介のVBAによるBox API認証も必要です。Box認証用URLをGASで生成できるならば、このスプレッドシートのみで完結します。

事前準備

kintone側

設定の変更

今回kintone側は、Google Apps Scriptからのkintone API実行を受けてレコードを追加します。中継させる理由は以下の通り

  1. Box側でkintone側のAPIを叩く為の認証をすることが出来ない
  2. kintone側ではIP制限を設定しています。しかし、Box側からのIPの範囲は不明であり、またkintoneはドメインで制限が出来ない。
  3. Google Apps Script側からのIP範囲はわかるので、それを登録する事でBox⇒GAS⇒kintoneの連携を実現する
  4. また、今回Box側⇒Microsoft365も自社内で制限されているため、Power Automateで直接受け取れない

そこで以下の設定変更を行います。今回はBasic認証の場合の事例を記述しています。

  1. Cyboze共通管理に入る
  2. 左サイドパネルの「ログイン」をクリック
  3. 左サイドパネルの「アクセス制限」をクリック
  4. サイボウズドットコムストアへをクリックし、ログインする。
  5. ドメイン管理のセキュリティと認証に於いて、Basic認証を有効にしてIDとPWを取得する
  6. 5.のID:PWの形式でつなげて、Base64でエンコードした文字列を作る
  7. また、普段使ってるユーザのIDとPWも同様にBase64エンコードした文字列を作っておく
  8. UrlfetchAppがアクセスしてくるIPアドレス範囲は広範囲であるのと、IPアドレスを登録してもなぜかアクセス出来ないので、今回はこのような処置をしています。

※kintoneがしょぼいなぁと思うのが、今どきIPアドレス制限だけでリモートホストのドメインで制限が出来ない点。大手のサービスのIPアドレスは固定じゃないので、IPアドレス制限だと連携が非常に厳しい。

必要な情報を集める

kintone APIのレコード登録で必要な情報を集めます。今回、kintone側はファイルID、ファイル名、ユーザ、アクションの4つのテキストボックスのみを用意しています。

  1. 書き込み先のアプリを用意し、各フィールドのフィールドコードは事前に控えておいてください
  2. また、そのアプリの番号(https://ドメイン名.cybozu.com/k/999/)の数字を控えておいてください
  3. 対象のプロジェクトのAPIトークンを取得しておいてください(権限はレコード追加のみでOK)

図:フィールドコードが重要です。

Box側

Box側で行う設定ですが、プロジェクトの作成や認証に必要な手順等については今回は省略します。準備編をご覧ください。ここではwebhookを使うのに必要な部分だけを記述しています。

  1. 開発コンソールにログインし、自分の作成したプロジェクトに入る
  2. 左サイドパネルの構成をクリック
  3. 下の方にあるアプリケーションスコープwebhookを管理にチェックを入れる
  4. 変更を保存をクリック

以下は予め作成しておいたカスタムアプリに入ってからの作業になります。

  1. 左サイドパネルのwebhookをクリック
  2. 下のほうにあるWebhook V1で追加をクリック
  3. 名称、説明を入力し、イベントのトリガーで必要なものだけを✔します。
  4. エンドポイント構成のペイロード構成はRESTとし、エンドポイントURLはGAS側の公開URLに最後?を付けたものを入力します。
  5. コールバックパラメータの追加をクリックし、方法:Get、パラメータ名:param1、取得パラメータ名は#item_name#を入力してみました(複数登録可能
  6. Webhookの保存をクリック
  7. これだけでは実は使えないので、前回のサンプルAccessファイルでこのプロジェクトの認証だけ済ませておきます。これをしないと、Boxのアプリとして登録してくれない・・・・なので、Box APIも必要
  8. 認証を終えると、アプリを開く。マイアプリケーションを開くと今回のプロジェクトが登録されています。これで稼働します。
  9. v1の場合自分がオーナー&自分にアクセス権のある全てのフォルダが通知のWebhook対象になり、細かく設定はできません(逆にv2の場合、フォルダ毎に設定は出来ますがパラメータを細かく設定出来ません。)。
  10. 誰かがアップロードや編集等を行うと、GASのURLが叩かれ、kintone側へと中継されます

図:共有イベントとしてwebhookが登録済みになってる

図:スコープの設定画面

ソースコード

Basic認証の場合のコード

この段階でBox側とkintone側の準備は完了しています。GAS側はUrlfetchappにてkintoneのREST APIで流し込んであげれば完了です。UrlfetchAppのQuotaはG Suite Basicで100,000回/1日なので、100名/各人1000回で到達する回数ですが、必要な通知を絞れば良いでしょう。今回は、Box側で通知する内容は、Moved, Deletedの2つに絞っています。

今回のkintone側の制限で気をつけないといけないのは、同時接続数100/ドメインくらい。

function doGet(e) {  
  //BOX側から受け取るパラメータを用意する
  var itemname = e.parameter.param1;
  var itemid = e.parameter.param2;
  var eventtype = e.parameter.param3;
  var username = e.parameter.param4;

  //kintoneへデータを送るURL
  var kinrecinsert = "https://ここにドメイン名.cybozu.com/k/v1/record.json";
  
  //リクエストボディを組み立てる
  var payload = {
    "app": "ここにkintoneのアプリの番号を入れる",
    "record": {
        "boxfileid": {
            "value": itemid
        },
        "filename": {
            "value": itemname
        }, 
        "username": {
            "value": username
        }, 
        "action": {
            "value": eventtype
        }, 
    }
  }

  //UrlfetchAppでアクセス
  var res = UrlFetchApp.fetch(
    kinrecinsert,
    {
      method:"POST",
      payload:JSON.stringify(payload),
      headers: {
        "X-Cybozu-Authorization": "ここに普段のIDPWをBase64エンコードした文字を入れる",
        "Authorization": "Basic ここにBasic認証をBase64エンコードした文字列を入れる",
        "Content-Type": "application/json"
      },
      muteHttpExceptions:true
    }
  )
}
  • e.parameterにてBox側で設定したパラメータを受け取っています
  • kintone REST APIにおけるレコード追加のAPIのエンドポイントを設定しておきます。
  • payloadに足してAPIへのリクエストBodyを作成していきます。
  • 今回はBasic認証で行っているので、headersにはX-Cybozu-Authorization、Authorizationそして、Content-Typeを指定しています。
  • X-Cybozu-Authorizationにはkintone側準備の5.で用意したBasic認証のID:PWをBase64エンコードした文字列を入れてあります。
  • Authorizationには普段kintoneで使ってるkintone側準備の6.で用意したID:PWをBase64エンコードした文字列を入れてあります。
  • payloadに組み立てたリクエストBodyを入れておきます。JSON.stringifyしておくことを忘れずに。
  • 最後にdoGetなのでNewにてウェブアプリケーションとして公開し、アクセスは誰でもできるようにしておきます。スプレッドシート本体は非公開で問題ありません。
  • kintone側権限を絞りたい場合には、対象のkintoneのアプリに於いて対象のユーザを追加し、レコード追加だけを設定。そのユーザのID:PWをBase64エンコードした文字列をAuthorizationに入れればよいのではないかと思います。
  • 素直にBox⇔Microsoft365の相互の通信を許可してPower Automateで受け取ってGraph APIでExcelにデータを書き込むほうがオススメです(変なQuotaとかも無いので通知をバンバン残せます)。

図:こんな感じで自動で通知がkintoneに追加されていきます

OAuth2.0認証の場合のコード

今回はBasic認証の場合の事例を紹介していました。これは今自分がおかれてる制限がある為です。しかし、特にIPアドレスで制限をしていないのであればBasic認証など使う必要も無いので、OAuth2.0認証でGAS側からKintoneへPOSTすることも可能です。

認証をするコード

//認証用の各種変数
var appid = 'ここにクライアントIDを入れる';
var appsecret='ここにクライアントシークレットを入れる';
var domain = "ここにドメイン名を入れる"
var scope = "k:app_record:write"  //レコード追加だけの権限を設定
var endpoint = "https://" + domain + ".cyboze.com/k/v1/record.json"
var tokenurl = "https://" + domain + ".cybozu.com/oauth2/token"
var authurl = "https://" + domain + ".cybozu.com/oauth2/authorization"

function startoauth(){
  //UIを取得する
  var ui = SpreadsheetApp.getUi();
  
  //認証済みかチェックする
  var service = checkOAuth();
  if (!service.hasAccess()) {
    //認証画面を出力
    var output = HtmlService.createHtmlOutputFromFile('template').setHeight(450).setWidth(500).setSandboxMode(HtmlService.SandboxMode.IFRAME);
    ui.showModalDialog(output, 'OAuth2.0認証');
  } else {
    //認証済みなので終了する
    ui.alert("すでに認証済みです。");
  }
}

//アクセストークンURLを含んだHTMLを返す関数
function authpage(){
  var service = checkOAuth();
  var authorizationUrl = service.getAuthorizationUrl();
  var html = "<center><b><a href='" + authorizationUrl + "' target='_blank' onclick='closeMe();'>アクセス承認</a></b></center>"
  return html;
}

//認証チェック
function checkOAuth() {
  return OAuth2.createService("kintone")
    .setAuthorizationBaseUrl(authurl)
    .setTokenUrl(tokenurl)
    .setClientId(appid)
    .setClientSecret(appsecret)
    .setScope(scope)
    .setCallbackFunction("authCallback") //認証を受けたら受け取る関数を指定する
    .setPropertyStore(PropertiesService.getScriptProperties())  //スクリプトプロパティに保存する
    .setParam("response_type", "code");
}

//認証コールバック
function authCallback(request) {
  var service = checkOAuth();
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じてください。");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。");
  }
}

//ログアウト
function reset() {
  checkOAuth().reset();
  SpreadsheetApp.getUi().alert("ログアウトしました。")
}
  • 予め、スプレッドシートにライブラリとして「1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF」を追加しておく必要があります。
  • OAuth2.0用のクライアントIDとクライアントシークレットを事前に取得しておく必要があります。こちらを参照してみてください。
  • startoauthで認証用ダイアログが出ます。あとは普通にログインすると、Access Token他が手に入りスクリプトプロパティに格納されます。

kintone APIへ送るコード

//OAuth2.0認証でPOSTする場合
function doGet(e) {  
  //受け取るパラメータを用意する
  var itemname = e.parameter.param1;
  var itemid = e.parameter.param2;
  var eventtype = e.parameter.param3;
  var username = e.parameter.param4;
  
  //kintoneへデータを送るURL
  var kinrecinsert = "https://ここにドメイン名.cybozu.com/k/v1/record.json";
  
  //リクエストボディを組み立てる
  var payload = {
    "app": "481",
    "record": {
        "boxfileid": {
            "value": itemid
        },
        "filename": {
            "value": itemname
        }, 
        "username": {
            "value": username
        }, 
        "action": {
            "value": eventtype
        }, 
    }
  }
  
  //OAuthサービスを取得
  var service = checkOAuth();

  //UrlfetchAppでアクセス
  var res = UrlFetchApp.fetch(
    kinrecinsert,
    {
      method:"POST",
      payload:JSON.stringify(payload),
      headers: {
        "Authorization": "Bearer " + service.getAccessToken(),
        "Content-Type": "application/json"
      },
      muteHttpExceptions:true
    }
  )
}
  • Basic認証の時と同様、doGetでBox側からのパラメータを受け取ります
  • 必ずウェブアプリケーションとして公開し、アクセスは誰でもできるようにしておく必要があります。スプレッドシートは非公開でOK.
  • AuthorizationにてAccess Tokenを付けてkintone REST APIへと送り込みます。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)