From 7c1cd1d1d16e3098eb35cf1d38902138abe96202 Mon Sep 17 00:00:00 2001 From: shimokubo Date: Wed, 17 May 2023 13:35:26 +0900 Subject: [PATCH 1/4] feat: enable DirSync control in search operation --- client.go | 1 + control.go | 92 +++++++++++++++++++++++++++++++++++++++++++++ control_test.go | 9 +++++ examples_test.go | 52 +++++++++++++++++++++++++ search.go | 63 +++++++++++++++++++++++++++++++ v3/client.go | 1 + v3/control.go | 92 +++++++++++++++++++++++++++++++++++++++++++++ v3/control_test.go | 9 +++++ v3/examples_test.go | 52 +++++++++++++++++++++++++ v3/search.go | 63 +++++++++++++++++++++++++++++++ 10 files changed, 434 insertions(+) diff --git a/client.go b/client.go index ee3c3696..b438d254 100644 --- a/client.go +++ b/client.go @@ -33,4 +33,5 @@ type Client interface { Search(*SearchRequest) (*SearchResult, error) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) + DirSync(searchRequest *SearchRequest, flags, maxAttrCount int64, cookie []byte) (*SearchResult, error) } diff --git a/control.go b/control.go index 2c5d6e32..8bbc026f 100644 --- a/control.go +++ b/control.go @@ -29,6 +29,16 @@ const ( ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417" // ControlTypeMicrosoftServerLinkTTL - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309" + // ControlTypeDirSync - Active Directory DirSync - https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx + ControlTypeDirSync = "1.2.840.113556.1.4.841" +) + +// Flags for DirSync control +const ( + DirSyncIncrementalValues int64 = 2147483648 + DirSyncPublicDataOnly int64 = 8192 + DirSyncAncestorsFirstOrder int64 = 2048 + DirSyncObjectSecurity int64 = 1 ) // ControlTypeMap maps controls to text descriptions @@ -40,6 +50,7 @@ var ControlTypeMap = map[string]string{ ControlTypeMicrosoftNotification: "Change Notification - Microsoft", ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft", ControlTypeMicrosoftServerLinkTTL: "Return TTL-DNs for link values with associated expiry times - Microsoft", + ControlTypeDirSync: "DirSync", } // Control defines an interface controls provide to encode and describe themselves @@ -490,6 +501,31 @@ func DecodeControl(packet *ber.Packet) (Control, error) { return NewControlMicrosoftServerLinkTTL(), nil case ControlTypeSubtreeDelete: return NewControlSubtreeDelete(), nil + case ControlTypeDirSync: + value.Description += " (DirSync)" + c := new(ControlDirSync) + if value.Value != nil { + valueChildren, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return nil, err + } + value.Data.Truncate(0) + value.Value = nil + value.AppendChild(valueChildren) + } + value = value.Children[0] + if len(value.Children) != 3 { // also on initial creation, Cookie is an empty string + return nil, fmt.Errorf("invalid number of children in dirSync control") + } + value.Description = "DirSync Control Value" + value.Children[0].Description = "Flags" + value.Children[1].Description = "MaxAttrCnt" + value.Children[2].Description = "Cookie" + c.Flags = value.Children[0].Value.(int64) + c.MaxAttrCnt = value.Children[1].Value.(int64) + c.Cookie = value.Children[2].Data.Bytes() + value.Children[2].Value = c.Cookie + return c, nil default: c := new(ControlString) c.ControlType = ControlType @@ -560,3 +596,59 @@ func encodeControls(controls []Control) *ber.Packet { } return packet } + +// ControlDirSync implements the control described in https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx +type ControlDirSync struct { + Flags int64 + MaxAttrCnt int64 + Cookie []byte +} + +// NewControlDirSync returns a dir sync control +func NewControlDirSync(flags int64, maxAttrCount int64, cookie []byte) *ControlDirSync { + return &ControlDirSync{ + Flags: flags, + MaxAttrCnt: maxAttrCount, + Cookie: cookie, + } +} + +// GetControlType returns the OID +func (c *ControlDirSync) GetControlType() string { + return ControlTypeDirSync +} + +// String returns a human-readable description +func (c *ControlDirSync) String() string { + return fmt.Sprintf("ControlType: %s (%q), Criticality: true, ControlValue: Flags: %d, MaxAttrCnt: %d", ControlTypeMap[ControlTypeDirSync], ControlTypeDirSync, c.Flags, c.MaxAttrCnt) +} + +// Encode returns the ber packet representation +func (c *ControlDirSync) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")")) + packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, true, "Criticality")) // must be true always + + val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)") + + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value") + seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags")) + seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCnt), "MaxAttrCount")) + + cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie") + if len(c.Cookie) != 0 { + cookie.Value = c.Cookie + cookie.Data.Write(c.Cookie) + } + seq.AppendChild(cookie) + + val.AppendChild(seq) + + packet.AppendChild(val) + return packet +} + +// SetCookie stores the given cookie in the dirSync control +func (c *ControlDirSync) SetCookie(cookie []byte) { + c.Cookie = cookie +} diff --git a/control_test.go b/control_test.go index 2bdbcec7..7bd4a8a5 100644 --- a/control_test.go +++ b/control_test.go @@ -43,6 +43,11 @@ func TestControlString(t *testing.T) { runControlTest(t, NewControlString("x", false, "")) } +func TestControlDirSync(t *testing.T) { + runControlTest(t, NewControlDirSync(DirSyncObjectSecurity, 1000, nil)) + runControlTest(t, NewControlDirSync(DirSyncObjectSecurity, 1000, []byte("I'm a cookie!"))) +} + func runControlTest(t *testing.T, originalControl Control) { header := "" if callerpc, _, line, ok := runtime.Caller(1); ok { @@ -116,6 +121,10 @@ func TestDescribeControlString(t *testing.T) { runAddControlDescriptions(t, NewControlString("x", false, ""), "Control Type ()") } +func TestDescribeControlDirSync(t *testing.T) { + runAddControlDescriptions(t, NewControlDirSync(DirSyncObjectSecurity, 1000, nil), "Control Type (DirSync)", "Criticality", "Control Value") +} + func runAddControlDescriptions(t *testing.T, originalControl Control, childDescriptions ...string) { header := "" if callerpc, _, line, ok := runtime.Caller(1); ok { diff --git a/examples_test.go b/examples_test.go index 60e674ae..f5742777 100644 --- a/examples_test.go +++ b/examples_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "log" + "time" ) // This example demonstrates how to bind a connection to an ldap user @@ -344,6 +345,57 @@ func ExampleControlPaging_manualPaging() { } } +// This example demonstrates how to use DirSync to manually execute a +// DirSync search request +func ExampleConn_DirSync() { + conn, err := Dial("tcp", "ad.example.org:389") + if err != nil { + log.Fatalf("Failed to connect: %s\n", err) + } + defer conn.Close() + + _, err = conn.SimpleBind(&SimpleBindRequest{ + Username: "cn=Some User,ou=people,dc=example,dc=org", + Password: "MySecretPass", + }) + + req := &SearchRequest{ + BaseDN: `DC=example,DC=org`, + Filter: `(&(objectClass=person)(!(objectClass=computer)))`, + Attributes: []string{"*"}, + Scope: ScopeWholeSubtree, + } + // This is the initial sync with all entries matching the filter + doMore := true + var cookie []byte + for doMore { + res, err := conn.DirSync(req, DirSyncObjectSecurity, 1000, cookie) + if err != nil { + log.Fatalf("failed to search: %s", err) + } + for _, entry := range res.Entries { + entry.Print() + } + ctrl := FindControl(res.Controls, ControlTypeDirSync) + if ctrl == nil || ctrl.(*ControlDirSync).Flags == 0 { + doMore = false + } + cookie = res.Cookie + } + // We're done with the initial sync. Now pull every 15 seconds for the + // updated entries - note that you get just the changes, not a full entry. + for { + res, err := conn.DirSync(req, DirSyncObjectSecurity, 1000, cookie) + if err != nil { + log.Fatalf("failed to search: %s", err) + } + for _, entry := range res.Entries { + entry.Print() + } + time.Sleep(15 * time.Second) + } +} + // This example demonstrates how to use EXTERNAL SASL with TLS client certificates. func ExampleConn_ExternalBind() { ldapCert := "/path/to/cert.pem" diff --git a/search.go b/search.go index 428a564d..61f904a6 100644 --- a/search.go +++ b/search.go @@ -352,6 +352,8 @@ type SearchResult struct { Referrals []string // Controls are the returned controls Controls []Control + // Cookie is the returned cookie + Cookie []byte } // Print outputs a human-readable description @@ -582,3 +584,64 @@ func unpackAttributes(children []*ber.Packet) []*EntryAttribute { return entries } + +// DirSync does a Search with dirSync Control. +func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte) (*SearchResult, error) { + var dirSyncControl *ControlDirSync + + control := FindControl(searchRequest.Controls, ControlTypeDirSync) + if control == nil { + dirSyncControl = NewControlDirSync(flags, maxAttrCount, cookie) + searchRequest.Controls = append(searchRequest.Controls, dirSyncControl) + } else { + castControl, ok := control.(*ControlDirSync) + if !ok { + return nil, fmt.Errorf("Expected DirSync control to be of type *ControlDirSync, got %v", control) + } + if castControl.Flags != flags { + return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", castControl.Flags, flags) + } + if castControl.MaxAttrCnt != maxAttrCount { + return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", castControl.MaxAttrCnt, maxAttrCount) + } + dirSyncControl = castControl + } + searchResult := new(SearchResult) + result, err := l.Search(searchRequest) + l.Debug.Printf("Looking for result...") + if err != nil { + return searchResult, err + } + if result == nil { + return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) + } + + for _, entry := range result.Entries { + searchResult.Entries = append(searchResult.Entries, entry) + } + for _, referral := range result.Referrals { + searchResult.Referrals = append(searchResult.Referrals, referral) + } + for _, control := range result.Controls { + searchResult.Controls = append(searchResult.Controls, control) + } + + l.Debug.Printf("Looking for DirSync Control...") + dirSyncResult := FindControl(result.Controls, ControlTypeDirSync) + if dirSyncResult == nil { + dirSyncControl = nil + l.Debug.Printf("Could not find dirSyncControl control. Breaking...") + return searchResult, nil + } + + cookie = dirSyncResult.(*ControlDirSync).Cookie + if len(cookie) == 0 { + dirSyncControl = nil + l.Debug.Printf("Could not find cookie. Breaking...") + return searchResult, nil + } + dirSyncControl.SetCookie(cookie) + searchResult.Cookie = cookie + + return searchResult, nil +} diff --git a/v3/client.go b/v3/client.go index ee3c3696..b438d254 100644 --- a/v3/client.go +++ b/v3/client.go @@ -33,4 +33,5 @@ type Client interface { Search(*SearchRequest) (*SearchResult, error) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) + DirSync(searchRequest *SearchRequest, flags, maxAttrCount int64, cookie []byte) (*SearchResult, error) } diff --git a/v3/control.go b/v3/control.go index ad405faf..0be697e2 100644 --- a/v3/control.go +++ b/v3/control.go @@ -34,6 +34,16 @@ const ( ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417" // ControlTypeMicrosoftServerLinkTTL - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309" + // ControlTypeDirSync - Active Directory DirSync - https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx + ControlTypeDirSync = "1.2.840.113556.1.4.841" +) + +// Flags for DirSync control +const ( + DirSyncIncrementalValues int64 = 2147483648 + DirSyncPublicDataOnly int64 = 8192 + DirSyncAncestorsFirstOrder int64 = 2048 + DirSyncObjectSecurity int64 = 1 ) // ControlTypeMap maps controls to text descriptions @@ -47,6 +57,7 @@ var ControlTypeMap = map[string]string{ ControlTypeMicrosoftServerLinkTTL: "Return TTL-DNs for link values with associated expiry times - Microsoft", ControlTypeServerSideSorting: "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)", ControlTypeServerSideSortingResult: "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)", + ControlTypeDirSync: "DirSync", } // Control defines an interface controls provide to encode and describe themselves @@ -501,6 +512,31 @@ func DecodeControl(packet *ber.Packet) (Control, error) { return NewControlServerSideSorting(value) case ControlTypeServerSideSortingResult: return NewControlServerSideSortingResult(value) + case ControlTypeDirSync: + value.Description += " (DirSync)" + c := new(ControlDirSync) + if value.Value != nil { + valueChildren, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return nil, err + } + value.Data.Truncate(0) + value.Value = nil + value.AppendChild(valueChildren) + } + value = value.Children[0] + if len(value.Children) != 3 { // also on initial creation, Cookie is an empty string + return nil, fmt.Errorf("invalid number of children in dirSync control") + } + value.Description = "DirSync Control Value" + value.Children[0].Description = "Flags" + value.Children[1].Description = "MaxAttrCnt" + value.Children[2].Description = "Cookie" + c.Flags = value.Children[0].Value.(int64) + c.MaxAttrCnt = value.Children[1].Value.(int64) + c.Cookie = value.Children[2].Data.Bytes() + value.Children[2].Value = c.Cookie + return c, nil default: c := new(ControlString) c.ControlType = ControlType @@ -572,6 +608,62 @@ func encodeControls(controls []Control) *ber.Packet { return packet } +// ControlDirSync implements the control described in https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx +type ControlDirSync struct { + Flags int64 + MaxAttrCnt int64 + Cookie []byte +} + +// NewControlDirSync returns a dir sync control +func NewControlDirSync(flags int64, maxAttrCount int64, cookie []byte) *ControlDirSync { + return &ControlDirSync{ + Flags: flags, + MaxAttrCnt: maxAttrCount, + Cookie: cookie, + } +} + +// GetControlType returns the OID +func (c *ControlDirSync) GetControlType() string { + return ControlTypeDirSync +} + +// String returns a human-readable description +func (c *ControlDirSync) String() string { + return fmt.Sprintf("ControlType: %s (%q), Criticality: true, ControlValue: Flags: %d, MaxAttrCnt: %d", ControlTypeMap[ControlTypeDirSync], ControlTypeDirSync, c.Flags, c.MaxAttrCnt) +} + +// Encode returns the ber packet representation +func (c *ControlDirSync) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")")) + packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, true, "Criticality")) // must be true always + + val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)") + + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value") + seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags")) + seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCnt), "MaxAttrCount")) + + cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie") + if len(c.Cookie) != 0 { + cookie.Value = c.Cookie + cookie.Data.Write(c.Cookie) + } + seq.AppendChild(cookie) + + val.AppendChild(seq) + + packet.AppendChild(val) + return packet +} + +// SetCookie stores the given cookie in the dirSync control +func (c *ControlDirSync) SetCookie(cookie []byte) { + c.Cookie = cookie +} + // ControlServerSideSorting type SortKey struct { diff --git a/v3/control_test.go b/v3/control_test.go index 41e5329c..0a88878f 100644 --- a/v3/control_test.go +++ b/v3/control_test.go @@ -43,6 +43,11 @@ func TestControlString(t *testing.T) { runControlTest(t, NewControlString("x", false, "")) } +func TestControlDirSync(t *testing.T) { + runControlTest(t, NewControlDirSync(DirSyncObjectSecurity, 1000, nil)) + runControlTest(t, NewControlDirSync(DirSyncObjectSecurity, 1000, []byte("I'm a cookie!"))) +} + func runControlTest(t *testing.T, originalControl Control) { header := "" if callerpc, _, line, ok := runtime.Caller(1); ok { @@ -116,6 +121,10 @@ func TestDescribeControlString(t *testing.T) { runAddControlDescriptions(t, NewControlString("x", false, ""), "Control Type ()") } +func TestDescribeControlDirSync(t *testing.T) { + runAddControlDescriptions(t, NewControlDirSync(DirSyncObjectSecurity, 1000, nil), "Control Type (DirSync)", "Criticality", "Control Value") +} + func runAddControlDescriptions(t *testing.T, originalControl Control, childDescriptions ...string) { header := "" if callerpc, _, line, ok := runtime.Caller(1); ok { diff --git a/v3/examples_test.go b/v3/examples_test.go index 60e674ae..f5742777 100644 --- a/v3/examples_test.go +++ b/v3/examples_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "log" + "time" ) // This example demonstrates how to bind a connection to an ldap user @@ -344,6 +345,57 @@ func ExampleControlPaging_manualPaging() { } } +// This example demonstrates how to use DirSync to manually execute a +// DirSync search request +func ExampleConn_DirSync() { + conn, err := Dial("tcp", "ad.example.org:389") + if err != nil { + log.Fatalf("Failed to connect: %s\n", err) + } + defer conn.Close() + + _, err = conn.SimpleBind(&SimpleBindRequest{ + Username: "cn=Some User,ou=people,dc=example,dc=org", + Password: "MySecretPass", + }) + + req := &SearchRequest{ + BaseDN: `DC=example,DC=org`, + Filter: `(&(objectClass=person)(!(objectClass=computer)))`, + Attributes: []string{"*"}, + Scope: ScopeWholeSubtree, + } + // This is the initial sync with all entries matching the filter + doMore := true + var cookie []byte + for doMore { + res, err := conn.DirSync(req, DirSyncObjectSecurity, 1000, cookie) + if err != nil { + log.Fatalf("failed to search: %s", err) + } + for _, entry := range res.Entries { + entry.Print() + } + ctrl := FindControl(res.Controls, ControlTypeDirSync) + if ctrl == nil || ctrl.(*ControlDirSync).Flags == 0 { + doMore = false + } + cookie = res.Cookie + } + // We're done with the initial sync. Now pull every 15 seconds for the + // updated entries - note that you get just the changes, not a full entry. + for { + res, err := conn.DirSync(req, DirSyncObjectSecurity, 1000, cookie) + if err != nil { + log.Fatalf("failed to search: %s", err) + } + for _, entry := range res.Entries { + entry.Print() + } + time.Sleep(15 * time.Second) + } +} + // This example demonstrates how to use EXTERNAL SASL with TLS client certificates. func ExampleConn_ExternalBind() { ldapCert := "/path/to/cert.pem" diff --git a/v3/search.go b/v3/search.go index 2fae94f4..1dab6b57 100644 --- a/v3/search.go +++ b/v3/search.go @@ -354,6 +354,8 @@ type SearchResult struct { Referrals []string // Controls are the returned controls Controls []Control + // Cookie is the returned cookie + Cookie []byte } // Print outputs a human-readable description @@ -584,3 +586,64 @@ func unpackAttributes(children []*ber.Packet) []*EntryAttribute { return entries } + +// DirSync does a Search with dirSync Control. +func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte) (*SearchResult, error) { + var dirSyncControl *ControlDirSync + + control := FindControl(searchRequest.Controls, ControlTypeDirSync) + if control == nil { + dirSyncControl = NewControlDirSync(flags, maxAttrCount, cookie) + searchRequest.Controls = append(searchRequest.Controls, dirSyncControl) + } else { + castControl, ok := control.(*ControlDirSync) + if !ok { + return nil, fmt.Errorf("Expected DirSync control to be of type *ControlDirSync, got %v", control) + } + if castControl.Flags != flags { + return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", castControl.Flags, flags) + } + if castControl.MaxAttrCnt != maxAttrCount { + return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", castControl.MaxAttrCnt, maxAttrCount) + } + dirSyncControl = castControl + } + searchResult := new(SearchResult) + result, err := l.Search(searchRequest) + l.Debug.Printf("Looking for result...") + if err != nil { + return searchResult, err + } + if result == nil { + return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) + } + + for _, entry := range result.Entries { + searchResult.Entries = append(searchResult.Entries, entry) + } + for _, referral := range result.Referrals { + searchResult.Referrals = append(searchResult.Referrals, referral) + } + for _, control := range result.Controls { + searchResult.Controls = append(searchResult.Controls, control) + } + + l.Debug.Printf("Looking for DirSync Control...") + dirSyncResult := FindControl(result.Controls, ControlTypeDirSync) + if dirSyncResult == nil { + dirSyncControl = nil + l.Debug.Printf("Could not find dirSyncControl control. Breaking...") + return searchResult, nil + } + + cookie = dirSyncResult.(*ControlDirSync).Cookie + if len(cookie) == 0 { + dirSyncControl = nil + l.Debug.Printf("Could not find cookie. Breaking...") + return searchResult, nil + } + dirSyncControl.SetCookie(cookie) + searchResult.Cookie = cookie + + return searchResult, nil +} From b0c36f2beeff30eea92e1f844c37a108516fd486 Mon Sep 17 00:00:00 2001 From: shimokubo Date: Wed, 17 May 2023 14:16:46 +0900 Subject: [PATCH 2/4] fix: fixed ineffectual assignment to err --- examples_test.go | 3 +++ v3/examples_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examples_test.go b/examples_test.go index f5742777..4898cba0 100644 --- a/examples_test.go +++ b/examples_test.go @@ -358,6 +358,9 @@ func ExampleConn_DirSync() { Username: "cn=Some User,ou=people,dc=example,dc=org", Password: "MySecretPass", }) + if err != nil { + log.Fatalf("failed to bind: %s", err) + } req := &SearchRequest{ BaseDN: `DC=example,DC=org`, diff --git a/v3/examples_test.go b/v3/examples_test.go index f5742777..4898cba0 100644 --- a/v3/examples_test.go +++ b/v3/examples_test.go @@ -358,6 +358,9 @@ func ExampleConn_DirSync() { Username: "cn=Some User,ou=people,dc=example,dc=org", Password: "MySecretPass", }) + if err != nil { + log.Fatalf("failed to bind: %s", err) + } req := &SearchRequest{ BaseDN: `DC=example,DC=org`, From e64703a61ba7c7dc94a7a135d7521f10b4fce10c Mon Sep 17 00:00:00 2001 From: shimokubo Date: Wed, 17 May 2023 14:29:01 +0900 Subject: [PATCH 3/4] fix: fixed should replace loop with error --- search.go | 12 +++--------- v3/search.go | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/search.go b/search.go index 61f904a6..29dfeb44 100644 --- a/search.go +++ b/search.go @@ -616,15 +616,9 @@ func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount i return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) } - for _, entry := range result.Entries { - searchResult.Entries = append(searchResult.Entries, entry) - } - for _, referral := range result.Referrals { - searchResult.Referrals = append(searchResult.Referrals, referral) - } - for _, control := range result.Controls { - searchResult.Controls = append(searchResult.Controls, control) - } + searchResult.Entries = append(searchResult.Entries, result.Entries...) + searchResult.Referrals = append(searchResult.Referrals, result.Referrals...) + searchResult.Controls = append(searchResult.Controls, result.Controls...) l.Debug.Printf("Looking for DirSync Control...") dirSyncResult := FindControl(result.Controls, ControlTypeDirSync) diff --git a/v3/search.go b/v3/search.go index 1dab6b57..36b8c7c9 100644 --- a/v3/search.go +++ b/v3/search.go @@ -618,15 +618,9 @@ func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount i return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) } - for _, entry := range result.Entries { - searchResult.Entries = append(searchResult.Entries, entry) - } - for _, referral := range result.Referrals { - searchResult.Referrals = append(searchResult.Referrals, referral) - } - for _, control := range result.Controls { - searchResult.Controls = append(searchResult.Controls, control) - } + searchResult.Entries = append(searchResult.Entries, result.Entries...) + searchResult.Referrals = append(searchResult.Referrals, result.Referrals...) + searchResult.Controls = append(searchResult.Controls, result.Controls...) l.Debug.Printf("Looking for DirSync Control...") dirSyncResult := FindControl(result.Controls, ControlTypeDirSync) From 777765946dfb6bbf017f49e39dc0dff103972824 Mon Sep 17 00:00:00 2001 From: shimokubo Date: Sat, 20 May 2023 22:48:09 +0900 Subject: [PATCH 4/4] fix: not include Cookie in SearchResult struct --- examples_test.go | 2 +- search.go | 3 --- v3/examples_test.go | 2 +- v3/search.go | 3 --- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/examples_test.go b/examples_test.go index 4898cba0..d788e4f5 100644 --- a/examples_test.go +++ b/examples_test.go @@ -383,7 +383,7 @@ func ExampleConn_DirSync() { if ctrl == nil || ctrl.(*ControlDirSync).Flags == 0 { doMore = false } - cookie = res.Cookie + cookie = ctrl.(*ControlDirSync).Cookie } // We're done with the initial sync. Now pull every 15 seconds for the // updated entries - note that you get just the changes, not a full entry. diff --git a/search.go b/search.go index 29dfeb44..ef3119b9 100644 --- a/search.go +++ b/search.go @@ -352,8 +352,6 @@ type SearchResult struct { Referrals []string // Controls are the returned controls Controls []Control - // Cookie is the returned cookie - Cookie []byte } // Print outputs a human-readable description @@ -635,7 +633,6 @@ func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount i return searchResult, nil } dirSyncControl.SetCookie(cookie) - searchResult.Cookie = cookie return searchResult, nil } diff --git a/v3/examples_test.go b/v3/examples_test.go index 4898cba0..d788e4f5 100644 --- a/v3/examples_test.go +++ b/v3/examples_test.go @@ -383,7 +383,7 @@ func ExampleConn_DirSync() { if ctrl == nil || ctrl.(*ControlDirSync).Flags == 0 { doMore = false } - cookie = res.Cookie + cookie = ctrl.(*ControlDirSync).Cookie } // We're done with the initial sync. Now pull every 15 seconds for the // updated entries - note that you get just the changes, not a full entry. diff --git a/v3/search.go b/v3/search.go index 36b8c7c9..9c0ccd07 100644 --- a/v3/search.go +++ b/v3/search.go @@ -354,8 +354,6 @@ type SearchResult struct { Referrals []string // Controls are the returned controls Controls []Control - // Cookie is the returned cookie - Cookie []byte } // Print outputs a human-readable description @@ -637,7 +635,6 @@ func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount i return searchResult, nil } dirSyncControl.SetCookie(cookie) - searchResult.Cookie = cookie return searchResult, nil }