結局 request に突っ込むのがいいみたい。
id:mopemope さんがいろいろ考えてくださったみたいなので、改めて Django の設計思想についていろいろ調べてみました。
結論から先に言うと、HttpRequest の次の属性にユーザとプロバイダを突っ込むことにしました。ちょっと行儀が悪いかなと思って HttpRequest をいじるのはあえて避けていたんですが、セッション・ミドルウェアでは session って属性を勝手に定義しているわけで、それならうちも、ということで。
- auth_user
- auth_provider
あ、ちなみにプロバイダというのは何かというと、ユーザ名に対応するパスワードのハッシュを提供するクラスです。なんでこんなまどろこしいことするの?っていう人もいるでしょうけど、これは、いいんです。
class Provider: manager = None realm = None def __init__(self, manager, realm): self.manager = manager self.realm = realm def __getitem__(self, key): if key[1] <> self.realm: raise KeyError('No such key: ' + repr(key)) try: return self.manager.get(username=key[0]).pw_hash except Exception, e: raise KeyError(e) def __setitem__(self, key, val): if key[1] <> self.realm: raise KeyError('No such key: ' + repr(key)) try: user = self.manager.get(username=key[0]) except Exception, e: raise KeyError(e) user.pw_hash = val user.save() def set_password(self, key, pw): self[key] = make_credential_hash(key[0], key[1], pw)
加えていくつかくだらないバグもあったので、それを直した物を以下に置きました。
http://voltex.jp/downloads/Mozo.Django.DigestAuth-0.1.tar.gz
django.contrib.auth ではデコレータを使ってますね。
django.contrib.auth.decorator より抜粋:
from django.contrib.auth import LOGIN_URL, REDIRECT_FIELD_NAME from django.http import HttpResponseRedirect from urllib import quote def user_passes_test(test_func, login_url=LOGIN_URL): """ Decorator for views that checks that the user passes the given test, redirecting to the log-in page if necessary. The test should be a callable that takes the user object and returns True if the user passes. """ def _dec(view_func): def _checklogin(request, *args, **kwargs): if test_func(request.user): return view_func(request, *args, **kwargs) return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, quote(request.get_full_path()))) _checklogin.__doc__ = view_func.__doc__ _checklogin.__dict__ = view_func.__dict__ return _checklogin return _dec login_required = user_passes_test(lambda u: u.is_authenticated()) login_required.__doc__ = ( """ Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. """ )
で、これを使うには次のようにするというわけですね。
django.contrib.auth.views.py:70:
def password_change(request, template_name='registration/password_change_form.html'): .... password_change = login_required(password_change)
なんだろう、Java のフレームワークでいうと AOP つかってインターセプトしているような感じでしょうか。
「django.utils.decorators の decorator_from_middleware をつかえばキミのミドルウェアをデコレータにできちゃう!」みたいなので、これを使ってみるのもよさそう。