...

Source file src/github.com/go-ldap/ldap/v3/ldap_test.go

Documentation: github.com/go-ldap/ldap/v3

     1  package ldap
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"log"
     7  	"testing"
     8  
     9  	ber "github.com/go-asn1-ber/asn1-ber"
    10  )
    11  
    12  const (
    13  	ldapServer  = "ldap://ldap.itd.umich.edu:389"
    14  	ldapsServer = "ldaps://ldap.itd.umich.edu:636"
    15  	baseDN      = "dc=umich,dc=edu"
    16  )
    17  
    18  var filter = []string{
    19  	"(cn=cis-fac)",
    20  	"(&(owner=*)(cn=cis-fac))",
    21  	"(&(objectclass=rfc822mailgroup)(cn=*Computer*))",
    22  	"(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))",
    23  }
    24  
    25  var attributes = []string{
    26  	"cn",
    27  	"description",
    28  }
    29  
    30  func TestUnsecureDialURL(t *testing.T) {
    31  	l, err := DialURL(ldapServer)
    32  	if err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	defer l.Close()
    36  }
    37  
    38  func TestSecureDialURL(t *testing.T) {
    39  	l, err := DialURL(ldapsServer, DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	defer l.Close()
    44  }
    45  
    46  func TestStartTLS(t *testing.T) {
    47  	l, err := DialURL(ldapServer)
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	defer l.Close()
    52  	err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  }
    57  
    58  func TestTLSConnectionState(t *testing.T) {
    59  	l, err := DialURL(ldapServer)
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	defer l.Close()
    64  	err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  
    69  	cs, ok := l.TLSConnectionState()
    70  	if !ok {
    71  		t.Errorf("TLSConnectionState returned ok == false; want true")
    72  	}
    73  	if cs.Version == 0 || !cs.HandshakeComplete {
    74  		t.Errorf("ConnectionState = %#v; expected Version != 0 and HandshakeComplete = true", cs)
    75  	}
    76  }
    77  
    78  func TestSearch(t *testing.T) {
    79  	l, err := DialURL(ldapServer)
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	defer l.Close()
    84  
    85  	searchRequest := NewSearchRequest(
    86  		baseDN,
    87  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
    88  		filter[0],
    89  		attributes,
    90  		nil)
    91  
    92  	sr, err := l.Search(searchRequest)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	t.Logf("TestSearch: %s -> num of entries = %d", searchRequest.Filter, len(sr.Entries))
    97  }
    98  
    99  func TestSearchStartTLS(t *testing.T) {
   100  	l, err := DialURL(ldapServer)
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	defer l.Close()
   105  
   106  	searchRequest := NewSearchRequest(
   107  		baseDN,
   108  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   109  		filter[0],
   110  		attributes,
   111  		nil)
   112  
   113  	sr, err := l.Search(searchRequest)
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	t.Logf("TestSearchStartTLS: %s -> num of entries = %d", searchRequest.Filter, len(sr.Entries))
   119  
   120  	t.Log("TestSearchStartTLS: upgrading with startTLS")
   121  	err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  
   126  	sr, err = l.Search(searchRequest)
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  
   131  	t.Logf("TestSearchStartTLS: %s -> num of entries = %d", searchRequest.Filter, len(sr.Entries))
   132  }
   133  
   134  func TestSearchWithPaging(t *testing.T) {
   135  	l, err := DialURL(ldapServer)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	defer l.Close()
   140  
   141  	err = l.UnauthenticatedBind("")
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	searchRequest := NewSearchRequest(
   147  		baseDN,
   148  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   149  		filter[2],
   150  		attributes,
   151  		nil)
   152  	sr, err := l.SearchWithPaging(searchRequest, 5)
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  
   157  	t.Logf("TestSearchWithPaging: %s -> num of entries = %d", searchRequest.Filter, len(sr.Entries))
   158  
   159  	searchRequest = NewSearchRequest(
   160  		baseDN,
   161  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   162  		filter[2],
   163  		attributes,
   164  		[]Control{NewControlPaging(5)})
   165  	sr, err = l.SearchWithPaging(searchRequest, 5)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  
   170  	t.Logf("TestSearchWithPaging: %s -> num of entries = %d", searchRequest.Filter, len(sr.Entries))
   171  
   172  	searchRequest = NewSearchRequest(
   173  		baseDN,
   174  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   175  		filter[2],
   176  		attributes,
   177  		[]Control{NewControlPaging(500)})
   178  	_, err = l.SearchWithPaging(searchRequest, 5)
   179  	if err == nil {
   180  		t.Fatal("expected an error when paging size in control in search request doesn't match size given in call, got none")
   181  	}
   182  }
   183  
   184  func searchGoroutine(t *testing.T, l *Conn, results chan *SearchResult, i int) {
   185  	searchRequest := NewSearchRequest(
   186  		baseDN,
   187  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   188  		filter[i],
   189  		attributes,
   190  		nil)
   191  	sr, err := l.Search(searchRequest)
   192  	if err != nil {
   193  		t.Error(err)
   194  		results <- nil
   195  		return
   196  	}
   197  	results <- sr
   198  }
   199  
   200  func testMultiGoroutineSearch(t *testing.T, TLS bool, startTLS bool) {
   201  	var l *Conn
   202  	var err error
   203  	if TLS {
   204  		l, err = DialURL(ldapsServer, DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))
   205  		if err != nil {
   206  			t.Fatal(err)
   207  		}
   208  		defer l.Close()
   209  	} else {
   210  		l, err = DialURL(ldapServer)
   211  		if err != nil {
   212  			t.Fatal(err)
   213  		}
   214  		defer l.Close()
   215  		if startTLS {
   216  			t.Log("TestMultiGoroutineSearch: using StartTLS...")
   217  			err := l.StartTLS(&tls.Config{InsecureSkipVerify: true})
   218  			if err != nil {
   219  				t.Fatal(err)
   220  			}
   221  		}
   222  	}
   223  
   224  	results := make([]chan *SearchResult, len(filter))
   225  	for i := range filter {
   226  		results[i] = make(chan *SearchResult)
   227  		go searchGoroutine(t, l, results[i], i)
   228  	}
   229  	for i := range filter {
   230  		sr := <-results[i]
   231  		if sr == nil {
   232  			t.Errorf("Did not receive results from goroutine for %q", filter[i])
   233  		} else {
   234  			t.Logf("TestMultiGoroutineSearch(%d): %s -> num of entries = %d", i, filter[i], len(sr.Entries))
   235  		}
   236  	}
   237  }
   238  
   239  func TestMultiGoroutineSearch(t *testing.T) {
   240  	testMultiGoroutineSearch(t, false, false)
   241  	testMultiGoroutineSearch(t, true, true)
   242  	testMultiGoroutineSearch(t, false, true)
   243  }
   244  
   245  func TestEscapeFilter(t *testing.T) {
   246  	if got, want := EscapeFilter("a\x00b(c)d*e\\f"), `a\00b\28c\29d\2ae\5cf`; got != want {
   247  		t.Errorf("Got %s, expected %s", want, got)
   248  	}
   249  	if got, want := EscapeFilter("Lučić"), `Lu\c4\8di\c4\87`; got != want {
   250  		t.Errorf("Got %s, expected %s", want, got)
   251  	}
   252  }
   253  
   254  func TestCompare(t *testing.T) {
   255  	l, err := DialURL(ldapServer)
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  	defer l.Close()
   260  
   261  	const dn = "cn=math mich,ou=User Groups,ou=Groups,dc=umich,dc=edu"
   262  	const attribute = "cn"
   263  	const value = "math mich"
   264  
   265  	sr, err := l.Compare(dn, attribute, value)
   266  	if err != nil {
   267  		t.Fatal(err)
   268  	}
   269  
   270  	t.Log("Compare result:", sr)
   271  }
   272  
   273  func TestMatchDNError(t *testing.T) {
   274  	l, err := DialURL(ldapServer)
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	defer l.Close()
   279  
   280  	const wrongBase = "ou=roups,dc=umich,dc=edu"
   281  
   282  	searchRequest := NewSearchRequest(
   283  		wrongBase,
   284  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   285  		filter[0],
   286  		attributes,
   287  		nil)
   288  
   289  	_, err = l.Search(searchRequest)
   290  	if err == nil {
   291  		t.Fatal("Expected Error, got nil")
   292  	}
   293  
   294  	t.Log("TestMatchDNError:", err)
   295  }
   296  
   297  func Test_addControlDescriptions(t *testing.T) {
   298  	type args struct {
   299  		packet *ber.Packet
   300  	}
   301  	tests := []struct {
   302  		name    string
   303  		args    args
   304  		wantErr bool
   305  	}{
   306  		{name: "timeBeforeExpiration", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x29, 0x30, 0x27, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0xa, 0x30, 0x8, 0xa0, 0x6, 0x80, 0x4, 0x7f, 0xff, 0xf6, 0x5c})}, wantErr: false},
   307  		{name: "graceAuthNsRemaining", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x26, 0x30, 0x24, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x7, 0x30, 0x5, 0xa0, 0x3, 0x81, 0x1, 0x11})}, wantErr: false},
   308  		{name: "passwordExpired", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x0})}, wantErr: false},
   309  		{name: "accountLocked", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x1})}, wantErr: false},
   310  		{name: "passwordModNotAllowed", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x3})}, wantErr: false},
   311  		{name: "mustSupplyOldPassword", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x4})}, wantErr: false},
   312  		{name: "insufficientPasswordQuality", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x5})}, wantErr: false},
   313  		{name: "passwordTooShort", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x6})}, wantErr: false},
   314  		{name: "passwordTooYoung", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x7})}, wantErr: false},
   315  		{name: "passwordInHistory", args: args{packet: ber.DecodePacket([]byte{0xa0, 0x24, 0x30, 0x22, 0x4, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x4, 0x5, 0x30, 0x3, 0x81, 0x1, 0x8})}, wantErr: false},
   316  	}
   317  	for _, tt := range tests {
   318  		t.Run(tt.name, func(t *testing.T) {
   319  			if err := addControlDescriptions(tt.args.packet); (err != nil) != tt.wantErr {
   320  				t.Errorf("addControlDescriptions() error = %v, wantErr %v", err, tt.wantErr)
   321  			}
   322  		})
   323  	}
   324  }
   325  
   326  func TestEscapeDN(t *testing.T) {
   327  	tests := []struct {
   328  		name string
   329  		dn   string
   330  		want string
   331  	}{
   332  		{name: "emptyString", dn: "", want: ""},
   333  		{name: "comma", dn: "test,user", want: "test\\,user"},
   334  		{name: "numberSign", dn: "#test#user#", want: "\\#test#user#"},
   335  		{name: "backslash", dn: "\\test\\user\\", want: "\\\\test\\\\user\\\\"},
   336  		{name: "whitespaces", dn: "  test user  ", want: "\\  test user \\ "},
   337  		{name: "nullByte", dn: "\u0000te\x00st\x00user" + string(rune(0)), want: "\\00te\\00st\\00user\\00"},
   338  		{name: "variousCharacters", dn: "test\"+,;<>\\-_user", want: "test\\\"\\+\\,\\;\\<\\>\\\\-_user"},
   339  		{name: "multiByteRunes", dn: "test\u0391user ", want: "test\u0391user\\ "},
   340  	}
   341  	for _, tt := range tests {
   342  		t.Run(tt.name, func(t *testing.T) {
   343  			if got := EscapeDN(tt.dn); got != tt.want {
   344  				t.Errorf("EscapeDN(%s) = %s, expected %s", tt.dn, got, tt.want)
   345  			}
   346  		})
   347  	}
   348  }
   349  
   350  func TestSearchAsync(t *testing.T) {
   351  	l, err := DialURL(ldapServer)
   352  	if err != nil {
   353  		t.Fatal(err)
   354  	}
   355  	defer l.Close()
   356  
   357  	searchRequest := NewSearchRequest(
   358  		baseDN,
   359  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   360  		filter[2],
   361  		attributes,
   362  		nil)
   363  
   364  	srs := make([]*Entry, 0)
   365  	ctx := context.Background()
   366  	r := l.SearchAsync(ctx, searchRequest, 64)
   367  	for r.Next() {
   368  		srs = append(srs, r.Entry())
   369  	}
   370  	if err := r.Err(); err != nil {
   371  		log.Fatal(err)
   372  	}
   373  
   374  	t.Logf("TestSearcAsync: %s -> num of entries = %d", searchRequest.Filter, len(srs))
   375  }
   376  
   377  func TestSearchAsyncAndCancel(t *testing.T) {
   378  	l, err := DialURL(ldapServer)
   379  	if err != nil {
   380  		t.Fatal(err)
   381  	}
   382  	defer l.Close()
   383  
   384  	searchRequest := NewSearchRequest(
   385  		baseDN,
   386  		ScopeWholeSubtree, DerefAlways, 0, 0, false,
   387  		filter[2],
   388  		attributes,
   389  		nil)
   390  
   391  	cancelNum := 10
   392  	srs := make([]*Entry, 0)
   393  	ctx, cancel := context.WithCancel(context.Background())
   394  	defer cancel()
   395  	r := l.SearchAsync(ctx, searchRequest, 0)
   396  	for r.Next() {
   397  		srs = append(srs, r.Entry())
   398  		if len(srs) == cancelNum {
   399  			cancel()
   400  		}
   401  	}
   402  	if err := r.Err(); err != nil {
   403  		log.Fatal(err)
   404  	}
   405  
   406  	if len(srs) > cancelNum+3 {
   407  		// the cancellation process is asynchronous,
   408  		// so it might get some entries after calling cancel()
   409  		t.Errorf("Got entries %d, expected < %d", len(srs), cancelNum+3)
   410  	}
   411  	t.Logf("TestSearchAsyncAndCancel: %s -> num of entries = %d", searchRequest.Filter, len(srs))
   412  }
   413  

View as plain text