PHPでActiveDirectoryユーザの一覧を表示してみた

**ActiveDirectory**で管理している従業員のログイン時刻などを、社内イントラのWebサイトから参照できると便利な状況が多々ある。
ということで、PHPで簡単なツールを書いてみた。

# 実行環境

|サーバ       |ソフトウェアのバージョン            |
|:------------|:--------------------|
|AD/DNSサーバ |Windows Server 2016 Standard Edition   |
|社内イントラ用Webサーバ    |CentOS 7.6 + Apache 2.4 + PHP 7.2 |

# LDAPモジュールを有効にする
ActiveDirectoryは**LDAP**を実装しているので、PHP組込みのLDAP関数でバインドする。
CentOS 7 で`php-ldap`を有効にする手順は次の通り。

```:LDAPモジュールがあるか調べる
# rpm -qa | grep php
php-json-7.0.16-1.el7.remi.x86_64
php-mcrypt-7.0.16-1.el7.remi.x86_64
php-pear-1.10.1-10.el7.remi.noarch
php-common-7.0.16-1.el7.remi.x86_64
php-mysqlnd-7.0.16-1.el7.remi.x86_64
php-mbstring-7.0.16-1.el7.remi.x86_64
php-gd-7.0.16-1.el7.remi.x86_64
php-cli-7.0.16-1.el7.remi.x86_64
php-devel-7.0.16-1.el7.remi.x86_64
php-fpm-7.0.16-1.el7.remi.x86_64
php-pdo-7.0.16-1.el7.remi.x86_64
php-7.0.16-1.el7.remi.x86_64
php-process-7.0.16-1.el7.remi.x86_64
php-pgsql-7.0.16-1.el7.remi.x86_64
php-xml-7.0.16-1.el7.remi.x86_64
```

```:なければRemiリポジトリからインストール
yum -y install --enablerepo=remi-php70 php-ldap
```

```ini:/etc/php.d/20-ldap.ini
extension=ldap
```

# 専用ユーザアカウントの作成
PHPがLDAPでアクセスするための専用ユーザアカウントを作成し、`Account Operators`に所属させる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/5c7eba62-2ec9-7cbd-0584-b595a55e2e98.png)
`Account Operators`はビルトインローカルグループのひとつで、ドメインのユーザアカウントの管理権限を持つ。

# コード

```php:PHPを埋め込んだHTML
<html lang="ja">
<head><meta charset="utf-8"><title>在席確認ツール</title>
<style type="text/css">
* {
  font-family: "ＭＳ ゴシック", "MS Gothic"
}
table {
  width: 100%;
}
table, th, td {
  border-collapse: collapse;
  border: 1px solid #999;
  line-height: 1.5;
}
th {
  color: #fff;
  background: #008;
}
tr:nth-child(even) {
  background: #eee;
}
td {
  text-align: center;
}
</style>
</head>
<body>
<h1>在席確認</h1>
<?php
date_default_timezone_set('Asia/Tokyo');

# ユーザアカウントの管理権限を持つアカウント
$user = 'XXXXXX';
$passwd = 'YYYYYY';

# ADサーバのアドレス
$ldapConn = ldap_connect('ldap://10.123.123.123');

ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);

# バインドできたら
if (ldap_bind($ldapConn, $user . '@foo.example.ne.jp', $passwd)) {
    $sr = ldap_list ($ldapConn, 'ou=Users,ou=BAR,dc=foo,dc=example,dc=ne,dc=jp', '(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))', array('samaccountname','description','displayname','lastlogon','pwdlastset','mail'));

    ldap_sort($ldapConn, $sr, 'description');
    $info = ldap_get_entries($ldapConn, $sr);
    $count = $info['count'];

    echo '<table border="1">';
    echo '<tr><th>No.</th><th>アカウント名</th><th>Description</th><th>氏名</th><th>メールアドレス</th><th>最終ログイン日時</th><th>最終パスワード変更日時</th></tr>';

    $n = 1;
    for ($i = 0; $i < $count; $i++) {
      $ent = $info[$i];
      $samaccountname = $ent['samaccountname'][0];
      $desc = $ent['description'][0];
      $displayname = $ent['displayname'][0];
      # メールアドレスが設定されている人は表示
      if (array_key_exists('mail', $ent)) {
        $mail = $ent['mail'][0];
      } else {
        $mail = '-';
      }
      $lastlogontime = round($ent['lastlogon'][0] / 10000000) - 11644473600;
      # 直近にログインしている人ほど背景を赤くする
      $diff = time() - $lastlogontime;
      if ($diff < 86400) {
          $bgcolor = 'bgcolor=#ff' . str_repeat(str_pad(dechex(255 * log(1 + $diff, 86400) ^ 2), 2, 0, STR_PAD_LEFT), 2);
      } else {
          $bgcolor = '';
      }
      $lastlogon = date('Y-m-d H:i:s', $lastlogontime);
      $pwdlastset = date('Y-m-d H:i:s', round($ent['pwdlastset'][0] / 10000000) - 11644473600);

      echo "<tr><td>$n</td><td>$samaccountname</td><td>$desc</td><td>$displayname</td><td>$mail</td><td $bgcolor>$lastlogon</td><td>$pwdlastset</td></tr>";
      $n++;
    }
    echo '</table>';
} else {
    echo 'Failed';
}
ldap_close($ldapConn);
?>
</body></html>
```

# 実行結果
直近までログインしている人ほど、背景色をより赤くなるようにしてみた。
この応用で、例えば１年以上パスワードを変えていない人には色を付けても良いと思う。

---
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/0bb6d255-09a8-7b26-a134-02454608f315.png)