Apacheのmod_auth_mellonでAzureADとSSOしてFlaskで応答表示したら日本語が文字化けして大変だったけど力業で何とかなった気がした話

はじめに

前回の続きです。
Apacheのmod_auth_mellonを利用したSAML認証を実装し、Flaskでページ表示ができました。
SAML応答を利用するため、Flaskに雑に表示してみたら日本語が文字化けして、文字化けをどうにかする寄り道をした話です。もっとスマートなやり方があると思いますが、力業で何とかした気になった記事となります。
誰かもっとスマートな解決方法を教えてください…

とりあえず表示させたコード

aap.py

from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def display_env():
    return str(request.environ)
if __name__ == "__main__":
    app.run()

MELLON_xxxxという環境変数を簡単に取得できたので満足したのですが、セキュリティグループを返す設定をした時の日本語表示が文字化けしました。
いったいどういう事なんだ…

【余談】セキュリティグループを返す設定

AzureADで作成したアプリのSAMLの設定で「属性とクレーム」を編集し追加しました。

グループ要求を追加する
アプリケーションに割り当てられているグループを選択して、ソース属性はクラウド専用グループの表示名としています。

文字化けの対応

AzureADのSAMLで応答のあるXMLは、cp1252を利用しているようなのですが、(要出典)受け手のApacheではUTF-8で解釈しようとして文字が化けている感じの予想をしました。
もう本当に原因がわからなくて紆余曲折し、結局Pythonで強引に文字化けを直す手段としました。

表示してほしい文字列

ラディウスアクセス許可グループ
Azure側で作成したセキュリティグループの表示名です。

初めの対応

ひとまず文字化けした項目のみ表示するようにしました。またcp1252でエンコードして、UTF-8でデコードしたものを表示するように変更してみました。エンコードデコードでエラーがでるので、backslashreplaceオプションを付けています。

from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def display_env():
    return str(request.environ['MELLON_http://schemas.microsoft.com/ws/2008/06/identity/claims/groups_1']).encode('cp1252', errors="backslashreplace").decode('utf-8', errors="backslashreplace")
if __name__ == "__main__":
    app.run()

結果の出力です。

なぜかだけ化けずに表示されていますが、他の文字は化けていました。この辺でもう本当によくわからなくなりました。。

次に取った対応

単純にもっとスマートな項目の取得方法があったので、そのまま表示させてみました。

from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def display_env():
    return request.environ.get('MELLON_http://schemas.microsoft.com/ws/2008/06/identity/claims/groups_1', '')
if __name__ == "__main__":
    app.run()

出力です。

return str(request.environ)をした時と同じ文字が出ると思いましたが、別の化け方で表示されました。本当に意味が解りません。

最後に試したコード

latin-1でエンコードして、UTF-8でデコードしたものを表示するように変更してみました。

from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def display_env():
    value = request.environ.get('MELLON_http://schemas.microsoft.com/ws/2008/06/identity/claims/groups_1', '')
    encode_str = value.encode('latin-1',errors='backslashreplace').decode('utf-8')
    return encode_str
if __name__ == "__main__":
    app.run()


おー!表示された!
誰だバベルの塔作った奴。

さいごに

やりたい事とは関係ない、見た目の処理にとにかく時間がかかりました。この記事では3パートで出来たように書いていますが、かなりトライアンドエラーを繰り返しています。2バイト文字が忌避される理由を身をもって体感。
ここからは本筋に戻り、SAML応答の値を利用してLDAPにユーザやグループを参照/追加する処理を考えたいと思います。

  • 検索してなければユーザ/グループ作成
  • グループにユーザを追加