15
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SecureXの承認ワークフローをチャットで実現する仕組みを考えてみた

Last updated at Posted at 2022-12-12

この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2022の一つとして投稿しています。

2022年版: https://qiita.com/advent-calendar/2022/cisco

以下、昨年までのものです。

2021年版(1枚目): https://qiita.com/advent-calendar/2021/cisco
2021年版(2枚目): https://qiita.com/advent-calendar/2021/cisco2
2020年版(1枚目): https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2
2019年版: https://qiita.com/advent-calendar/2019/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2017年版: https://qiita.com/advent-calendar/2017/cisco

やったこと

ワークフローを開始すると、承認を求めるメッセージがWebex Messagingに届き、Wait For Eventのブロックでワークフローがいったん停止します。承認することでワークフローが再開し、後続する処理が実行されます。

securex-approval.gif

モチベーション

既存のサンプルだとSecureXのWebインターフェイス上で承認するものがあります。
承認者にいちいちSecureXにログインしてもらうのも何なんでチャットで承認できる方法を考えてみました。

ワークフローの全体像

image.png

  1. Create Approval Request
  2. HTTP Request
  3. Wait For Event
  4. Completed

以下、SecureXのワークフローだけでなくWebexクラウドや外部Webサービスとの連携も加えて、順をおって解説します。

1. Create Approval Request

ワークフローを開始するとSecureX上に承認リクエストが作成されます。リクエスト毎にランダムなTask IDが生成されます。各サービス間で連携する際にこのTask IDを引き回すことで、どのリクエストへの承認もしくは拒否がなされたのかの判断できるようにします。

2と3で行われる処理の全体像

image.png

① HTTPリクエストが呼ばれるとWebex API経由でWebex MessagingクライアントにAdaptive Cards形式のメッセージが送信されます。

② 承認者がWebex Messagingクライアント上で「承認する」もしくは「拒否する」のボタンを押すと、WebexクラウドからWebhookが呼ばれます。

③ 承認者によるアクションの詳細を得るためにWebhookの中のIDをみてWebex API経由でアクションの詳細を取得します。

④ SecureXのワークフローが承認待ちで停止状態なので、Wait For Eventに紐づいているSecureX Webhookを呼びだし、ワークフローを再開させます。

⑤ 最後に、承認者をメンションして承認したのかもしくは拒否したのかのメッセージをスレッドに残します。

Webexの前準備

Webex MessagingのAPIを通じて、チャットベースの承認リクエストを送ったり、承認者から承認もしくは拒否のアクションを得ます。

image.png

そのためWebex MessagingのBotとWebhookを作成しておきます。
WebhookはBotのWebex Tokenで作成します。

Webex MessagingにおけるBotとWebhookの作成には以下の記事をご参考にしてください。

Webhookのパラメータは以下のとおりです。

name: Webhook for attachment actions(任意)
targetUrl: https://{my_app}.{my_subdomain}.workers.dev
resourace: attachmentActions
event: created

2. HTTP Request

主な設定項目は以下のとおりです。

Target

image.png

Target

Webex Teams

HTTP Request

image.png

Relative Url

/v1/messages

Method

POST

Request Body

roomIdはWebexのルームIDで変数で渡してあげます。
どういったリクエストへの承認なのか分かるようにCreate Approval Requestで生成されたTask IDをdataに埋め込んでおきます。

{
  "roomId": "<webex-roomId>",
  "text": "Give me your approval",
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.card.adaptive",
      "content": {
        "type": "AdaptiveCard",
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "version": "1.2",
        "body": [
          {
            "type": "Image",
            "url": "https://4.bp.blogspot.com/-gKPdnJWscyI/VCIkF3Po4DI/AAAAAAAAmjo/fAKkTMyf8hM/s150/monster01.png",
            "horizontalAlignment": "Center",
"width": "150px",
            "height": "150px"
          },
          {
            "type": "Container",
            "style": "emphasis",
            "items": [
              {
                "type": "TextBlock",
                "wrap": true,
                "text": "承認欲求モンスターが承認を求めています",
                "spacing": "None",
                "size": "Large",
                "weight": "Bolder",
                "color": "Accent",
                "isSubtle": false
              }
            ]
          },
          {
            "type": "ActionSet",
            "actions": [
              {
                "type": "Action.Submit",
                "title": "承認する",
                "id": "1",
                "data": {
                  "taskId": "<task-id>",
                  "action": "Approved"            
                }
              },
              {
                "type": "Action.Submit",
                "title": "拒否する",
                "id": "2",
                "data": {
                  "taskId": "<task-id>",
                  "action": "Rejected"
                }
              }
            ]
          }
        ]
      }
    }
  ]
}

Headers

image.png

Content Type

Application JSON

Custom headers

HEADER: Authorization
VALUE: Bearer <webex-bot-token>

Webex MessagingでAdaptive Cards形式のメッセージを送ったり、カードへのアクションを取得する方法については、以下のサイトを参考にしてください。

[Webex公式] Buttons and Cards
https://developer.webex.com/docs/api/guides/cards

WebexからのWebhookを受けるWebサービスの作成

Adaptive Cards形式のメッセージにボタンを押した際にWebhookが呼ばれます。
そのWebhookを受けるのに今回はCloudflare Workersを使いました。
インターネット上にあってWebex Cloudから到達性があれば、AWS Lambdaとかで用意しても構いません。

Cloudflare Workersの使い方は以下の投稿を参照ください。

Cloudflare Workersで動作させるアプリケーションの雛型を作成します。

$ wrangler generate securex-approval
$ cd securex-approval

Webhookが呼ばれたときの処理をindex.jsに編集します。

Webexクラウドから呼び出されるWebhookのリクエストBodyからIDを取得して、Webex API経由でアクションの詳細を取得します。
Task IDと承認もしくは拒否が分かりますので、以下のようにその内容を埋めて、今度はSecureXのWebhookを呼び出します。

{
    "taskId": "021RPLBIJIKPO1mfxQsiRySzeViIdnstv6p",
    "action":"Approved"
} 

最後に、承認者をメンションして承認したのかもしくは拒否したのかをスレッドに残します。

index.js
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})
/**
 * Respond with hello worker text
 * @param {Request} request
 */
async function handleRequest(request) {
  const body = await request.json();
  const resp = await fetch(`https://webexapis.com/v1/attachment/actions/${body.data.id}`, {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${WEBEX_TOKEN}`
    }
  })
  const data = await resp.json()
  console.log(data)

  // call securex webhook

  await fetch(SECUREX_WEBHOOK, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ taskId: data.inputs.taskId, action: data.inputs.action }),
  })

  await fetch('https://webexapis.com/v1/messages/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${WEBEX_TOKEN}`
    },
    body: JSON.stringify({ parentId: data.messageId ,roomId: data.roomId, markdown: `<@personId:${body.data.personId}> You ${data.inputs.action}!` }),
  })

  return new Response('{}', {
    headers: { 'content-type': 'application/json' },
  })
}

Webex Bot Tokenをシークレット変数として登録します。

$ wrangler secret put WEBEX_TOKEN

SecureX Webhookをシークレット変数として登録します。

$ wrangler secret put SECUREX_WEBHOOK

アプリケーションを公開します。

$ wrangler publish

SecureX Webhookの作成

SecureXオーケストレーションのトップ画面に戻り、左ナビのEvents & WebhooksからWebhookタブに遷移して、New Webhookボタンをクリックします。

下のようなWebhookの作成画面がでてきたら、Display Nameを入力して、右下のSubmitボタンをクリックします

image.png

3. Wait For Event

主な設定項目は以下のとおりです。

Event Info

image.png

Event Type

直前に作成したWebhookを指定

Conditions

Cloudflare Workers上のWebサービスからのSecureXのWebhookへのリクエストBodyにTask IDと承認(Approved)もしくは拒否(Rejected)の情報が含まれるので、Task IDと承認(Approved)の文字列が正規表現マッチすることを条件にします。

4. Completed

本来であれば、承認後になんらかの処理を実行するはずですが、今回の例では何もせずにワークフローを終了させてます。

課題

面倒くさいので 過度に複雑な実装にするより分かりやすさを優先して以下の点は妥協しました。

  • Create Approal Taskを使ったため、SecureX上にもタスクができてしまいます。仮にSecureX上で承認してもワークフローは再開しません。

  • 承認リクエストがタイムアウトした後もチャットから承認できてしまいます。この場合もワークフローは再開しません。

  • いったん拒否した後に承認してもワークフローが再開します。

おしまい

15
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?