Go言語からActive Directoryにアクセスしてみる（更新/削除編）

#はじめに
[前々回](http://qiita.com/nagase/items/38ba71b710a0a6926950)、[前回](http://qiita.com/nagase/items/d0109cbf2e7056418915)に引き続き、Active Directoryのエントリを更新、削除してみたいと思います。

#やってみる内容

[前回](http://qiita.com/nagase/items/d0109cbf2e7056418915)登録したユーザーアカウント`testuser`に属性`mail`を追加、属性`displayName`の値を変更します。
その後、ユーザーアカウント`testuser`エントリを削除します。

#サンプルコード（更新編）

```go:ldapmod.go
package main

import (
    "fmt"
    "log"
    "gopkg.in/ldap.v2"
    "crypto/tls"
)

const (
    LDAPSV = "dc1.mydomain.local"
    PROTO  = "tcp"
    PORTNO = 636
    BINDDN = "cn=Administrator,cn=Users,dc=mydomain,dc=local"
    BINDPW = "XXXXXXXX"
)

func main() {
    // LDAPサーバへ接続(LDAPS)
    tlsConfig := &tls.Config{InsecureSkipVerify: true}
    l, err := ldap.DialTLS(PROTO, fmt.Sprintf("%s:%d", LDAPSV, PORTNO), tlsConfig)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Ldap server connected.")
    defer func() {
        l.Close()
        fmt.Println("Ldap server disconnected.")
    }()

    //LDAPサーバ認証（バインド）
    err = l.Bind(BINDDN, BINDPW)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Ldap server logged in.")

    //更新リクエスト作成
    modRequest := ldap.NewModifyRequest("cn=testuser,cn=Users,dc=mydomain,dc=local")
    modRequest.Add("mail", []string{"testuder@example.com"})
    modRequest.Replace("displayName", []string{"Test User"})

    //エントリ属性変更
    err = l.Modify(modRequest)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Ldap server entry modified.")
}
```

エントリの`unicodePwd`を変更しなければ、普通にLDAP(port=389)経由で更新することもできますが、 ~~折角ADをLDAPSに対応させたので~~セキュリティ面を考慮し、LDAPS経由で更新しています。

また更新リクエスト作成の箇所では、リクエストインスタンスに対し`.Add()`、`.Replace()`、`.Delete()`の各メソッドにより属性を操作することが出来ます。

#実行してみる（更新編）
サンプルコードを実行すると、エラーが無ければ次のようなメッセージとともに正常終了します。

```
>go run ldapmod.go
Ldap server connected.
Ldap server logged in.
Ldap server entry modified.
Ldap server disconnected.
```

#確認してみる（更新編）

ADサーバ側で`testuser`エントリの`mail`属性が追加され、更に`displayName`属性の値が変更されています。

```
>dsquery * "cn=testuser,cn=Users,dc=mydomain,dc=local" -attr *
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: testuser
distinguishedName: CN=testuser,CN=Users,DC=mydomain,DC=local
instanceType: 4
whenCreated: 06/23/2017 03:02:24
whenChanged: 06/29/2017 04:59:10
displayName: Test User
uSNCreated: 24753
uSNChanged: 36935
name: testuser
objectGUID: {205A09A4-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
userAccountControl: 66048
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 0
lastLogoff: 0
lastLogon: 0
pwdLastSet: 131426605443450970
primaryGroupID: 513
objectSid: S-1-5-21-2873641411-XXXXXXXXXX-XXXXXXXXXX-XXXX
accountExpires: 9223372036854775807
logonCount: 0
sAMAccountName: testuser
sAMAccountType: 805306368
userPrincipalName: testuser@mydomain.local
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=mydomain,DC=local
dSCorePropagationData: 01/01/1601 00:00:00
mail: testuder@example.com
ADsPath: LDAP://DC1.mydomain.local/CN=testuser,CN=Users,DC=mydomain,DC=local
```

#サンプルコード（削除編）

```go:ldapdel.go
package main

import (
    "fmt"
    "log"
    "gopkg.in/ldap.v2"
    "crypto/tls"
)

const (
    LDAPSV = "dc1.mydomain.local"
    PROTO  = "tcp"
    PORTNO = 636
    BINDDN = "cn=Administrator,cn=Users,dc=mydomain,dc=local"
    BINDPW = "XXXXXXXX"
)

func main() {
    // LDAPサーバへ接続
    //l, err := ldap.Dial(PROTO, fmt.Sprintf("%s:%d", LDAPSV, PORTNO))
    tlsConfig := &tls.Config{InsecureSkipVerify: true}
    l, err := ldap.DialTLS(PROTO, fmt.Sprintf("%s:%d", LDAPSV, PORTNO), tlsConfig)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Ldap server connected.")
    defer func() {
        l.Close()
        fmt.Println("Ldap server disconnected.")
    }()

    //LDAPサーバ認証（バインド）
    err = l.Bind(BINDDN, BINDPW)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Ldap server logged in.")

    //削除リクエスト作成
    delRequest := ldap.NewDelRequest("cn=testuser,cn=Users,dc=mydomain,dc=local", nil)

    //エントリ削除
    err = l.Del(delRequest)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Ldap server entry deleted.")
}
```

エントリの削除では、`NewDelRequest()`メソッドにDNを引数として削除リクエストを作成します。

#実行してみる（削除編）

サンプルコードを実行すると、エラーが無ければ次のようなメッセージとともに正常終了します。

```
>go run ldapdel.go
Ldap server connected.
Ldap server logged in.
Ldap server entry deleted.
Ldap server disconnected.
```

#確認してみる（削除編）

ADサーバ側で`testuser`を参照しようとすると、エントリが存在しない為、以下のようなエラーメッセージが返されます。

```
>dsquery * "cn=testuser,cn=Users,dc=mydomain,dc=local" -attr *
dsquery 失敗:ディレクトリオブジェクトが見つかりません。
dsquery /? と入力するとヘルプが表示されます。
```

#参考

http://godoc.org/gopkg.in/ldap.v2