Golang'de LDAP Authentication, Bind ve Search

Windows Active Directory veya Samba hakkında bilginiz varsa, LDAP hakkında halihazırda bilginiz olabilir. Ama yoksa, Wikipedia'daki açıklama şu şekildedir.

Lightweight Directory Access Protocol veya kısaca LDAP (Türkçe: Basit İndeks Erişim Protokolü) TCP/IP üzerinde çalışan indeks servislerini sorgulama ve değiştirme amacıyla kullanılan uygulama katmanı protokolü.

Projeyi Oluşturmak

İlk olarak, kütüphaneyi indirmemiz gerekiyor:

go get github.com/go-ldap/ldap

Değişkenler

Artık kütüphaneyi kullanmaya başlayabiliriz. İlk olarak, daha sonra kullanacağımız değişkenleri oluşturuyoruz(Eğer anonymous bind kullanacaksanız sadece Filter değişkeni yeterlidir):

const (
    BindUsername = "[email protected]"
    BindPassword = "password"
    FQDN         = "DC.example.com"
    BaseDN       = "cn=Configuration,dc=example,dc=com"
    Filter       = "(objectClass=*)"
)

Connect

LDAP'a bağlanmak için, ldap.DialURL() fonksiyonunu kullanacağız. Aşağıdaki fonksiyon sadece bağlantı için kullanılacak bir fonksiyondur:

// Ldap Connection without TLS
func Connect() (*ldap.Conn, error) {
    // You can also use IP instead of FQDN
    l, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", FQDN))
    if err != nil {
        return nil, err
    }

    return l, nil
}

TLS Connect

Eğer TLS Connection kullanmak istiyorsanız, aşağıdaki fonksiyonu kullanabilirsiniz:

// Ldap Connection with TLS
func ConnectTLS() (*ldap.Conn, error) {
    // You can also use IP instead of FQDN
    l, err := ldap.DialURL(fmt.Sprintf("ldaps://%s:636", FQDN))
    if err != nil {
        return nil, err
    }

    return l, nil
}

Anonymous Bind ve Search

Eğer anonymous bind kullanmak istiyorsanız, bu fonksiyonu kullanabilirsiniz:

// Anonymous Bind and Search
func AnonymousBindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
    l.UnauthenticatedBind("")

    anonReq := ldap.NewSearchRequest(
        "",
        ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
        ldap.NeverDerefAliases,
        0,
        0,
        false,
        Filter,
        []string{},
        nil,
    )
    result, err := l.Search(anonReq)
    if err != nil {
        return nil, fmt.Errorf("Anonymous Bind Search Error: %s", err)
    }

    if len(result.Entries) > 0 {
        result.Entries[0].Print()
        return result, nil
    } else {
        return nil, fmt.Errorf("Couldn't fetch anonymous bind search entries")
    }
}

Bind ve Search

Onun yerine, normal bind kullanmak isterseniz:

// Normal Bind and Search
func BindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
    l.Bind(BindUsername, BindPassword)

    searchReq := ldap.NewSearchRequest(
        BaseDN,
        ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
        ldap.NeverDerefAliases,
        0,
        0,
        false,
        Filter,
        []string{},
        nil,
    )
    result, err := l.Search(searchReq)
    if err != nil {
        return nil, fmt.Errorf("Search Error: %s", err)
    }

    if len(result.Entries) > 0 {
        return result, nil
    } else {
        return nil, fmt.Errorf("Couldn't fetch search entries")
    }
}

Main Fonksiyon

Son olarak, bu fonksiyonları kullanabileceğimiz bir main fonksiyona ihtiyacımız var.

TLS Connection ile Bind ve Search

func main() {
    // TLS Connection
    l, err := ConnectTLS()
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()

    // Normal Bind and Search
    result, err = BindAndSearch(l)
    if err != nil {
        log.Fatal(err)
    }
    result.Entries[0].Print()
}

Non-TLS Connection ile Anonymous Bind ve Search

func main() {
    // Non-TLS Connection
    l, err := Connect()
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()

    // Anonymous Bind and Search
    result, err := AnonymousBindAndSearch(l)
    if err != nil {
        log.Fatal(err)
    }
    result.Entries[0].Print()
}

Son Olarak

Bu fonksiyonlar ile LDAP ile Authentication, bind ve search'ün anlaşılabildiğini düşünüyorum. Sonunda kod aşağıdaki gibi bir hale geliyor:

package main

import (
    "fmt"
    "log"

    "github.com/go-ldap/ldap/v3"
)

const (
    BindUsername = "[email protected]"
    BindPassword = "password"
    FQDN         = "DC.example.com"
    BaseDN       = "cn=Configuration,dc=example,dc=com"
    Filter       = "(objectClass=*)"
)

func main() {
    // TLS Connection
    l, err := ConnectTLS()
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()

    // Non-TLS Connection
    //l, err := Connect()
    //if err != nil {
    //  log.Fatal(err)
    //}
    //defer l.Close()

    // Anonymous Bind and Search
    result, err := AnonymousBindAndSearch(l)
    if err != nil {
        log.Fatal(err)
    }
    result.Entries[0].Print()

    // Normal Bind and Search
    result, err = BindAndSearch(l)
    if err != nil {
        log.Fatal(err)
    }
    result.Entries[0].Print()
}

// Ldap Connection with TLS
func ConnectTLS() (*ldap.Conn, error) {
    // You can also use IP instead of FQDN
    l, err := ldap.DialURL(fmt.Sprintf("ldaps://%s:636", FQDN))
    if err != nil {
        return nil, err
    }

    return l, nil
}

// Ldap Connection without TLS
func Connect() (*ldap.Conn, error) {
    // You can also use IP instead of FQDN
    l, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", FQDN))
    if err != nil {
        return nil, err
    }

    return l, nil
}

// Anonymous Bind and Search
func AnonymousBindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
    l.UnauthenticatedBind("")

    anonReq := ldap.NewSearchRequest(
        "",
        ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
        ldap.NeverDerefAliases,
        0,
        0,
        false,
        Filter,
        []string{},
        nil,
    )
    result, err := l.Search(anonReq)
    if err != nil {
        return nil, fmt.Errorf("Anonymous Bind Search Error: %s", err)
    }

    if len(result.Entries) > 0 {
        result.Entries[0].Print()
        return result, nil
    } else {
        return nil, fmt.Errorf("Couldn't fetch anonymous bind search entries")
    }
}

// Normal Bind and Search
func BindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
    l.Bind(BindUsername, BindPassword)

    searchReq := ldap.NewSearchRequest(
        BaseDN,
        ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
        ldap.NeverDerefAliases,
        0,
        0,
        false,
        Filter,
        []string{},
        nil,
    )
    result, err := l.Search(searchReq)
    if err != nil {
        return nil, fmt.Errorf("Search Error: %s", err)
    }

    if len(result.Entries) > 0 {
        return result, nil
    } else {
        return nil, fmt.Errorf("Couldn't fetch search entries")
    }
}

Ayrıca, oluşturduğum gist'e de göz atabilirsiniz.

Okduğunuz için teşekkürler. Umarım yardımcı olabilmişimdir.

15