package ldap import ( "errors" "testing" ldap "github.com/vjeantet/ldapserver" ) // fakeConn stands in for the server's unexported client type so that // per-connection bind state can be exercised in tests. type fakeConn struct { data interface{} } func (c *fakeConn) GetData() interface{} { return c.data } func (c *fakeConn) SetData(d interface{}) { c.data = d } func boundTestServer(c naClient) *server { fc := &fakeConn{} s := &server{l: testLogger(), c: c} s.nc = []string{"dc=netauth", "dc=example", "dc=com"} s.conn = func(*ldap.Message) connection { return fc } return s } func TestSearchRequiresBind(t *testing.T) { s := boundTestServer(&fakeClient{}) w := &recordingWriter{} s.handleSearchEntities(w, mustDecode(t, berSearchRequest("ou=entities,dc=netauth,dc=example,dc=com"))) if len(w.responses) != 1 { t.Fatalf("got %d responses, want exactly 1", len(w.responses)) } if code := resultCode(t, w.responses[0]); code != ldap.LDAPResultInsufficientAccessRights { t.Errorf("entities: result code is %d, want %d", code, ldap.LDAPResultInsufficientAccessRights) } w = &recordingWriter{} s.handleSearchGroups(w, mustDecode(t, berSearchRequest("ou=groups,dc=netauth,dc=example,dc=com"))) if code := resultCode(t, w.responses[0]); code != ldap.LDAPResultInsufficientAccessRights { t.Errorf("groups: result code is %d, want %d", code, ldap.LDAPResultInsufficientAccessRights) } } func TestSearchAfterBindSucceeds(t *testing.T) { s := boundTestServer(&fakeClient{}) // A successful bind marks the connection, so a follow-up search // is no longer refused for lack of authentication. s.handleBind(&recordingWriter{}, mustDecode(t, berBindRequest("uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com", "hunter2"))) w := &recordingWriter{} s.handleSearchEntities(w, mustDecode(t, berSearchRequest("ou=entities,dc=netauth,dc=example,dc=com"))) if code := resultCode(t, w.responses[len(w.responses)-1]); code == ldap.LDAPResultInsufficientAccessRights { t.Errorf("a bound connection was still refused") } } func TestFailedRebindClearsConnection(t *testing.T) { c := &fakeClient{authErr: errors.New("invalid password")} s := boundTestServer(c) // Mark the connection bound, then have a rebind fail; RFC 4511 // requires the connection to drop back to unauthenticated. s.markBound(&ldap.Message{}) w := &recordingWriter{} s.handleBind(w, mustDecode(t, berBindRequest("uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com", "wrong"))) if code := resultCode(t, w.responses[0]); code != ldap.LDAPResultInvalidCredentials { t.Fatalf("bind result code is %d, want %d", code, ldap.LDAPResultInvalidCredentials) } if s.bound(&ldap.Message{}) { t.Error("connection still marked bound after a failed bind") } }