Google App EngineのアプリケーションにBasic認証を設定する

最近仕事で Google App Engine を使う機会が増えてきてて、今更ながらまともに使い始めました(公開初日から触ってたのになー
作りかけのアプリケーションで、とりあえずアップロードするけど、公開までは全体にBasic認証かけときたいなーなどということが多い。
ググればやり方はいくつか出てきたけど、良さそうなのがなかったので自分でも書いてみた。

basicauth.py

AUTH_RESPONSE_BODY = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>401 Authorization Required</title>
</head>
<body>401 Authorization Required</body>
</html>"""

def auth_required_app(environ, start_response, realm):
    start_response('401 Authorization Required', [
        ('WWW-Authenticate', 'Basic realm="%s"' % realm),
        ('Content-Type', 'text/html; charset=iso-8859-1'),
    ])
    return AUTH_RESPONSE_BODY

class AuthMiddleware(object):
    def __init__(self, application, realm, username, password):
        self.application = application
        self.realm = realm
        self.username = username
        self.password = password

    def get_token(self):
        import base64
        return base64.b64encode('%s:%s' % (self.username, self.password))

    def get_auth_basic_header(self):
        return 'Basic %s' % self.get_token()

    def __call__(self, environ, start_response):
        auth_header = environ.get('HTTP_AUTHORIZATION')
        if auth_header and auth_header == self.get_auth_basic_header():
            return self.application(environ, start_response)
        return auth_required_app(environ, start_response, self.realm)

AuthMiddlewareがWSGIミドルウェアになってます。

使い方

AppEngine Launcherの[Create New Applicaiton...]で作成したアプリケーションに適用してみる。
1. 上記の basicauth.py をプロジェクトディレクトリに置く

2. プロジェクトディレクトリにappengine_config.py を作成して次のように記述する

appengine_config.py
import basicauth

def webapp_add_wsgi_middleware(app):
    return basicauth.AuthMiddleware(app, realm='secret', username='hoge', password='fuga')

realmにレルム、usernameにユーザ名、passwordにパスワードを任意に設定する。
これでLanucherから起動してアクセスしてみると、認証のダイアログが出るようになりました。

2011/09/01追記

これを使ったことにより、taskqueueやcronが動かなくてハマってる人がいたので追記。
taskqueueやcronはAppEngineのインフラからHTTPリクエストが投げられるので、この記事のコードをそのまま使うと、各エントリポイントが401のレスポンスを返してしまって動かないのでご注意ください。
暇ができたらその辺をうまく回避するようなコードに修正するつもり。