package ldap import ( "context" "errors" "testing" ldap "github.com/ps78674/ldapserver" pb "github.com/netauth/protocol" ) var _ naClient = (*fakeClient)(nil) type fakeClient struct { authErr error authCalls []string } func (f *fakeClient) AuthEntity(ctx context.Context, id, password string) error { f.authCalls = append(f.authCalls, id) return f.authErr } func (f *fakeClient) EntitySearch(context.Context, string) ([]*pb.Entity, error) { return nil, nil } func (f *fakeClient) EntityGroups(context.Context, string) ([]*pb.Group, error) { return nil, nil } func (f *fakeClient) GroupSearch(context.Context, string) ([]*pb.Group, error) { return nil, nil } func (f *fakeClient) GroupMembers(context.Context, string) ([]*pb.Entity, error) { return nil, nil } func TestAnonBindSingleResponse(t *testing.T) { c := &fakeClient{} s := &server{l: testLogger(), c: c} s.nc = []string{"dc=netauth", "dc=example", "dc=com"} s.allowAnon = true w := &recordingWriter{} s.handleBind(w, mustDecode(t, berBindRequest("", ""))) if len(w.responses) != 1 { t.Fatalf("got %d responses for an anonymous bind, want exactly 1", len(w.responses)) } if code := resultCode(t, w.responses[0]); code != ldap.LDAPResultSuccess { t.Errorf("result code is %d, want %d", code, ldap.LDAPResultSuccess) } if len(c.authCalls) != 0 { t.Errorf("an anonymous bind reached NetAuth with %v", c.authCalls) } } func TestAnonBindDisabled(t *testing.T) { s := &server{l: testLogger(), c: &fakeClient{}} s.nc = []string{"dc=netauth", "dc=example", "dc=com"} w := &recordingWriter{} s.handleBind(w, mustDecode(t, berBindRequest("", ""))) 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.LDAPResultInvalidDNSyntax { t.Errorf("result code is %d, want %d", code, ldap.LDAPResultInvalidDNSyntax) } } func TestBindValidCredentials(t *testing.T) { c := &fakeClient{} s := &server{l: testLogger(), c: c} s.nc = []string{"dc=netauth", "dc=example", "dc=com"} w := &recordingWriter{} s.handleBind(w, mustDecode(t, berBindRequest("uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com", "hunter2"))) 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.LDAPResultSuccess { t.Errorf("result code is %d, want %d", code, ldap.LDAPResultSuccess) } if len(c.authCalls) != 1 || c.authCalls[0] != "jsmith" { t.Errorf("NetAuth authenticated %v, want [jsmith]", c.authCalls) } } func TestBindInvalidCredentials(t *testing.T) { c := &fakeClient{authErr: errors.New("invalid password")} s := &server{l: testLogger(), c: c} s.nc = []string{"dc=netauth", "dc=example", "dc=com"} w := &recordingWriter{} s.handleBind(w, mustDecode(t, berBindRequest("uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com", "wrong"))) 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.LDAPResultInvalidCredentials { t.Errorf("result code is %d, want %d", code, ldap.LDAPResultInvalidCredentials) } } func TestBindUnsupportedMechanism(t *testing.T) { s := &server{l: testLogger(), c: &fakeClient{}} s.nc = []string{"dc=netauth", "dc=example", "dc=com"} // A bind request carrying SASL credentials rather than a simple // password must be refused outright. sasl := berElement(0x60, berInteger(3), berOctetString("uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com"), berElement(0xa3, berOctetString("GSSAPI")), ) msg := berElement(0x30, berInteger(1), sasl) w := &recordingWriter{} s.handleBind(w, mustDecode(t, msg)) 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.LDAPResultUnwillingToPerform { t.Errorf("result code is %d, want %d", code, ldap.LDAPResultUnwillingToPerform) } }