package ldap import ( "bytes" "fmt" "reflect" "runtime" "testing" ber "github.com/go-asn1-ber/asn1-ber" ) func TestControlPaging(t *testing.T) { runControlTest(t, NewControlPaging(0)) runControlTest(t, NewControlPaging(100)) } func TestControlManageDsaIT(t *testing.T) { runControlTest(t, NewControlManageDsaIT(true)) runControlTest(t, NewControlManageDsaIT(false)) } func TestControlMicrosoftNotification(t *testing.T) { runControlTest(t, NewControlMicrosoftNotification()) } func TestControlMicrosoftShowDeleted(t *testing.T) { runControlTest(t, NewControlMicrosoftShowDeleted()) } func TestControlMicrosoftServerLinkTTL(t *testing.T) { runControlTest(t, NewControlMicrosoftServerLinkTTL()) } func TestControlSubtreeDelete(t *testing.T) { runControlTest(t, NewControlSubtreeDelete()) } func TestControlString(t *testing.T) { runControlTest(t, NewControlString("x", true, "y")) runControlTest(t, NewControlString("x", true, "")) runControlTest(t, NewControlString("x", false, "y")) runControlTest(t, NewControlString("x", false, "")) } func TestControlDirSync(t *testing.T) { runControlTest(t, NewRequestControlDirSync(DirSyncObjectSecurity, 1000, nil)) runControlTest(t, NewRequestControlDirSync(DirSyncObjectSecurity, 1000, []byte("I'm a cookie!"))) } func runControlTest(t *testing.T, originalControl Control) { header := "" if callerpc, _, line, ok := runtime.Caller(1); ok { if caller := runtime.FuncForPC(callerpc); caller != nil { header = fmt.Sprintf("%s:%d: ", caller.Name(), line) } } encodedPacket := originalControl.Encode() encodedBytes := encodedPacket.Bytes() // Decode directly from the encoded packet (ensures Value is correct) fromPacket, err := DecodeControl(encodedPacket) if err != nil { t.Errorf("%sdecoding encoded bytes control failed: %s", header, err) } if !bytes.Equal(encodedBytes, fromPacket.Encode().Bytes()) { t.Errorf("%sround-trip from encoded packet failed", header) } if reflect.TypeOf(originalControl) != reflect.TypeOf(fromPacket) { t.Errorf("%sgot different type decoding from encoded packet: %T vs %T", header, fromPacket, originalControl) } // Decode from the wire bytes (ensures ber-encoding is correct) pkt, err := ber.DecodePacketErr(encodedBytes) if err != nil { t.Errorf("%sdecoding encoded bytes failed: %s", header, err) } fromBytes, err := DecodeControl(pkt) if err != nil { t.Errorf("%sdecoding control failed: %s", header, err) } if !bytes.Equal(encodedBytes, fromBytes.Encode().Bytes()) { t.Errorf("%sround-trip from encoded bytes failed", header) } if reflect.TypeOf(originalControl) != reflect.TypeOf(fromPacket) { t.Errorf("%sgot different type decoding from encoded bytes: %T vs %T", header, fromBytes, originalControl) } } func TestDescribeControlManageDsaIT(t *testing.T) { runAddControlDescriptions(t, NewControlManageDsaIT(false), "Control Type (Manage DSA IT)") runAddControlDescriptions(t, NewControlManageDsaIT(true), "Control Type (Manage DSA IT)", "Criticality") } func TestDescribeControlPaging(t *testing.T) { runAddControlDescriptions(t, NewControlPaging(100), "Control Type (Paging)", "Control Value (Paging)") runAddControlDescriptions(t, NewControlPaging(0), "Control Type (Paging)", "Control Value (Paging)") } func TestDescribeControlSubtreeDelete(t *testing.T) { runAddControlDescriptions(t, NewControlSubtreeDelete(), "Control Type (Subtree Delete Control)") } func TestDescribeControlMicrosoftNotification(t *testing.T) { runAddControlDescriptions(t, NewControlMicrosoftNotification(), "Control Type (Change Notification - Microsoft)") } func TestDescribeControlMicrosoftShowDeleted(t *testing.T) { runAddControlDescriptions(t, NewControlMicrosoftShowDeleted(), "Control Type (Show Deleted Objects - Microsoft)") } func TestDescribeControlMicrosoftServerLinkTTL(t *testing.T) { runAddControlDescriptions(t, NewControlMicrosoftServerLinkTTL(), "Control Type (Return TTL-DNs for link values with associated expiry times - Microsoft)") } func TestDescribeControlString(t *testing.T) { runAddControlDescriptions(t, NewControlString("x", true, "y"), "Control Type ()", "Criticality", "Control Value") runAddControlDescriptions(t, NewControlString("x", true, ""), "Control Type ()", "Criticality") runAddControlDescriptions(t, NewControlString("x", false, "y"), "Control Type ()", "Control Value") runAddControlDescriptions(t, NewControlString("x", false, ""), "Control Type ()") } func TestDescribeControlDirSync(t *testing.T) { runAddControlDescriptions(t, NewRequestControlDirSync(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 { if caller := runtime.FuncForPC(callerpc); caller != nil { header = fmt.Sprintf("%s:%d: ", caller.Name(), line) } } encodedControls := encodeControls([]Control{originalControl}) _ = addControlDescriptions(encodedControls) encodedPacket := encodedControls.Children[0] if len(encodedPacket.Children) != len(childDescriptions) { t.Errorf("%sinvalid number of children: %d != %d", header, len(encodedPacket.Children), len(childDescriptions)) } for i, desc := range childDescriptions { if encodedPacket.Children[i].Description != desc { t.Errorf("%sdescription not as expected: %s != %s", header, encodedPacket.Children[i].Description, desc) } } } func TestDecodeControl(t *testing.T) { type args struct { packet *ber.Packet } tests := []struct { name string args args want Control wantErr bool }{ { 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})}, want: &ControlBeheraPasswordPolicy{Expire: 2147481180, Grace: -1, Error: -1, ErrorString: ""}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: 17, Error: -1, ErrorString: ""}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 0, ErrorString: "Password expired"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 1, ErrorString: "Account locked"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 3, ErrorString: "Policy prevents password modification"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 4, ErrorString: "Policy requires old password in order to change password"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 5, ErrorString: "Password fails quality checks"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 6, ErrorString: "Password is too short for policy"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 7, ErrorString: "Password has been changed too recently"}, wantErr: false, }, { 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})}, want: &ControlBeheraPasswordPolicy{Expire: -1, Grace: -1, Error: 8, ErrorString: "New password is in list of old passwords"}, wantErr: false, }, } for i := range tests { err := addControlDescriptions(tests[i].args.packet) if err != nil { t.Fatal(err) } tests[i].args.packet = tests[i].args.packet.Children[0] } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := DecodeControl(tt.args.packet) if (err != nil) != tt.wantErr { t.Errorf("DecodeControl() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("DecodeControl() got = %v, want %v", got, tt.want) } }) } } func TestControlServerSideSortingDecoding(t *testing.T) { control := NewControlServerSideSortingWithSortKeys([]*SortKey{{ MatchingRule: "foo", AttributeType: "foobar", Reverse: true, }, { MatchingRule: "foo", AttributeType: "foobar", Reverse: false, }, { MatchingRule: "", AttributeType: "", Reverse: false, }, { MatchingRule: "totoRule", AttributeType: "", Reverse: false, }, { MatchingRule: "", AttributeType: "totoType", Reverse: false, }}) controlDecoded, err := NewControlServerSideSorting(control.Encode()) if err != nil { t.Fatal(err) } if control.GetControlType() != controlDecoded.GetControlType() { t.Fatalf("control type mismatch: control:%s - decoded:%s", control.GetControlType(), controlDecoded.GetControlType()) } if len(control.SortKeys) != len(controlDecoded.SortKeys) { t.Fatalf("sort keys length mismatch (control: %d - decoded: %d)", len(control.SortKeys), len(controlDecoded.SortKeys)) } for i, sk := range control.SortKeys { dsk := controlDecoded.SortKeys[i] if sk.AttributeType != dsk.AttributeType { t.Fatalf("attribute type mismatch for sortkey %d", i) } if sk.MatchingRule != dsk.MatchingRule { t.Fatalf("matching rule mismatch for sortkey %d", i) } if sk.Reverse != dsk.Reverse { t.Fatalf("reverse mismtach for sortkey %d", i) } } }