やりたいことはタイトルの通りなので、前置きは飛ばしても良いです。
背景、前置き
Google API Client Library を使ってGoogle Calendar いじるアプリを書いていた。Google Cloud Platform からOAuth2のクライアント情報を client_secret.json
として落としてきて、その ファイル名 を oauth2client.client.flow_from_clientsecrets()
の引数に指定する。
ここで問題。セキュリティ上の問題でgitのバージョン管理に client_secret.json
を加えたくない。かといって Heroku でアプリを動かしているので、ファイルをアップロードできない。(Amazon S3などのオブジェクトストレージを使えば良いらしいのだが、使ったことがなかったのでめんどくさいのでやめた)
ようするに、 client_secret.json
を指定しなければならないのに、そのファイルをHerokuサーバーに置けなくて困ったということである。
ということで、 oauth2client.client.flow_from_clientsecrets()
関数に ファイル名
ではなく辞書型データを入れるようにしたい。そしてHerokuの環境変数に client_secret.json
の内容をそれぞれ格納して、それを辞書データに落とし込めば良い。
(環境変数に機密情報入れるの良くないってマジ?)
追記
普通に OAuth2WebServerFlow
のインスタンスを作れば良かったということにコード書いてから気づきました。よって別にオーバーライドなんてしなくて良いのですが、オーバーライドこうやってできるんだねってことで知見になったので残しておきます。
やりたいこと
oauth2client.client.flow_from_clientsecrets()
関数に ファイル名 ではなく 辞書型 を入れるようにしたい。
どうしたか
ライブラリのソースコードはこんな感じだった。
(以降は、Google Inc. のApache license コードより抜粋)
oauth2client.client.py
より一部省略。
@_helpers.positional(2)
def flow_from_clientsecrets(filename, scope, redirect_uri=None,
message=None, cache=None, login_hint=None,
device_uri=None, pkce=None, code_verifier=None):
try:
client_type, client_info = clientsecrets.loadfile(filename,
cache=cache)
if client_type in (clientsecrets.TYPE_WEB,
-----以下略------
注目するのは filename
が clientsecrets.loadfile()
に渡されているということ。
さらに clientsecrets.loadfile()
を見てみた。
def loadfile(filename, cache=None):
_SECRET_NAMESPACE = 'oauth2client:secrets#ns'
if not cache:
return _loadfile(filename)
obj = cache.get(filename, namespace=_SECRET_NAMESPACE)
-----以下略------
cache
が存在しない場合、 _loadfile
関数に処理を移譲してる。 _loadfile
を見てみよう。
def _loadfile(filename):
try:
with open(filename, 'r') as fp:
obj = json.load(fp)
except IOError as exc:
raise InvalidClientSecretsError('Error opening file', exc.filename,
exc.strerror, exc.errno)
return _validate_clientsecrets(obj)
ファイルをロードしてる関数にたどり着いた。この関数をオーバーライドすれば filename
引数に辞書型をぶちこめるのではないか。
ということで以降は自分のコード
# `client.flow_from_clientsecrets` 関数が引数に辞書型を取れるように
# `clientsecrets._loadfile` 関数をオーバーライドする
def new_loadfile(data):
return clientsecrets._validate_clientsecrets(data)
clientsecrets._loadfile = new_loadfile
こうすれば以降
flow = client.flow_from_clientsecrets(
辞書型,
scope='https://www.googleapis.com/auth/calendar',
redirect_uri=flask.url_for('oauth2callback', _external=True))
のように 辞書型を引数に取る flow_from_clientsecrets
が使えるようになった。
まとめ
def new_func(args):
# いろんな処理
return hoge
hoge_library.func = new_func
こうすれば簡単にオーバーライドできて、その関数を使う関数全てに影響するんだね。
ライブラリ関数のオーバーライド初めてやったけど、こんな簡単にちゃんと動くんだなー。怖いけど。