// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package ldaputil import ( "testing" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestDialLDAP duplicates a potential panic that was // present in the previous version of TestDialLDAP, // then confirms its fix by passing. func TestDialLDAP(t *testing.T) { ldapClient := Client{ Logger: hclog.NewNullLogger(), LDAP: NewLDAP(), } ce := &ConfigEntry{ Url: "ldap://localhost:384654786", RequestTimeout: 3, } if _, err := ldapClient.DialLDAP(ce); err == nil { t.Fatal("expected error") } } func TestLDAPEscape(t *testing.T) { testcases := map[string]string{ "#test": "\\#test", "test,hello": "test\\,hello", "test,hel+lo": "test\\,hel\\+lo", "test\\hello": "test\\\\hello", " test ": "\\ test \\ ", "": "", `\`: `\\`, "trailing\000": `trailing\00`, "mid\000dle": `mid\00dle`, "\000": `\00`, "multiple\000\000": `multiple\00\00`, "backlash-before-null\\\000": `backlash-before-null\\\00`, "trailing\\": `trailing\\`, "double-escaping\\>": `double-escaping\\\>`, } for test, answer := range testcases { res := EscapeLDAPValue(test) if res != answer { t.Errorf("Failed to escape %s: %s != %s\n", test, res, answer) } } } func TestGetTLSConfigs(t *testing.T) { config := testConfig(t) if err := config.Validate(); err != nil { t.Fatal(err) } tlsConfig, err := getTLSConfig(config, "138.91.247.105") if err != nil { t.Fatal(err) } if tlsConfig == nil { t.Fatal("expected 1 TLS config because there's 1 url") } if tlsConfig.InsecureSkipVerify { t.Fatal("InsecureSkipVerify should be false because we should default to the most secure connection") } if tlsConfig.ServerName != "138.91.247.105" { t.Fatalf("expected ServerName of \"138.91.247.105\" but received %q", tlsConfig.ServerName) } expected := uint16(771) if tlsConfig.MinVersion != expected || tlsConfig.MaxVersion != expected { t.Fatal("expected TLS min and max version of 771 which corresponds with TLS 1.2 since TLS 1.1 and 1.0 have known vulnerabilities") } } func TestSIDBytesToString(t *testing.T) { testcases := map[string][]byte{ "S-1-5-21-2127521184-1604012920-1887927527-72713": {0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0xA0, 0x65, 0xCF, 0x7E, 0x78, 0x4B, 0x9B, 0x5F, 0xE7, 0x7C, 0x87, 0x70, 0x09, 0x1C, 0x01, 0x00}, "S-1-1-0": {0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}, "S-1-5": {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, } for answer, test := range testcases { res, err := sidBytesToString(test) if err != nil { t.Errorf("Failed to conver %#v: %s", test, err) } else if answer != res { t.Errorf("Failed to convert %#v: %s != %s", test, res, answer) } } } func TestClient_renderUserSearchFilter(t *testing.T) { t.Parallel() tests := []struct { name string conf *ConfigEntry username string want string errContains string }{ { name: "valid-default", username: "alice", conf: &ConfigEntry{ UserAttr: "cn", }, want: "(cn=alice)", }, { name: "escaped-malicious-filter", username: "foo@example.com)((((((((((((((((((((((((((((((((((((((userPrincipalName=foo", conf: &ConfigEntry{ UPNDomain: "example.com", UserFilter: "(&({{.UserAttr}}={{.Username}})({{.UserAttr}}=admin@example.com))", }, want: "(&(userPrincipalName=foo@example.com\\29\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28\\28userPrincipalName=foo@example.com)(userPrincipalName=admin@example.com))", }, { name: "bad-filter-unclosed-action", username: "alice", conf: &ConfigEntry{ UserFilter: "hello{{range", }, errContains: "search failed due to template compilation error", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { c := Client{ Logger: hclog.NewNullLogger(), LDAP: NewLDAP(), } f, err := c.RenderUserSearchFilter(tc.conf, tc.username) if tc.errContains != "" { require.Error(t, err) assert.ErrorContains(t, err, tc.errContains) return } require.NoError(t, err) assert.NotEmpty(t, f) assert.Equal(t, tc.want, f) }) } }