Golang'de LDAP Authentication, Bind ve Search

Rıdvan Tülemen - Jun 30 '21 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

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 = "user@example.com"
    BindPassword = "password"
    FQDN         = "DC.example.com"
    BaseDN       = "cn=Configuration,dc=example,dc=com"
    Filter       = "(objectClass=*)"
)
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

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 = "user@example.com"
    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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .