```
// FileName: ldap.go
// Author : Darin Han
// Copyright 2020 Darin. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ad
// package for ad operation by ldap
import (
"errors"
"fmt"
"gopkg.in/ldap.v2"
"log"
)
type UserClaims struct {
UserName string
PassWord string
}
type LDAPClient struct {
Host string
Port int
BaseDN string
UserClaims
Connection *ldap.Conn
}
//Const Name for LDAP Elements
const ObjectCategory_OU string = "organizationalUnit"
const ObjectCategory_Group string = "group"
const ObjectCategory_Person string = "user"
// open connection for ldap, Close() should be called intermediatly with defer
func (client *LDAPClient) Open() (*ldap.Conn, error) {
if client.UserName == "" || client.PassWord == "" {
return nil, errors.New("no user account and password !")
}
con, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", client.Host, client.Port))
if err != nil {
return nil, err
}
err = con.Bind(client.UserName, client.PassWord)
if err != nil {
return nil, err
}
client.Connection = con
return con, nil
}
// close connection for ldap
func (client *LDAPClient) Close() {
client.Connection.Close()
client.Connection = nil
}
// try to connect ldap server, return error for fail.
func (client *LDAPClient) Connect() (bool, error) {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", client.Host, client.Port))
if err != nil {
return false, err
}
defer l.Close()
err = l.Bind(client.UserName, client.PassWord)
if err != nil {
log.Fatal(err)
}
return true, nil
}
/*
OU attributes:
description:描述
c:国家简称
co:国家
l:市县
st:省
street:街道
postalCode:邮编
*/
//search ou in the whole tree by dn,return ldap.SearchResult which contains entries , controls
func (client *LDAPClient) SearchOU(search string) (*ldap.SearchResult, error) {
conn, err := client.Open()
if err != nil {
return nil, err
}
defer client.Close()
return conn.Search(ldap.NewSearchRequest(client.BaseDN,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(&(objectCategory=%s)(%s))", ObjectCategory_OU, search), // The filter to apply
[]string{}, // A list attributes to retrieve
nil))
}
// get ou by specified ouname in specified path
func (client *LDAPClient) GetOU(parents []string, subname string) (entry *ldap.Entry, err error) {
conn, err := client.Open()
if err != nil {
return nil, err
}
defer client.Close()
basedn := client.getdn(parents, "")
search := fmt.Sprintf("OU=%s", subname)
result, err := conn.Search(ldap.NewSearchRequest(basedn,
ldap.ScopeSingleLevel,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(&(objectCategory=%s)(%s))", ObjectCategory_OU, search), // The filter to apply
[]string{}, // A list attributes to retrieve
nil,
))
if err != nil {
return nil, err
}
if result.Entries != nil && len(result.Entries) > 0 {
return result.Entries[0], nil
}
return nil, nil
}
// create ou in specified path with specified attributes
func (client *LDAPClient) CreateOU(parents []string, ou string, attributes map[string][]string) (err error) {
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
dn := client.getdn(parents, fmt.Sprintf("OU=%s", ou))
request := ldap.NewAddRequest(dn)
request.Attribute("ou", []string{ou})
request.Attribute("objectClass", []string{"top", ObjectCategory_OU})
if attributes != nil {
for key, val := range attributes {
request.Attribute(key, val)
}
}
if attributes != nil {
for key, val := range attributes {
request.Attribute(key, val)
}
}
return conn.Add(request)
}
// construct dn
func (client *LDAPClient) getdn(parents []string, cn string) string {
dn := client.BaseDN
if len(parents) > 0 {
for _, pou := range parents {
dn = fmt.Sprintf("OU=%s,%s", pou, dn)
}
}
if cn != "" {
dn = fmt.Sprintf("%s,%s", cn, dn)
}
return dn
}
func (client *LDAPClient) UpdateOU(parents []string, ou string, attributes map[string][]string) (err error) {
entry, err := client.GetOU(parents, ou)
if err != nil {
return err
}
if entry == nil {
return errors.New(fmt.Sprintf("no entry find for %s shall not be empty!", client.getdn(parents, ou)))
}
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
dn := client.getdn(parents, fmt.Sprintf("OU=%s", ou))
mq := ldap.NewModifyRequest(dn)
if attributes != nil {
for key, val := range attributes {
exists := false
for _, att := range mq.AddAttributes {
if att.Type == key {
mq.Replace(att.Type, val)
exists = true
break
}
}
if !exists {
mq.Add(key, val)
}
}
}
return conn.Modify(mq)
}
func (client *LDAPClient) DelOU(parents []string, ou string) (err error) {
entry, err := client.GetOU(parents, ou)
if err != nil {
return err
}
if entry == nil {
return errors.New(fmt.Sprintf("no entry find for %s shall not be empty!", client.getdn(parents, fmt.Sprintf("OU=%s", ou))))
}
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
request := ldap.NewDelRequest(client.getdn(parents, fmt.Sprintf("OU=%s", ou)), nil)
return conn.Del(request)
}
func (client *LDAPClient) SearchGroup(search string) (*ldap.SearchResult, error) {
conn, err := client.Open()
if err != nil {
return nil, err
}
defer client.Close()
return conn.Search(ldap.NewSearchRequest(client.BaseDN,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(&(objectCategory=%s)(%s))", ObjectCategory_Group, search), // The filter to apply
[]string{}, // A list attributes to retrieve
nil))
}
func (client *LDAPClient) GetGroup(parents []string, group string) (entry *ldap.Entry, err error) {
conn, err := client.Open()
if err != nil {
return nil, err
}
defer client.Close()
basedn := client.getdn(parents, "")
search := fmt.Sprintf("CN=%s", group)
result, err := conn.Search(ldap.NewSearchRequest(basedn,
ldap.ScopeSingleLevel,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(&(objectCategory=%s)(%s))", ObjectCategory_Group, search), // The filter to apply
[]string{}, // A list attributes to retrieve
nil,
))
if err != nil {
return nil, err
}
if result.Entries != nil && len(result.Entries) > 0 {
return result.Entries[0], nil
}
return nil, nil
}
func (client *LDAPClient) CreateGroup(parents []string, group string, attributes map[string][]string) error {
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
dn := client.getdn(parents, "")
dn = fmt.Sprintf("cn=%s,%s", group, dn)
request := ldap.NewAddRequest(dn)
request.Attribute("CN", []string{group})
request.Attribute("objectClass", []string{"top", ObjectCategory_Group})
if attributes != nil {
for key, val := range attributes {
request.Attribute(key, val)
}
}
if attributes != nil {
for key, val := range attributes {
request.Attribute(key, val)
}
}
return conn.Add(request)
}
func (client *LDAPClient) UpdateGroup(parents []string, group string, attributes map[string][]string) (err error) {
entry, err := client.GetGroup(parents, group)
if err != nil {
return err
}
if entry == nil {
return errors.New(fmt.Sprintf("no entry find for %s shall not be empty!", client.getdn(parents, fmt.Sprintf("cn=%s", group))))
}
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
dn := client.getdn(parents, "")
dn = fmt.Sprintf("cn=%s,%s", group, dn)
mq := ldap.NewModifyRequest(dn)
if attributes != nil {
for key, val := range attributes {
exists := false
for _, att := range mq.AddAttributes {
if att.Type == key {
mq.Replace(att.Type, val)
exists = true
break
}
}
if !exists {
mq.Add(key, val)
}
}
}
return conn.Modify(mq)
}
func (client *LDAPClient) DelGroup(parents []string, group string) error {
entry, err := client.GetGroup(parents, group)
if err != nil {
return err
}
if entry == nil {
return errors.New(fmt.Sprintf("no entry find for %s !", client.getdn(parents, fmt.Sprintf("CN=%s", group))))
}
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
request := ldap.NewDelRequest(client.getdn(parents, fmt.Sprintf("cn=%s", group)), nil)
return conn.Del(request)
}
/*
User Attributes:
sn:
givenname:
Mail:
userPrincipalName:
SAMAccountName:
displayname:
*/
func (client *LDAPClient) SearchUser(search string) (*ldap.SearchResult, error) {
conn, err := client.Open()
if err != nil {
return nil, err
}
defer client.Close()
return conn.Search(ldap.NewSearchRequest(client.BaseDN,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(&(objectCategory=%s)(%s))", ObjectCategory_Person, search), // The filter to apply
[]string{}, // A list attributes to retrieve
nil))
}
func (client *LDAPClient) GetUser(parents []string, user string) (entry *ldap.Entry, err error) {
conn, err := client.Open()
if err != nil {
return nil, err
}
defer client.Close()
basedn := client.getdn(parents, "")
search := fmt.Sprintf("CN=%s", user)
result, err := conn.Search(ldap.NewSearchRequest(basedn,
ldap.ScopeSingleLevel,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(&(objectCategory=%s)(%s))", ObjectCategory_Person, search), // The filter to apply
[]string{}, // A list attributes to retrieve
nil,
))
if err != nil {
return nil, err
}
if result.Entries != nil && len(result.Entries) > 0 {
return result.Entries[0], nil
}
return nil, nil
}
func (client *LDAPClient) CreateUser(parents []string, user string, attributes map[string][]string) error {
conn, err := client.Open()
if err != nil {
return err
}
defer client.Close()
dn := client.getdn(parents, "")
dn = fmt.Sprintf("cn=%s,%s", user, dn)
log.Println(dn)
request := ldap.NewAddRequest(dn)
request.Attribute("CN", []string{user})
request.Attribute("objectClass", []string{"top", "person", ObjectCategory_Person, "organizationalPerson"})
if attributes != nil {
for key, val := range attributes {
request.Attribute(key, val)
}
}
if attributes != nil {
for key, val := range attributes {
request.Attribute(key, val)
}
}
return conn.Add(request)
}
```
```
// FileName: ldap_test.go
// Author : Darin Han
// Copyright 2020 Darin. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ad
import "testing"
func getldapclient() LDAPClient {
return LDAPClient{
Host: "adhostnamel",
Port: 389,
BaseDN: "DC=*,DC=*",
UserClaims: UserClaims{
UserName: "domain\\admin",
PassWord: "password",
},
}
}
func TestLDAPClient_GetOU(t *testing.T) {
tests := []struct {
parents []string
input string
result bool
}{
{[]string{}, "集团", false},
}
client := getldapclient()
for index, test := range tests {
entry, err := client.GetOU(test.parents, test.input)
if err != nil {
t.Fatal(err, index, test.parents, test.input)
}
t.Log(entry.DN)
for _, att := range entry.Attributes {
t.Log(att.Name, att.Values)
}
}
}
func TestLDAPClient_CreateOU(t *testing.T) {
tests := []struct {
parents []string
input string
result bool
}{
{[]string{}, "集团2", false},
}
client := getldapclient()
for _, test := range tests {
err := client.CreateOU(test.parents, test.input, nil)
if err != nil {
t.Fatal(err)
}
}
}
func TestLDAPClient_UpdateOU(t *testing.T) {
tests := []struct {
parents []string
input string
attributes map[string][]string
result bool
}{
{[]string{}, "集团2", make(map[string][]string), false},
}
client := getldapclient()
atts := make(map[string][]string)
atts["description"] = []string{"testdesc"}
for _, test := range tests {
err := client.UpdateOU(test.parents, test.input, atts)
if err != nil {
t.Fatal(err)
}
}
}
func TestLDAPClient_DelOU(t *testing.T) {
tests := []struct {
parents []string
input string
result bool
}{
{[]string{}, "集团2", false},
}
client := getldapclient()
for _, test := range tests {
err := client.DelOU(test.parents, test.input)
if err != nil {
t.Fatal(err)
}
}
}
func TestLDAPClient_SearchGroup(t *testing.T) {
tests := []struct {
input string
result bool
}{
{"cn=testgroup", false},
}
client := getldapclient()
for index, test := range tests {
searchresult, err := client.SearchGroup(test.input)
if err != nil {
t.Fatal(err, index, test.input)
}
t.Log(index, searchresult.Entries[0])
}
}
func TestLDAPClient_CreateGroup(t *testing.T) {
tests := []struct {
parents []string
input string
result bool
}{
{[]string{}, "testgroup", false},
{[]string{"集团"}, "testgroup", false},
}
client := getldapclient()
for _, test := range tests {
err := client.CreateGroup(test.parents, test.input, nil)
if err != nil {
t.Fatal(err)
}
}
}
func TestLDAPClient_GetGroup(t *testing.T) {
tests := []struct {
parents []string
input string
result bool
}{
{[]string{}, "testgroup", false},
}
client := getldapclient()
for _, test := range tests {
err := client.DelGroup(test.parents, test.input)
if err != nil {
t.Fatal(err)
}
}
}
func TestLDAPClient_DelGroup(t *testing.T) {
tests := []struct {
parents []string
input string
result bool
}{
{[]string{}, "testgroup", false},
{[]string{}, "testgroup2", false},
{[]string{"集团"}, "testgroup", false},
}
client := getldapclient()
for _, test := range tests {
err := client.DelGroup(test.parents, test.input)
if err != nil {
t.Fatal(err)
}
}
}
func TestLDAPClient_SearchUser(t *testing.T) {
tests := []struct {
input string
result bool
}{
{"cn=userid", false},
}
client := getldapclient()
for index, test := range tests {
searchresult, err := client.SearchUser(test.input)
if err != nil {
t.Fatal(err, index, test.input)
}
for ii, entry := range searchresult.Entries {
t.Log(ii, "dn:", entry.DN)
for _, att := range entry.Attributes {
t.Log(att.Name, att.Values)
}
}
}
}
func TestLDAPClient_CreateUser(t *testing.T) {
tests := []struct {
parents []string
input string
atts map[string][]string
result bool
}{
{[]string{"集团"}, "testuser", nil, false},
}
client := getldapclient()
for _, test := range tests {
err := client.CreateUser(test.parents, test.input, test.atts)
if err != nil {
panic(err)
}
}
}
```
有疑问加站长微信联系(非本文作者))