django: decoratorでスタティックファイルのアクセスコントロール

synopsis: urls.pyにちょっとデコレータをふりかければ、djangoのユーザ管理機能を使ってスタテックファイルのアクセスをコントロールできるというレシピ。

djangoの前に座っているnginxのアクセスログのレポートなどはdjangoの外のものなので、下のようにnginxの設定でbasic authを使って守っていた。


location /log/ {
alias /var/www/tengu.us/;
auth_basic "tengu.us";
auth_basic_user_file passwd/tengu.us.htpasswd;
}

しかし、htpasswdの管理するのが面倒臭い。また見る方もユーザとパスワードをもう一つ覚えなくてはいけない。djangoには優れたユーザ管理機能がるのに、なんてナンセンスなことだ。というわけで、djangoのスタティックファイルサーバを使いアクセス管理させる。

プロダクションではスタティックファイルはdjangoに扱わせないのがパフォーマンス上のきまりだが、これはプロジェクト関係者だけが見る、一日数ヒットのurlなので問題はない。

django.contrib.auth.decoratorsにはログインをさせるデコレータlogin_requiredがある。さらに細かい権限をチェックするpermission_requiredもあるが、権限の設定が面倒なんで、スタッフ権限を求めるデコレータを書く。それをurlpatternsのハンドラーに被せる。

urls.py コードスケッチ

# ログインさせるdecoratorが必要
from django.contrib.auth.decorators import login_required

...

# decoratorは実際の関数を対象にしているので、ハンドラーをインポート。
# 'django.views.static.serve'のように文字列で定義しないから。
import django.views.static

...

urlpatterns = patterns(
'',
# nginx accesslogのレポートディレクトリを/log/としてアクセスできるようにする。
(r'^(?Plog/.*)$',
# ログインと権限チェックのdecoratorを入れ子状にしてstatic.serveを守る
login_required(staff_required(django.views.static.serve)),
{'document_root': settings.WWW_ROOT}),
...
)

def staff_required(view):
"""
django.contrib.auth.decoratorsにありそうでないデコレータ。
user.is_staffでないと弾く。これは/admin/以外、アプリケーションが持つ
管理画面にも便利だね。
"""
def wrapper(request, *args, **kw):
# precondition: user must be authenticated
assert not request.user.is_anonymous()
if request.user.is_staff:
return view(request, *args, **kw)
from django.http import HttpResponseForbidden
return HttpResponseForbidden('スタッフじゃないと入れないよ')
return wrapper

これで、スタッフ一括管理でnginxログなど見せられる。このレシピはdjangoにstaticファイルを送らせるので、一般ユーザ向けでなく内輪だけのアクセス数が少ないurlだけにあてはまるものなんで注意してください。