- /*
- * Openldap (2.4.30) binding in GO
- *
- *
- * link to ldap or ldap_r (for thread-safe binding)
- *
- *
- * Copyright (C) 2012 - Marc Quinton.
- *
- * Use of this source code is governed by the MIT Licence :
- * http://opensource.org/licenses/mit-license.php
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
- package openldap
- /*
- #define LDAP_DEPRECATED 1
- #include <stdlib.h>
- #include <ldap.h>
- static inline char* to_charptr(const void* s) { return (char*)s; }
- static inline LDAPControl** to_ldapctrlptr(const void* s) {
- return (LDAPControl**) s;
- }
- */
- // #cgo CFLAGS: -DLDAP_DEPRECATED=1
- // #cgo linux CFLAGS: -DLINUX=1
- // #cgo LDFLAGS: -lldap -llber
- import "C"
- import (
- "errors"
- "fmt"
- "unsafe"
- "strings"
- "strconv"
- )
- /* Intialize() open an LDAP connexion ; supported url formats :
- *
- * ldap://host:389/
- * ldaps://secure-host:636/
- *
- * return values :
- * - on success : LDAP object, nil
- * - on error : nil and error with error description.
- */
- func Initialize(url string) (*Ldap, error) {
- _url := C.CString(url)
- defer C.free(unsafe.Pointer(_url))
- var ldap *C.LDAP
- var val int
- val = LDAP_OPT_X_TLS_NEVER
- C.ldap_set_option(ldap,LDAP_OPT_X_TLS_REQUIRE_CERT, unsafe.Pointer(&val))
- // API: int ldap_initialize (LDAP **ldp, LDAP_CONST char *url )
- rv := C.ldap_initialize(&ldap, _url)
- if rv != 0 {
- err := errors.New(fmt.Sprintf("LDAP::Initialize() error (%d) : %s", rv, ErrorToString(int(rv))))
- return nil, err
- }
- return &Ldap{ldap}, nil
- }
- /*
- * StartTLS() is used for regular LDAP (not
- * LDAPS) connections to establish encryption
- * after the session is running.
- *
- * return value :
- * - nil on success,
- * - error with error description on error.
- */
- func (self *Ldap) StartTLS() error {
- var rv int
- // API: int ldap_start_tls_s(LDAP *ld, LDAPControl **serverctrls, LDAPControl **clientctrls);
- rv = int(C.ldap_start_tls_s(self.conn,
- C.to_ldapctrlptr(unsafe.Pointer(nil)),
- C.to_ldapctrlptr(unsafe.Pointer(nil))))
- if rv == LDAP_OPT_SUCCESS {
- return nil
- }
- return errors.New(fmt.Sprintf("LDAP::StartTLS() error (%d) : %s", rv,
- ErrorToString(rv)))
- }
- /*
- * Bind() is used for LDAP authentifications
- *
- * if who is empty this is an anonymous bind
- * else this is an authentificated bind
- *
- * return value :
- * - nil on succes,
- * - error with error description on error.
- *
- */
- func (self *Ldap) Bind(who, cred string) error {
- var rv int
- authmethod := C.int(LDAP_AUTH_SIMPLE)
- // DEPRECATED
- // API: int ldap_bind_s (LDAP *ld, LDAP_CONST char *who, LDAP_CONST char *cred, int authmethod );
- if who == "" {
- _who := C.to_charptr(unsafe.Pointer(nil))
- _cred := C.to_charptr(unsafe.Pointer(nil))
- rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod))
- } else {
- _who := C.CString(who)
- _cred := C.CString(cred)
- defer C.free(unsafe.Pointer(_who))
- rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod))
- }
- if rv == LDAP_OPT_SUCCESS {
- return nil
- }
- self.conn = nil
- return errors.New(fmt.Sprintf("LDAP::Bind() error (%d) : %s", rv, ErrorToString(rv)))
- }
- /*
- * close LDAP connexion
- *
- * return value :
- * - nil on succes,
- * - error with error description on error.
- *
- */
- func (self *Ldap) Close() error {
- // DEPRECATED
- // API: int ldap_unbind(LDAP *ld)
- rv := C.ldap_unbind(self.conn)
- if rv == LDAP_OPT_SUCCESS {
- return nil
- }
- self.conn = nil
- return errors.New(fmt.Sprintf("LDAP::Close() error (%d) : %s", int(rv), ErrorToString(int(rv))))
- }
- /*
- * Unbind() close LDAP connexion
- *
- * an alias to Ldap::Close()
- *
- */
- func (self *Ldap) Unbind() error {
- return self.Close()
- }
- /* Search() is used to search LDAP server
- - base is where search is starting
- - scope allows local or deep search. Supported values :
- - LDAP_SCOPE_BASE
- - LDAP_SCOPE_ONELEVEL
- - LDAP_SCOPE_SUBTREE
- - filter is an LDAP search expression,
- - attributes is an array of string telling with LDAP attribute to get from this request
- */
- func (self *Ldap) Search(base string, scope int, filter string, attributes []string) (*LdapMessage, error) {
- var attrsonly int = 0 // false: returns all, true, returns only attributes without values
- _base := C.CString(base)
- defer C.free(unsafe.Pointer(_base))
- _filter := C.CString(filter)
- defer C.free(unsafe.Pointer(_filter))
- // transform []string to C.char** null terminated array (attributes argument)
- _attributes := make([]*C.char, len(attributes)+1) // default set to nil (NULL in C)
- for i, arg := range attributes {
- _attributes[i] = C.CString(arg)
- defer C.free(unsafe.Pointer(_attributes[i]))
- }
- var msg *C.LDAPMessage
- // DEPRECATED
- // API: int ldap_search_s (LDAP *ld, char *base, int scope, char *filter, char **attrs, int attrsonly, LdapMessage * ldap_res)
- rv := int(C.ldap_search_s(self.conn, _base, C.int(scope), _filter, &_attributes[0], C.int(attrsonly), &msg))
- if rv == LDAP_OPT_SUCCESS {
- _msg := new(LdapMessage)
- _msg.ldap = self
- _msg.errno = rv
- _msg.msg = msg
- return _msg, nil
- }
- return nil, errors.New(fmt.Sprintf("LDAP::Search() error : %d (%s)", rv, ErrorToString(rv)))
- }
- // ------------------------------------- Ldap* method (object oriented) -------------------------------------------------------------------
- // Create a new LdapAttribute entry with name and values.
- func LdapAttributeNew(name string, values []string)(*LdapAttribute){
- a := new(LdapAttribute)
- a.values = values
- a.name = name
- return a
- }
- // Append() adds an LdapAttribute to self LdapEntry
- func (self *LdapEntry) Append(a LdapAttribute){
- self.values = append(self.values, a)
- }
- // String() is used for fmt.Println(self)
- //
- func (self *LdapAttribute) String() string{
- return self.ToText()
- }
- // ToText() returns a text string representation of LdapAttribute
- // avoiding displaying binary data.
- //
- func (self *LdapAttribute) ToText() string{
- var list []string
- for _, a := range self.Values() {
- if (!_isPrint(a)) {
- list = append(list, fmt.Sprintf("binary-data[%d]", len(a)))
- } else {
- list = append(list, a)
- }
- }
- if len(list) > 1 {
- return fmt.Sprintf("%s: (%d)[%s]", self.name, len(list), strings.Join(list, ", "))
- }
- return fmt.Sprintf("%s: [%s]", self.name, strings.Join(list, ", "))
- }
- // Name() return attribute name
- func (self *LdapAttribute) Name() string{
- return self.name
- }
- // Values() returns array values for self LdapAttribute
- //
- func (self *LdapAttribute) Values() []string{
- return self.values
- }
- // _isPrint() returns true if str is printable
- //
- // @private method
- func _isPrint(str string) bool{
- for _, c := range str{
- if !strconv.IsPrint(rune(c)) {
- return false
- }
- }
- return true
- }
- // IsPrint() returns true is self LdapAttribute is printable.
- func (self *LdapAttribute) IsPrint() bool{
- for _, a := range self.Values() {
- if (!_isPrint(a)) {
- return false
- }
- }
- return true
- }
- // Dn() returns DN (Distinguish Name) for self LdapEntry
- func (self *LdapEntry) Dn() string{
- return self.dn
- }
- // Attributes() returns an array of LdapAttribute
- func (self *LdapEntry) Attributes() []LdapAttribute{
- return self.values
- }
- // Print() allow printing self LdapEntry with fmt.Println()
- func (self *LdapEntry) String() string {
- return self.ToText()
- }
- // GetValuesByName() get a list of values for self LdapEntry, using "name" attribute
- func (self *LdapEntry) GetValuesByName(attrib string) []string{
- for _, a := range self.values{
- if a.Name() == attrib {
- return a.values
- }
- }
- return []string{}
- }
- // GetOneValueByName() ; a quick way to get a single attribute value
- func (self *LdapEntry) GetOneValueByName(attrib string) (string, error){
- for _, a := range self.values{
- if a.Name() == attrib {
- return a.values[0], nil
- }
- }
- return "", errors.New(fmt.Sprintf("LdapEntry::GetOneValueByName() error : attribute %s not found", attrib))
- }
- // ToText() return a string representating self LdapEntry
- func (self *LdapEntry) ToText() string{
- txt := fmt.Sprintf("dn: %s\n", self.dn)
- for _, a := range self.values{
- txt = txt + fmt.Sprintf("%s\n", a.ToText())
- }
- return txt
- }
- // Append() add e to LdapSearchResult array
- func (self *LdapSearchResult) Append(e LdapEntry){
- self.entries = append(self.entries, e)
- }
- // ToText() : a quick way to print an LdapSearchResult
- func (self *LdapSearchResult) ToText() string{
- txt := fmt.Sprintf("# query : %s\n", self.filter)
- txt = txt + fmt.Sprintf("# num results : %d\n", self.Count())
- txt = txt + fmt.Sprintf("# search : %s\n", self.Filter())
- txt = txt + fmt.Sprintf("# base : %s\n", self.Base())
- txt = txt + fmt.Sprintf("# attributes : [%s]\n", strings.Join(self.Attributes(), ", "))
- for _, e := range self.entries{
- txt = txt + fmt.Sprintf("%s\n", e.ToText())
- }
- return txt
- }
- // String() : used for fmt.Println(self)
- func (self *LdapSearchResult) String() string{
- return self.ToText()
- }
- // Entries() : returns an array of LdapEntry for self
- func (self *LdapSearchResult) Entries() []LdapEntry{
- return self.entries
- }
- // Count() : returns number of results for self search.
- func (self *LdapSearchResult) Count() int{
- return len(self.entries)
- }
- // Filter() : returns filter for self search
- func (self *LdapSearchResult) Filter() string{
- return self.filter
- }
- // Filter() : returns base DN for self search
- func (self *LdapSearchResult) Base() string{
- return self.base
- }
- // Filter() : returns scope for self search
- func (self *LdapSearchResult) Scope() int{
- return self.scope
- }
- // Filter() : returns an array of attributes used for this actual search
- func (self *LdapSearchResult) Attributes() []string{
- return self.attributes
- }
- // SearchAll() : a quick way to make search. This method returns an LdapSearchResult with all necessary methods to
- // access data. Result is a collection (tree) of []LdapEntry / []LdapAttribute.
- //
- func (self *Ldap) SearchAll(base string, scope int, filter string, attributes []string) (*LdapSearchResult, error) {
- sr := new(LdapSearchResult)
- sr.ldap = self
- sr.base = base
- sr.scope = scope
- sr.filter = filter
- sr.attributes = attributes
- // Search(base string, scope int, filter string, attributes []string) (*LDAPMessage, error)
- result, err := self.Search(base, scope, filter, attributes)
- if err != nil {
- fmt.Println(err)
- return sr, err
- }
- // Free LDAP::Result() allocated data
- defer result.MsgFree()
- e := result.FirstEntry()
- for e != nil {
- _e := new(LdapEntry)
- _e.dn = e.GetDn()
- attr, _ := e.FirstAttribute()
- for attr != "" {
- _attr := LdapAttributeNew(attr, e.GetValues(attr))
- _e.Append(*_attr)
- attr, _ = e.NextAttribute()
- }
- sr.Append(*_e)
- e = e.NextEntry()
- }
- return sr, nil
- }