Djangoの認証にActive Directoryを利用する

# はじめに
- Active Directory対応の認証バックエンドを作成しました
- Active DirectoryのユーザーIDとパスワードで認証を行います
- ただし、認証以外の認可等は、対応していません
- なので、ごく基本的な仕組みのみの解説となります
- 参考URL https://docs.djangoproject.com/en/2.0/topics/auth/customizing/


# 前提
以下の条件で確認を行いました。

## Django on Docker
- OS: Ubuntu Server 18.04 LTS
- Python: 3.6.5
- Django: 2.0.5
- ldap3: 2.5

## Active Directory
- Windows Server 2008 R2 Standard
- 検証用のユーザーは、事前に作成

# 検証環境

##ソース等一式
https://github.com/maedamikio/public/tree/master/django_ldap

## Docker環境の構築
```
$ docker build -t django_ldap .
$ docker run -it -p 8000:8000 -v $PWD:$PWD -w $PWD --name django_ldap --rm django_ldap /bin/bash
```

## Django環境の構築

### データベースの作成
```
$ python3 manage.py migrate
```

### 起動
```
$ python3 manage.py runserver 0.0.0.0:8000
```

### ウェブブラウザでアクセスし、検証環境が構築出来たことを確認します
http://localhost:8000/
<img width="980" alt="django_localhost.png" src="https://qiita-image-store.s3.amazonaws.com/0/68632/2fae945a-d07e-7f94-d0ef-bad6cee042db.png">

http://localhost:8000/admin/
<img width="416" alt="django_admin.png" src="https://qiita-image-store.s3.amazonaws.com/0/68632/966d2a94-06a1-4b9a-78fe-1b8a92ea1319.png">


# バックエンド

以下のバックエンドクラスを説明いたします
django_ldap/backend.py

## インポート等
Active Directory用のldapとsslのインポート
Djangoの設定とユーザークラスのインポート

```
from ssl import CERT_NONE, PROTOCOL_SSLv23
from ldap3 import Server, Connection, Tls

from django.conf import settings
from django.contrib.auth.models import User
```

## Active Directory用の認証バックエンドクラス
下記のメソッドが必須です。
authenticateに、Active Directory関係を実装します
get_userは、参考URLからの変更は不要と思います

```
class Backend:

    def authenticate(self, request, username=None, password=None):
        省略

    def get_user(self, user_id):
        省略
```

## Active Directory関係を実装

最初に、usernameとpasswordが存在するか確認しています
次に、Active Directoryへの接続の準備を行っています
最後に、Djangoに該当usernameが存在しない場合は、新規作成しています

```
    def authenticate(self, request, username=None, password=None):

        # usernameとpasswordの存在の確認しています
        if not (username and password):
            return None

        # ssl関係を準備しています
        tls = Tls(validate=CERT_NONE, version=PROTOCOL_SSLv23)

        # Active Directoryへの接続の設定を行っています
        # 設定は、settings.pyにまとめて記載しています
        server = Server(host=settings.LDAP_HOST, port=settings.LDAP_PORT, use_ssl=True, tls=tls)

        # Active Directoryへ該当usernameとpasswordで接続しています
        # 設定は、settings.pyにまとめて記載しています
        connection = Connection(
            server,
            user='{}\\{}'.format(settings.LDAP_DOMAIN, username),
            password=password)

        # bindを行って、認証を実施しています
        if not connection.bind():
            return None

        # 認証が成功した場合の処理です
        try:
            # 該当usernameがDjangoに存在しているか確認しています
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            # 該当usernameがDjangoに存在していない場合は、新規作成しています
            user = User(username=username)
            # 今回は、基本的な認証の確認のため、管理者権限でユーザーを作成しています
            user.is_staff = True
            user.is_superuser = True
            user.save()

        # 最後にDjangoユーザーを返却します
        return user
```

## 必須のメソッドを実装
ここは、参考URLをそのまま引用しています

```
    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None
```


# Active Directoryの設定
以下の設定を説明いたします
django_ldap/settings.py

```
# 上記のActive Directory用の認証バックエンドクラスの指定
# ここを変更すると標準のバックエンドは利用されなくなり、すべてActive Directoryになるので注意
AUTHENTICATION_BACKENDS = ['django_ldap.backend.Backend']

# IPアドレスやホスト名を文字列で記載して下さい
LDAP_HOST = 'your-ldap-host-ip-address'

# LDAPSは、標準の636を数値で記載して下さい
LDAP_PORT = 636

# Active Directoryのドメインを文字列で記載して下さい
LDAP_DOMAIN = 'your-ldap-domain'
```

# Acitive Directoryでの認証の確認

## 検証サーバーの起動と、管理サイトのログイン画面を表示

```
$ python3 manage.py runserver 0.0.0.0:8000
```

http://localhost:8000/admin/

## 認証の確認
ユーザーは、予めActive Directoryへ作成しておいて下さい

<img width="408" alt="django_admin_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/68632/85361f54-6348-e50d-84a4-5f258b8e5363.png">

## 管理サイトの表示
<img width="1623" alt="django_admin_3.png" src="https://qiita-image-store.s3.amazonaws.com/0/68632/c4dad833-2f74-943c-d282-3a4f287f4531.png">

## 認証と認可のユーザーを表示
該当ユーザーが新規作成されています
<img width="1623" alt="django_admin_4.png" src="https://qiita-image-store.s3.amazonaws.com/0/68632/e7f90732-78a4-3889-aaf7-5ee3f41d161c.png">

## ユーザーの詳細を確認
<img width="1622" alt="django_admin_5.png" src="https://qiita-image-store.s3.amazonaws.com/0/68632/df01b5fe-b4a1-551e-e74f-89d4f6819b84.png">


# おわりに
今回は、Active Directoryを使った認証の基本的な仕組みを解説しました。
ここから、色々応用して頂ければ幸いです。