📊

Data Studioをセキュアに埋め込む方法を調べてみた

Leaner Technologies でプロダクトマネージャーをしている @corocn です。
今回は Data Studio をアプリに埋め込む方法を調べたのでブログにしてみました。

なぜ Data Studio の埋め込みを検討しているのか

  • プロダクトに分析機能の追加を検討している
  • いきなり開発すると大変なので、既存の BI ツールを上手く活用して楽して検証したい
  • 既存資産としてサポート用途で内部向けに作った Data Studio のレポートがある
  • 上記レポートを改変少なく活用したい

やりたいこと

  • Data Studio をログイン後のユーザー画面に埋め込みたい
  • 他のユーザー・組織のデータを見られないように保護したい

参考にした資料

https://developers.google.com/datastudio/solution/viewers-cred-with-3p-credentials?hl=ja

こちらの記事の次の一文が目に留まり、具体的にどういう仕組みで実現できるのか検証しました。

目的:サードパーティ製プラットフォームにダッシュボードを埋め込み、閲覧者が Google アカウントにログインしていなくても、関連するデータのみが表示されるようにします。

仕組み

トークンという言葉がでてくるので、検証前は以下のように、ユーザー単位でワンタイムトークンを生成して、Data Studio のビューをいい感じに保護した状態で埋め込めるのでは?と考えていました。

検証前の頭の中

しかし実際にドキュメントを読み解いてみると、Data Studio はパブリックな形で公開して埋め込み、Data Studio が利用しているプログラマブルなコネクターで無理やり認証を挟むという方法が提案されています。

現実

内部的には Data Studio のビューを保護するというよりか、データへのコネクタを保護するような形ですね。既存の BigQuery コネクタが使えないので、既存資産の流用が難しく、クエリも書き直しになりそうです。現実はつらい。

補足: Community Connector

Data Studio ではコネクタという形で、色んなデータソースとの連携を簡単に行える仕組みが提供されています。App Script を利用して、独自のコネクタを作成できます。これが Community Connector(コミュニティコネクタ)です。

Connectors

検証手順

どんな感じで動くのかざっと確認したので、検証手順を紹介しておきます。

2022 年 6 月時点では、公式ドキュメントが古く、サンプルが動かない状態になっているのでご注意ください。(使われていない証拠?)

  • コミュニティコネクタのコードラボ(チュートリアル)が古くて動かない
  • 埋め込みダッシュボードのパラメーターの渡し方のサンプルが古い

手順

  1. サンプルとして扱うデータソース
  2. AppScript でコネクタを自作する
  3. 作成したコネクタを Data Studio で使う
  4. Data Studio を Web アプリに埋め込む

ゴール

埋め込み元から token を渡して AppScript で扱えることを確認できたらゴールとします。 token を AppScript まで渡せられれば後はなんとでもなりそうなので、そこまでの検証に留めています。

1. データソース

サンプルとして https://jsonplaceholder.typicode.com/Users API を使います。

レスポンスの例
[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  }
]

2. AppScript でコネクタを自作

AppScript を新規作成し、以下のファイルを配置します。

コード.gs
var cc = DataStudioApp.createCommunityConnector();

function getAuthType() {
  var AuthTypes = cc.AuthType;
  return cc
    .newAuthTypeResponse()
    .setAuthType(AuthTypes.NONE)
    .build();
}

function getConfig(request) {
  var config = cc.getConfig();
  
  config
  .newTextInput()
  .setId('token')
  .setName('token')
  .setAllowOverride(true);
  
  return config.build();
}

function getFields(request) {
  var cc = DataStudioApp.createCommunityConnector();
  var fields = cc.getFields();
  var types = cc.FieldType;
  var aggregations = cc.AggregationType;
  
  fields.newDimension()
    .setId('id')
    .setType(types.NUMBER);
  
  fields.newDimension()
    .setId('name')
    .setType(types.TEXT);
  
  return fields;
}

function getSchema(request) {
  var fields = getFields(request).build();
  return { schema: fields };
}

function responseToRows(requestedFields, response, token) {
  return response.map(function(item) {
    var row = [];
    requestedFields.asArray().forEach(function (field) {
      switch (field.getId()) {
        case 'id':
          return row.push(item.id);
        case 'name':
          return row.push(item.name + " " + token);
        default:
          return row.push('');
      }
    });
    return { values: row };
  });
}

function getData(request) {
  var requestedFieldIds = request.fields.map(function(field) {
    return field.name;
  });
  var requestedFields = getFields().forIds(requestedFieldIds);
  
  var token = request.configParams.token;
  
  var url = ['https://jsonplaceholder.typicode.com/users']
  var response = UrlFetchApp.fetch(url.join(''));
  var parsedResponse = JSON.parse(response);
  var rows = responseToRows(requestedFields, parsedResponse, token);
  
  return {
    schema: requestedFields.build(),
    rows: rows
  };
}
appscript.json
{
  "dataStudio": {
    "name": "Sample Data",
    "logoUrl": "https://leaner.co.jp/wp/wp-content/themes/leaner-co-jp-2020/assets/images/common/header_logo.png",
    "company": "Leaner Technologies Inc.",
    "companyUrl": "https://leaner.co.jp/",
    "addonUrl": "https://leaner.co.jp/",
    "supportUrl": "https://leaner.co.jp/",
    "description": "sample",
    "sources": ["jsonplaceholder"]
  }
}

appscript.json に書かれた情報がコネクタの定義です。 本番公開するつもりがないので今回は適当に埋めていますが、空文字だと怒られるので全部埋めておきます。

appscript.json はデフォルトで表示されていませんが、次のメニューにチェックすると表示されます。

3. 作成したコネクタを Data Studioで使う

AppScript のメニューの "マニュフェストから配置" を選びます。

Deployments が表示されるのでリンクをクリックします。

token のパラメーターは仮で aiueo としておきます。
パラメータ「token」にチェックを入れて接続します。

このパラメータは、getConfig の次の記述によって実現されています。iframe の外側から URL 経由でパラメーターを渡して上書きする場合は、setAllowOverride が必要です。

config
  .newTextInput()
  .setId('token')
  .setName('token')
  .setAllowOverride(true);

レポートを作成で Data Studio のビューを作成します。

確認のため name と token を連結して表示するようにしておきました。

case 'name':
    return row.push(item.name + " " + token);
default:
    return row.push('');

テーブルが表示されました。 自作コネクタを使った表示の確認ができました。

4. Data Studio を Web アプリに埋め込む

埋め込みを試します。

埋め込み用のレポート URL からパラメータを渡せるように、レポート URL での変更を許可が必要です。

ds0.token という名前はパラメーターを渡すときのキーになるので覚えておきます。接続しているデータソースが増えると、ds1, ds2 ... となります。

次に、レポートの埋め込みを有効にします。

埋め込み URL の reporting/<reportID>/page/<pageId> を覚えておきます。

埋め込み元のサンプルコードが以下です。Vue です。次の記事を参考に URL を組み立てます。reportId, pageId, params あたりを適宜書き換えてください。

https://developers.google.com/datastudio/connector/data-source-parameters?hl=ja#set_url_parameters
index.vue
<template>
<iframe width='1280px' height='720px'
  style="border:0" allowfullscreen
  :src="srcUrl"
/>
</template>

<script lang="tsx">
import { defineComponent } from '@nuxtjs/composition-api'

export default defineComponent({
  setup() {
    const reportId = 'xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx' // FIXME
    const pageId = 'XXXXX' // FIXME
    const token = 'abcxyz'

    const params = {
      'ds0.token': token, // FIXME
    }

    const paramsAsString = JSON.stringify(params);
    const encodedParams = encodeURIComponent(paramsAsString)

    const srcUrl = `https://datastudio.google.com/embed/reporting/${reportId}/page/${pageId}?params=${encodedParams}`

    return {
      srcUrl
    }
  }
})
</script>

これで、token = ‘abcxyz’ を AppScript に渡すことができました。

ここから先は検証していませんが、残りは token を利用してフィルタリングの情報をアプリから取得しつつ BigQuery に SQL を投げつけるところを書けば、条件に応じたデータを引っ張ってこれそうですね。

まとめ

  • Data Studio をログイン後の画面に埋め込む方法を検証しました。
  • 公式ドキュメントが古いので試すときは注意が必要です。
  • 手順も若干面倒だったので、自前でサッと作ってしまったほうがいいかもしれません。

宣伝

Leaner Technologies ではデータ分析基盤が得意なエンジニアを募集しています!

https://careers.leaner.co.jp/engineering
リーナーテックブログ

Leaner Technologiesのテックブログです! 採用情報: careers.leaner.co.jp/

Discussion

ログインするとコメントできます