はじめに
こんにちは。新卒一年目、APIチームの成田です。
今回はSaaSアカウント、特にGitHubアカウントの管理についてのお話です。 社内でのSaaSのアカウント管理は非常に重要であり、もしサボってしまうと、 辞めたはずの人が社内情報にアクセスできてしまう、というような事態にもなりかねません。(*1)
日経では社員や協力会社の方々の各SaaSアカウントをデータベースで管理しています。 下図のように、氏名、会社名、Emailアドレスを持った親アカウントをつくり、 その下に、Slack、nulab、kibela、GitHub等のアカウントを紐づけています。 このように紐づけておくと、退職時のアカウント削除処理の自動化なども可能になります。

現在では、まず最初にSlack招待時に下図のようなフォームを利用させ、この情報から親アカウントを作っています。 各SaaSのアカウントは各APIで取得し、EmailアドレスをKeyとして紐づけます。

しかし、この方法ではGitHubのアカウントを紐づけるのは困難です。 なぜなら、私用Emailアドレスを使用しているアカウントが多いうえ、 そもそもEmailアドレスがAPIで取得できない場合がほとんどだからです。 さらに、アカウント名から本名を連想できないケースが多く、管理者が人力で紐づけることすら難しくなっています。
そのため、詳しい人に「これ誰?」と聞く必要があるのですが、これを人の手でやるのは大変で、 溜まれば溜まるほど面倒なことになる(実際なった)ので、これを自動化しようというのが、今回のエントリーです。
こんなのつくりました
GitHubにアカウントが追加されると、リアルタイムで下図のようなメッセージが送信されます。 メンションされる人は招待を行ったユーザーです。

メンションされた人を含め任意の人が、追加されたユーザーのSlackアカウントを下図のように選択することで、 Slackアカウントと同じ親アカウントにGitHubアカウントが紐づけられます。

もしなければ下図フォームで氏名等の情報を入力することで、その場で新しい親アカウントを作成し、 その下にGitHubアカウントが紐づけられます。

紐づけが完了すると、メッセージが下図のように書き換えられ、ログとして参照出来るようになります。

概観
概観は下図のようになります。今回データベースにはMySQLを利用していますが、そこまで大掛かりにせず、 Google Spreadsheetでも代用可能です。

まず、GitHub OrganizationのWebhookを利用して、アカウント追加情報をリアルタイムにサーバに送ります。
hookするイベントはOrganizatios/member_added
イベントです。Outside collaboratorの追加も見たいときは
Collaborator add, remove, or changed/added
をhookします。
ただし、このイベントはcollaboratorを新しくレポジトリに追加するたびに発火するので、サーバーでうまく処理する必要があります。
送信されるpayloadには追加ユーザーのGitHub IDや、招待したユーザーのGitHub IDが含まれています。
イベント情報を受信したサーバーは、まずデータベースに招待したユーザーのSlack IDを問い合わせます。 そのため、少なくとも招待する権限を持ったユーザーのGitHub IDとSlack IDの紐づけは完了している必要があります。 取得したSlack IDと追加ユーザーのGitHub IDを利用してメッセージをSlackにPOSTします。 Slack側で紐づけが行われると、サーバーにそのSlack IDまたは氏名、会社名、Emailアドレス情報が送信されます。 情報を受信したサーバーはそれをデータベースに書き込み、Slack側にログを送信します。
Slack Botの詳細
今回、投稿フォームには今年新しく導入されたメッセージUIフレームワーク、Block kitを使っています。 Block kitは画像、テキスト入力フォーム、date picker、ボタンなどの要素を積み重ねて、 リッチなメッセージを送信することが出来ます。
今回の例では下図のような簡単な構成となっており、以下のようなpayloadをSlackにPOSTします。
blocks
に使いたい要素の配列を渡します。

{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "hogehoge"
},
...
},
{
"type": "actions",
"elements": [
{
"type": "users_select",
"action_id": "select_xxx",
...
},
{
"type": "button",
"action_id": "bind_xxx.",
...
},
],
...
},
{
"type": "actions",
"elements": [
{
"type": "button",
"action_id": "modal_xxx",
...
}
],
...
}
]
}
ユーザーを選ぶ、ボタンを押す等のactionを行うと以下のようなpayloadがSlackからサーバーに送信されます。
response_url
はメッセージを更新するために、trigger_id
はmodalと呼ばれる入力フォームを送信するために利用します。
{
"type": "block_actions",
"user": {
"id": "U0XXXXX",
...
},
"trigger_id": "123646734323.XXXXXXX",
"response_url": "https://www.xxxxxx",
"actions": [
{
"action_id": "hoge_xxx",
...
}
]
...
}
ますSlack IDを紐づける場合を考えます。
users_select
でworkspace内のユーザーを選択し,
隣のbutton
を押すことで、サーバー上で紐づけが行われ、メッセージ更新用payloadがSlackにPOSTされます。
button
を押すときのpayloadには何もしないとusers_select
で選んだユーザーの情報は送られないので、
users_select
でユーザーを選ぶたび、その情報をaction_id
に変数として格納しておきます。
pythonならば例えば以下のようにaction_id
を利用できます。
# 送信時
action_id = f"bind_{selected_user}"
# 受信時
selected_user = res.json()["payload"]["actions"][0]["action_id"].split("_", 1)[1]
一番下のbutton
を押すと、入力フォームが立ち上がりますが、
これはmodalと呼ばれる機能で、これ自体もBlock kitを使用します。
以下のようなpayloadをSlackにPOSTします。先ほどと同様に、blocks
として使用したい要素の配列を渡します。
{
"trigger_id": "123455666.xxxxxxx",
...
"view": {
"type": "modal",
"private_metadata": "https://www...(response_url)",
"blocks": [
{
"type": "input",
"element": {
"type": "plain_text_input",
"action_id": "name_input",
...
},
"label": {
"type": "plain_text",
"text": "氏名"
},
"block_id": "name"
},
{
"type": "input",
"block_id": "company",
...
},
{
"type": "input",
"block_id": "email",
...
}
]
}
}
今回はシンプルなテキスト入力フォーム、plain_text_input
を3つ利用しました。
またmodalにはprivate_metadata
というfieldが存在し、
このfieldにメッセージを更新するためのresponse_url
などの情報を格納出来ます。
紐づける
ボタンを押すことで入力された内容が以下のようなpayloadがサーバーに送信されます。
受け取ったprivate_metadata
のresponse_urlに新しいメッセージを送信することでメッセージの更新が出来ます。
実際には招待された人のGitHub IDなどもprivate_metadata
に保存して、メッセージの更新に使用します。
{
"type": "view_submissins",
"user": {
"id": "U0XXXXX",
...
},
"private_metadata": "https://www...(resppnse_url)",
"state": {
"values": {
"name": {
"name_input": {
"value": "日経 太郎",
...
}
},
"comapany": {
"company_input": {
"value": "日本経済新聞社",
...
}
},
"email": {
"email_input": {
"value": "hoge@example.com",
...
}
}
}
...
}
}
終わりに
今回はGitHubのアカウント管理方法の提案をさせていただきました。 Slackのapi周りの充実度は素晴らしく、このBlock kitの登場によってかなりかゆいところに手が届くようになりました。 今後のアップデートによってさらに使い方が増えるそうなので、楽しみです。
*1 SAML等で統一できるところは少しずつ進めていますが、運用上すぐに変更できなかったり、SAMLに対応していないサービスも結構あったりします