ITの窓辺から

三流IT技術者の日常

DjangoでLDAP認証 その1

Djangoの認証についてすこーしだけ理解したのでその内容をメモ代わりに取りまとめてみます。長くなりそうかつまだ未検証事項が大量にあるので記事を分割していきます。

試した環境は以下の通り。
macOS(intel CPU) Big Sur
Python3.8.5 (venv環境)
Django3.2.5

また、これは解説記事ではなくDjango及びWebアプリケーション初心者によるメモ書きです。できる限り自分で納得した上で進めるようにはしていますが、ほとんど独学であることから内容の信頼性は低いと認識しています。記事を鵜呑みしないようにご留意ください。

当面の目的

主に以下の2個の要件を満たすことを目的としています。まずは1個目の要件を満たせるように頑張っていきます。

  • Active Directory上に格納されたユーザアカウントを使用してDjango上のアプリケーションを使用する際にLDAP認証によるユーザ認証を行う。ユーザはアカウントが存在するだけでなく特定のセキュリティグループに属している必要がある。
  • ユーザアカウントに付与されているセキュリティグループによってアプリケーションの操作権限を変えられるようにする。

意味わからんと思ったもの

Djangoの認証を勉強し始めて、何言ってるのか分からん、となったことがいくつかあります。

  • デフォルトのユーザーモデルを使うことは非推奨
  • 認証バックエンドをカスタマイズする時、カスタマイズ用の認証用コードにauthenticateとget_userメソッドが必須

とりあえず??????な状態ですが、何が分からないのか考えてみたので一つずつ書いてみます。この記事では1個目の疑問に対して記載します。

デフォルトのユーザーモデルを使うことは非推奨

公式ドキュメントやインターネットの記事をいくつか読むと、デフォルトのユーザーモデルを使用していると後から変更が必要になった時に大変らしいです。まぁあるあるだよね、という直感はあれど、具体的にどういうことなのか分かりません。

そもそもユーザーモデルって何だよというという状態です。公式ドキュメントの認証に関する事項を読んでも、いきなり始まるのはUserオブジェクトの説明でユーザーモデルの説明は出てこないように見受けられます。そしてユーザーモデルの説明はないまま、ドキュメントは「Djangoの認証方法のカスタマイズ」に進み、急にユーザーモデルが分かっていることを前提するかのような説明が始まります。ここで「?」マークが大量に発生しました。

デフォルトのユーザーモデルを使うことは非推奨だからカスタムユーザーモデルを作れと言われてもユーザーモデルが何なのか分かっていないと何をすれば良いのか分からない・・・。一応、とりあえずこれをやっとけ、というような例は示されているのですが内容が全くわかりませんでした。

〜authenticateとget_userメソッドが必須

長いので省略しました。ドキュメントを見ると、どちらもユーザーモデルを返すようです。またユーザーモデルかよ、と思ったことに加えて、どちらもユーザーモデルを返すと言われてもメソッドごとの違いは何なのよ、という疑問で頭が埋め尽くされました。

とりあえず、ユーザーモデルとは何なのかを理解しないとあまりにもブラックボックスが増えることが分かった。

ユーザーモデルが何なのか調べた

そういうわけで、ユーザーモデルが何なのかを調べてみました。
とりあえずモデルと名前がつくぐらいなので、Djangoのアプリ内でいうModelのことかと当たりをつけました。幸いにもこれはビンゴだったようです。(当たり前とか言わないで・・・)

Djangoの認証は結局Djangoアプリケーションの一つで、はじめから組み込まれているもののようです(そもそもこの認証の仕組みをカスタマイズする方法はあるのだろうか、と思ったりもするところ)。startprojectした時に生成されるsettings.pyのINSTALLED_APPSに含まれている「django.contrib.auth」がそれです。これは単純にPythonのモジュールサーチパスのようなので、venvのlibディレクトリ内からこれを探しました。するとcontrib > authディレクトリの中にmodels.pyがあるではありませんか。

このmodels.pyを見ると、AbstractUserというクラスがあり、その中にusernameとかfirst_nameとかそれっぽいデータベースカラム定義があります。さらに既に作成済みのDjangoプロジェクトのデータベースを見るとauth_userというテーブルがあり、このテーブルのカラム一覧とそのカラム定義が一致します。とりあえずこのAbstractUserというクラスがデフォルトのユーザーモデルの本質と考えて良さそうです。

もう少しmodels.pyを見るとUserというAbstractUserを継承しているクラスがありました。さらにクラスドキュメントとしてDjangoの認証システムはこのモデルによって表現される、めいたことが書いてあります。ということから、ユーザーモデルの本質はAbstractUserであっても実際に認証システムが使用するModelはUserのように思えます。しかしUserクラスの中身のMetaクラスに書かれているswappableという変数が何なのかは不明・・・。

ということでこの疑問に対する結論。
ユーザーモデルとはユーザ認証アプリケーションが使用するModelのこと。

ユーザーモデルのカスタマイズとは何なのか調べた

ユーザーモデルはユーザ認証アプリケーションが使用するModelなのだから、このModelを変更することがカスタマイズなのだろうと思われます。要するにAbstractUserクラスの中身を変更するということです。

しかし、実際に認証アプリケーションが使用するクラスはUserと思われるので、実際には新たにUserに相当するクラスを定義し、そのクラスをユーザ認証アプリケーションが使用するModelとして指定するのが正しそうです。実際にドキュメントでも以下のようにUserクラスを新しく定義しています。

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):

    pass

しかしここで新たに疑問が。
このコードってどこに書くの?元々のauthアプリのmodels.pyを書き換えるのか?ドキュメントには特に記載がない・・・。

理屈上は上記の通りでも良いのかもしれませんが、これはDjangoのライブラリ上のファイルを書き換えることになるわけで、このライブラリを使用する全てのDjangoプロジェクトに影響します。Djangoライブラリ1個あたり1プロジェクトでもよく考えるとあまり違和感はありませんが、デザインとしてはいまいち微妙な気がします。ということでもう少し調査しました。

ドキュメントには「AUTH_USER_MODELを指定するのを忘れないでください」と書かれています。AUTH_USER_MODELって何だよ。

ドキュメントによるとsettings.pyのパラメータらしいです。

「Userを表すために使うモデルです」

????
原文でも「The model to use to represent a User.」

????

皆こういうのってどうやって理解してるんですかね・・・。海外ブログとかだと大量に情報があるのだろうか。

ちょっと解読してみましょう。
説明の通り、AUTH_USER_MODELはModelと思われます。Userを表すために使うModelということは、ユーザーモデルを表すために使うModelです、ということだろうか。原文でもUserと大文字で始まっているので、一般名詞としてのユーザではなく固有名詞と思われるので文脈的にはユーザーモデルと考えて間違いないような気がします。ということで、無理やり読むと、ユーザーモデルを指定するための変数、と読めないだろうか。

実際にコードを見てみようと思いましたが、settings.pyにAUTH_USER_MODELがありません。ということで、django > conf > global_settings.pyの方を見たら書いてありました。値はauth.Userのようです。

公式ドキュメントにも説明がありました。先程のユーザーモデルのカスタマイズの少し上に書いてありました。その順序は分かりやすいのか?
とりあえずAUTH_USER_MODELには「アプリ名.ユーザーモデル名」と指定する作法のようです。つまり「auth.User」というのはauthアプリのUserというユーザーモデルという意味で、AUTH_USER_MODELにこの値が指定されているということは、Djangoが使用するユーザーモデルはauthアプリのUserというModelということになるようです。(くどい)

記述が長くなりました。
カスタマイズしたユーザーモデルをどこに書くのか、という問題に戻ります。AUTH_USER_MODELにはアプリ名とモデル名が必要なのだから、ユーザーモデルを定義するためのDjangoアプリケーションを作成し、そのアプリのmodels.pyにユーザーモデルを書くということ、と理解しました。

そしてAUTH_USER_MODELを指定するということは、Djangoの認証アプリケーションが使用するユーザーモデルを指定すること、と理解しました。

自分でユーザーモデルを作らないといけないのか?中身考えるの面倒なんだが?と思ったのですが、ドキュメントにはとりあえずUserの中身はpassで良くて、AbstractUserを継承しておけばとりあえず良いようです。つまり、デフォルトのUserクラスに書かれている変数swappableが密結合状態にしてしまうのでしょうか。(何と何が密結合なのかも分かっていない)

ということで、この疑問(ユーザーモデルのカスタマイズってなんなの?)の結論。
ユーザーモデルのカスタマイズとは、新しくユーザーモデルを作り、それをDjangoの認証アプリケーションが使用するように指定すること。

そしてそのために必要な作業は以下の通り。

  1. 新しくDjangoアプリケーションを作り、その中でユーザーモデルを作る。そのユーザーモデルは要件が特になければAbstractUserを継承した上で内容はpassで良い。(settings.pyのINSTALLED_APPSの追加も行う)
  2. settings.pyでAUTH_USER_MODELの値に上記のユーザーモデルを指定する。
  3. この記事では書いていませんが、Django管理画面向け設定が必要なようです。新しく作ったDjangoアプリケーションのadmin.pyを公式ドキュメントの通りに書く。

新しくDjangoアプリケーションを作り、と書きましたが既存アプリケーションにユーザーモデルを作っても良いのでしょうね。

既存のユーザーモデルでは要件を満たせないって何?

そもそもなんですが、これってユーザー情報にAbstractUserで規定されていない値が必要な時、って意味ですよね??

次回

この記事では冒頭付近に書いた2個の大疑問のうち1個目について書きました。しかし、これ単独で理解してもとりあえずおまじないとして内容がpassのユーザーモデルを新しく作っておけ、ということが分かった以外何も進展していないんですよね。ということで、次回は認証バックエンドについて調べたことを書きます。