diff --git a/connector/ldap/ldap.go b/connector/ldap/ldap.go index 53f9062f55..2f20661f8e 100644 --- a/connector/ldap/ldap.go +++ b/connector/ldap/ldap.go @@ -9,6 +9,7 @@ import ( "fmt" "io/ioutil" "net" + "os" "gopkg.in/ldap.v2" @@ -88,8 +89,9 @@ type Config struct { // BindDN and BindPW for an application service account. The connector uses these // credentials to search for users and groups. - BindDN string `json:"bindDN"` - BindPW string `json:"bindPW"` + BindDN string `json:"bindDN"` + BindPW string `json:"bindPW"` + BindPWFromEnv string `json:bindPWFromEnv` // UsernamePrompt allows users to override the username attribute (displayed // in the username/password prompt). If unset, the handler will use @@ -237,6 +239,18 @@ func (c *Config) openConnector(logger log.Logger) (*ldapConnector, error) { } } + if c.BindPW == "" && c.BindPWFromEnv == "" { + return nil, fmt.Errorf("ldap: bindPW or bindPWFromEnv are required for the LDAP connector") + } + bindPW := c.BindPW + if c.BindPWFromEnv != "" { + if c.BindPW != "" { + return nil, fmt.Errorf("ldap: bindPW and bindPWFromEnv are exclusive for the LDAP connector") + } + bindPW = os.Getenv(c.BindPWFromEnv) + } + c.BindPW = bindPW + var ( host string err error diff --git a/connector/ldap/ldap_test.go b/connector/ldap/ldap_test.go index 1dc26afd64..bd49b31de2 100644 --- a/connector/ldap/ldap_test.go +++ b/connector/ldap/ldap_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "reflect" "testing" "time" @@ -46,6 +47,78 @@ type subtest struct { want connector.Identity } +func TestOpenForBindPW(t *testing.T) { + c := &Config{} + c.UserSearch.BaseDN = "ou=People,dc=example,dc=org" + c.UserSearch.NameAttr = "cn" + c.UserSearch.EmailAttr = "mail" + c.UserSearch.IDAttr = "DN" + c.UserSearch.Username = "cn" + c.Host = fmt.Sprintf("%s:%s", "127.0.0.1", "8080") + c.InsecureNoSSL = true + c.BindDN = "cn=admin,dc=example,dc=org" + + tests := []struct { + bindPW string + bindPWFromEnv string + expectedError error + }{ + { + bindPW: "", + bindPWFromEnv: "", + expectedError: fmt.Errorf("ldap: bindPW or bindPWFromEnv are required for the LDAP connector"), + }, + { + bindPW: "admin", + bindPWFromEnv: "admin", + expectedError: fmt.Errorf("ldap: bindPW and bindPWFromEnv are exclusive for the LDAP connector"), + }, + { + bindPW: "", + bindPWFromEnv: "LDAP_BIND_PW", + expectedError: nil, + }, + { + bindPW: "admin", + bindPWFromEnv: "", + expectedError: nil, + }, + } + + l := &logrus.Logger{Out: ioutil.Discard, Formatter: &logrus.TextFormatter{}} + + for _, tc := range tests { + if tc.bindPWFromEnv != "" { + os.Setenv(tc.bindPWFromEnv, "admin") + } + c.BindPW = tc.bindPW + c.BindPWFromEnv = tc.bindPWFromEnv + conn, err := c.openConnector(l) + if tc.expectedError != nil { + if conn != nil { + t.Errorf("Expected conn to be nil") + } + expectEquals(t, err, tc.expectedError) + } else { + if err != nil { + t.Errorf("Expected err to be nil") + } + expectEquals(t, conn.Config.Host, c.Host) + expectEquals(t, conn.Config.InsecureNoSSL, c.InsecureNoSSL) + expectEquals(t, conn.Config.UserSearch, c.UserSearch) + expectEquals(t, conn.Config.BindDN, c.BindDN) + if tc.bindPW != "" { + expectEquals(t, conn.Config.BindPW, tc.bindPW) + } else { + expectEquals(t, conn.Config.BindPW, os.Getenv(tc.bindPWFromEnv)) + } + } + if tc.bindPWFromEnv != "" { + os.Unsetenv(tc.bindPWFromEnv) + } + } +} + func TestQuery(t *testing.T) { schema := ` dn: ou=People,dc=example,dc=org @@ -1081,3 +1154,9 @@ func runTests(t *testing.T, schema string, connMethod connectionMethod, config * }) } } + +func expectEquals(t *testing.T, a interface{}, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Errorf("Expected %+v to equal %+v", a, b) + } +}