T-DASHでメールの二要素認証を突破してみた

## この記事関して
テスト自動化ツールT-DASHでメールの二要素認証を突破するカスタム動作を作成しました。
下記のような認証コードを基にログインしたい場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/a96d4886-2745-7706-b378-5e9cebee530b.png)

## 作成したテストシナリオ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/4fb28097-1942-884b-e16c-cafc60ff83db.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/05d29b58-9d86-66f2-cf4e-48e03c56b957.png)

## 今回の作成したSyncファイル

[mail_sync.zip](https://github.com/valtes-rd/t-dash/releases/download/Qiita/mail_sync.zip)

■参考
[プロジェクトファイルをインポートしよう](https://service.valtes.co.jp/t-dash/function/tutorial/import_export_projectfiles_vol_001)
[カスタム動作ファイルをインポートしよう](https://service.valtes.co.jp/t-dash/function/tutorial/import_export_custombehavior_vol_001)


## 最新のメールの本文を取得する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/86303457-9a33-784f-2b20-d5606fc5ed98.png)


```Python:MailLibrary.py

def get_email_body_python(imap_server, username, password):
    """
    最新のメールの本文を取得する。

    Args:
    imap_server (str): IMAPサーバーのアドレス
    username (str): ユーザー名
    password (str): パスワード

    Returns:
    str: メール本文
    """
    # IMAPサーバーに接続
    mail = imaplib.IMAP4_SSL(imap_server)
    # ログイン
    mail.login(username, password)
    # メールボックスを選択
    mail.select("inbox")
    # すべてのメールのUIDを取得
    status, messages = mail.uid('search', None, "ALL")
    mail_ids = messages[0].split()
    # 最新のメールを取得する場合
    latest_email_uid = mail_ids[-1]

    # メールのUIDを使用して、メールのデータを取得
    status, msg_data = mail.uid('fetch', latest_email_uid, '(RFC822)')

    # メールをパース
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            msg = email.message_from_bytes(response_part[1])

            # メールのヘッダーをデコードして表示
            def decode_mime_words(s):
                return ''.join(
                    word.decode(encoding or 'utf-8') if isinstance(word, bytes) else word
                    for word, encoding in email.header.decode_header(s))

            subject = decode_mime_words(msg["Subject"])
            print("Subject:", subject)

            from_ = decode_mime_words(msg.get("From"))
            print("From:", from_)

            # メールの本文を取得
            # メールがマルチパートの場合(HTMLメールや添付ファイルが含まれる場合)
            if msg.is_multipart():
                for part in msg.walk():
                    content_type = part.get_content_type()
                    content_disposition = str(part.get("Content-Disposition"))
                    # テキストの本文を取得
                    # text/plain かつ添付ファイルではないものを指定する。
                    if content_type == "text/plain" and "attachment" not in content_disposition:
                        payload = part.get_payload(decode=True)
                        if payload:
                            charset = part.get_content_charset()
                            if charset is None:
                                result = chardet.detect(payload)
                                charset = result['encoding']
                            try:
                                body = payload.decode(charset)
                                print("Body:", body)
                            except Exception as e:
                                print(f"Failed to decode part: {e}")
            # メールがマルチパートでない場合(テキストメールの場合など)
            else:
                payload = msg.get_payload(decode=True)
                if payload:
                    charset = msg.get_content_charset()
                    if charset is None:
                        result = chardet.detect(payload)
                        charset = result['encoding']
                    try:
                        body = payload.decode(charset)
                        print("Body:", body)
                    except Exception as e:
                        print(f"Failed to decode body: {e}")
    # ログアウト
    mail.logout()
    return body

```

## メール本文の内容を正規表現で成形する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/8a315387-bde3-0c43-b193-44c5706bcef1.png)

正規表現チェッカーなどでメール本文に対してどの値を抜き出したいか調整してください。
https://weblabo.oscasierra.net/tools/regex/

:::note warn
正規表現で\は2つ書く必要があります。Pythonに渡す際のエスケープの問題で
※T-DASH上では￥￥表示されます

■手順
「認証コード：(\\\d{6})」での正規表現でメール本文の内容を成形する

■OK
「認証コード：(\\\d{6})」

■NG
「認証コード：(\\d{6})」
:::


```Python:MailLibrary.py
def extract_pattern(mail_body, pattern):
    """
    メール本文から指定された正規表現パターンに一致する部分を抽出します。

    Args:
    mail_body (str): メール本文
    pattern (str): 抽出したい正規表現パターン

    Returns:
    str: 抽出された文字列または None
    """
    match = re.search(pattern, mail_body)
    if match:
        return match.group(1)
    else:
        BuiltIn().fail(f'正規表現 "{pattern}" に一致する値が見つかりませんでした。')

```

## 二要素認証情報を入力する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/70e152d4-29ed-fcb6-6dcb-0090a4371fdf.png)

``メール本文の内容を正規表現で成形する``で取得した値は``${6桁数字の二要素}``という変数に保持されるので、認証欄に入力する操作をしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742767/c59dda35-02c1-3919-0b89-a9b0cf6c5121.png)



## 最後に
**・robotframework-imaplibraryも使用してみましたが本文のエンコードがされないのでPythonで自作しました。**

**・IMAPのサーバー情報を公開していないメーラーも多いので汎用的には使いづらいかもしれません。(OutlookやGmailは公開されていないぽい・・)**


https://ricky.one/robotframework-imaplibrary/doc/ImapLibrary.html