LDAPクライアントプログラミングは怖くない（.NET編）

# はじめに

　弊社オープンソース・ソリューション・テクノロジでは、LDAPのサーバ側製品としてOpenLDAPを取り扱ってます。おかげさまで多くのお客様に導入いただき、ユーザ統合管理や統合認証の実現に寄与させていただいてます。
　
　OpenLDAPを導入いただく際にお客様から時々ご質問いただくのが、「LDAPサーバ内のユーザ情報を読み書きするクライアントプログラムを開発したいのだけれど、どうしたら良い？」だったりします。
　基本的には「LDAPは標準仕様ですので、仕様に準拠しているクライアントライブラリなどを利用して開発してください。」という回答になりますが…、LDAPクライアントプログラミングって多くの人が経験するものではなく、結果としてWeb上でも情報が少な目になってしまうので、突然担当することになってしまったら不安になってしまうかもしれません。

　しかし、実はやってみると案外スッと実現できるものなので、やってみた記事を書いてみることにします。いつか、どこかのSEさんが「あぁ、これくらいで動かせるんだー」と安心できる助けとなりますように。
　今回は.NET編。[次回](https://qiita.com/hoshino_osstech/items/8dd63d61df4915104396)はJava編ということで書いていきます。


# 準備するもの

.NETの開発環境…は当たり前なので詳述せず、ここでは、LDAPに直接関連する事前準備について記します。
基本的に、全てLDAPサーバ側の管理者に問い合わせて情報を得るものです。

### [1] LDAPサーバ情報

「どうやってLDAPサーバに繋げばいいの？」の情報を得ておきます。
以下の情報が得られたと仮定します。

|項目|値|
|---|---|
|サーバ名|ldapserver.mydomain.test|
|プロトコル（LDAP/LDAPS）|LDAPS|
|ポート|636|

ここで、LDAPSのためのサーバ証明書が止むを得ず自己署名証明書であった場合は、
予め証明書を入手してLDAPクライアント機にインポートしておきましょう。

### [2] データ操作用アカウント情報

「どのアカウントを使ってLDAPサーバ上のデータ操作したらいいの？」の情報を得ておきます。
以下の情報が得られたと仮定します。

|項目|値|
|---|---|
|データ操作用アカウント名|cn=datamaster,dc=ldapserver,dc=mydomain,dc=test|
|パスワード|xahTh8phou6Ieyoh|

### [3] ユーザエントリの格納ツリーや属性情報

「ユーザエントリをLDAPツリー上のどこに格納し、どの属性値を扱ったらいいの？」の情報を得ておきます。
以下の情報が得られたと仮定します。

|項目|値|
|---|---|
|ユーザエントリ格納場所|ou=staff,dc=ldapserver,dc=mydomain,dc=test|
|エントリの属性情報: objectClass|"top", "person", "inetOrgPerson", "organizationalPerson"|
|エントリの属性情報: dn用の属性値|uid（内部処理用のユーザIDを格納することとする）|
|エントリの属性情報: 一般属性値|cn（ユーザのフルネームを格納することとする）|
|エントリの属性情報: 一般属性値|sn（ユーザの姓を格納することとする）|

実際はもっと多くの属性値を用いると思いますが、本記事ではサンプルとして最小限にしておきます。

# LDAPクラアイントプログラミングしてみる

今回は Visual Basic を使ってコンソールアプリを書いてみます。
まずは、LDAP操作用のライブラリを利用するため、プロジェクトの「参照」にて System.DirectoryServices.Protocols を追加しておく必要があります。

### プログラム

あとはごりごりっとプログラムを書くだけです。
LDAPサーバに繋ぎ、ユーザエントリの追加、属性値の変更、検索、削除を実施するプログラムが…こちらです。

```visualbasic
Imports System.DirectoryServices.Protocols

Module Module1
    Sub Main()
        '-------------
        ' Connect
        '-------------
        Dim ldapConnection = New LdapConnection("ldapserver.mydomain.test:636")
        ldapConnection.SessionOptions.SecureSocketLayer = True
        ldapConnection.Credential = New Net.NetworkCredential(
            "cn=datamaster,dc=ldapserver,dc=mydomain,dc=test", "xahTh8phou6Ieyoh")
        ldapConnection.AuthType = AuthType.Basic ' 基本認証で繋ぐ
        ldapConnection.Bind()

        '-------------
        ' ADD
        '-------------
        Dim attributes() As DirectoryAttribute = {
            New DirectoryAttribute("objectClass", 
                New String() {"top", "person", "inetOrgPerson", "organizationalPerson"}),
            New DirectoryAttribute("cn", "Hoshino Ko"),
            New DirectoryAttribute("sn", "Hoshino")
            }
        Dim addRequest As AddRequest =
            New AddRequest("uid=user0000111,ou=staff,dc=ldapserver,dc=mydomain,dc=test", attributes)
        ldapConnection.SendRequest(addRequest)
        Console.WriteLine(">> Add finished!")

        '-------------
        ' MODIFY
        '-------------
        Dim modifyRequest As ModifyRequest =
            New ModifyRequest("uid=user0000111,ou=staff,dc=ldapserver,dc=mydomain,dc=test", 
                DirectoryAttributeOperation.Replace, "cn", "Hoshino ""LDAP"" Ko") ' ミドルネームを入れてみる
        ldapConnection.SendRequest(modifyRequest)
        Console.WriteLine(">> Modify finished!")

        '-------------
        ' SEARCH
        '-------------
        Dim searchRequest As SearchRequest =
            New SearchRequest("ou=staff,dc=ldapserver,dc=mydomain,dc=test", 
                "(cn=Hoshino*)", SearchScope.Subtree, New String() {"uid", "cn", "sn"})
        Dim searchResponse As SearchResponse = ldapConnection.SendRequest(searchRequest)

        Console.WriteLine("----- Search Result -----")
        For Each resultEntry As SearchResultEntry In searchResponse.Entries ' 検索で複数人ヒットした場合、全員分表示
            For Each attribute As DictionaryEntry In resultEntry.Attributes ' 取得した属性値を全部表示
                For Each value As Object In attribute.Value ' 同じ属性名の属性値が複数存在していたら全部表示
                    Console.WriteLine(attribute.Key + ": " + System.Text.Encoding.UTF8.GetString(value))
                Next
            Next
            Console.WriteLine()
        Next
        Console.WriteLine("-------------------------")
        Console.WriteLine(">> Search finished!")

        '-------------
        ' DELETE
        '-------------
        Dim deleteRequest As DeleteRequest =
            New DeleteRequest("uid=user0000111,ou=staff,dc=ldapserver,dc=mydomain,dc=test")
        ldapConnection.SendRequest(deleteRequest)
        Console.WriteLine(">> Delete finished!")

        Console.Write("Press any key to continue...")
        Console.ReadKey(True)
    End Sub
End Module
```

### プログラム実行結果

実行してみると、以下のようにコンソールに表示されます。
ちゃんとユーザエントリが追加されて、cnの値が変更されてて、そして検索ができてる感じです。

```
>> Add finished!
>> Modify finished!
----- Search Result -----
sn: Hoshino
uid: user0000111
cn: Hoshino "LDAP" Ko

-------------------------
>> Search finished!
>> Delete finished!
Press any key to continue...
```


# おわりに

というわけで今回は、.NET／Visual Basicを使って、さくっとLDAPクライアントプログラミングしてみました。

やってみると実はそんなに難しくないですし、プロジェクト毎のスキーマ仕様やテーブル結合などを
気にしなくて良い分、一般的なRelational DatabaseよりもLDAPのクライアントプログラミングの方が
楽だったりします。
というわけで、、LDAP使い倒しましょう！

（[次回Java編](https://qiita.com/hoshino_osstech/items/8dd63d61df4915104396)では、今回のコードのJava版や、ちょっとした応用例について書く予定です。
 　ここまで読んでくださった方、[次回Java編](https://qiita.com/hoshino_osstech/items/8dd63d61df4915104396)も読んでいただけると嬉しいです。）