-
Notifications
You must be signed in to change notification settings - Fork 365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add DirSync Control / Search #110
Changes from 2 commits
168a848
2302d93
f9f1bf2
408c0fa
930a257
dfca44e
bb7a9ca
827639d
81a4058
8a26cc6
eaa36e3
ad451f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,13 +22,24 @@ const ( | |
ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5" | ||
// ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296 | ||
ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2" | ||
// 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 | ||
var ControlTypeMap = map[string]string{ | ||
ControlTypePaging: "Paging", | ||
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft", | ||
ControlTypeManageDsaIT: "Manage DSA IT", | ||
ControlTypeDirSync: "DirSync", | ||
} | ||
|
||
// Control defines an interface controls provide to encode and describe themselves | ||
|
@@ -377,6 +388,26 @@ func DecodeControl(packet *ber.Packet) Control { | |
value.Value = c.Expire | ||
|
||
return c | ||
case ControlTypeDirSync: | ||
value.Description += " (DirSync)" | ||
c := new(ControlDirSync) | ||
if value.Value != nil { | ||
valueChildren := ber.DecodePacket(value.Data.Bytes()) | ||
value.Data.Truncate(0) | ||
value.Value = nil | ||
value.AppendChild(valueChildren) | ||
} | ||
value = value.Children[0] | ||
value.Description = "DirSync Control Value" | ||
value.Children[0].Description = "Flags" | ||
value.Children[1].Description = "MaxAttrCnt" | ||
value.Children[2].Description = "Cookie" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this safe; assuming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the spec says there must be always 3, the cookie will be an empty string if it's unset (which only happens when the client sends the first time |
||
c.Flags = value.Children[0].Value.(int64) | ||
c.MaxAttrCnt = value.Children[0].Value.(int64) | ||
c.Cookie = value.Children[2].Data.Bytes() | ||
value.Children[2].Value = c.Cookie | ||
return c | ||
|
||
default: | ||
c := new(ControlString) | ||
c.ControlType = ControlType | ||
|
@@ -418,3 +449,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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
package ldap | ||
|
||
import ( | ||
"log" | ||
"reflect" | ||
"testing" | ||
"time" | ||
) | ||
|
||
// TestNewEntry tests that repeated calls to NewEntry return the same value with the same input | ||
|
@@ -29,3 +31,48 @@ func TestNewEntry(t *testing.T) { | |
iteration = iteration + 1 | ||
} | ||
} | ||
|
||
func ExampleDirSync() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there tests that could be added too? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, encoding / decoding in control_test.go |
||
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, | ||
} | ||
doMore := true | ||
for doMore { | ||
res, err := conn.DirSync(req, DirSyncObjectSecurity, 1000) | ||
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 | ||
} | ||
} | ||
for { | ||
res, err := conn.DirSync(req, DirSyncObjectSecurity, 1000) | ||
if err != nil { | ||
log.Fatalf("failed to search: %s", err) | ||
} | ||
for _, entry := range res.Entries { | ||
entry.Print() | ||
} | ||
time.Sleep(15 * time.Second) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason
DecodePacketErr
is not used and checked here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
laziness? ;-) this started as a copy of the paging control above...