mirror of
https://github.com/netauth/ldap.git
synced 2026-11-03 11:43:44 -06:00
140 lines
4.2 KiB
Go
140 lines
4.2 KiB
Go
package ldap
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/ps78674/goldap/message"
|
|
|
|
pb "github.com/netauth/protocol"
|
|
)
|
|
|
|
func strptr(s string) *string { return &s }
|
|
func intptr(i int32) *int32 { return &i }
|
|
|
|
// messageSelection builds an AttributeSelection from plain strings.
|
|
func messageSelection(vals []string) message.AttributeSelection {
|
|
if vals == nil {
|
|
return nil
|
|
}
|
|
sel := make(message.AttributeSelection, len(vals))
|
|
for i, v := range vals {
|
|
sel[i] = message.LDAPString(v)
|
|
}
|
|
return sel
|
|
}
|
|
|
|
func testEntity(id string, number int32) *pb.Entity {
|
|
return &pb.Entity{ID: strptr(id), Number: intptr(number)}
|
|
}
|
|
|
|
func testGroup(name string, number int32) *pb.Group {
|
|
return &pb.Group{Name: strptr(name), Number: intptr(number)}
|
|
}
|
|
|
|
func TestAttributeRequested(t *testing.T) {
|
|
cases := []struct {
|
|
selection []string
|
|
name string
|
|
want bool
|
|
}{
|
|
{nil, "memberOf", true},
|
|
{[]string{"*"}, "memberOf", true},
|
|
{[]string{"uid", "memberOf"}, "memberOf", true},
|
|
{[]string{"uid"}, "memberOf", false},
|
|
{[]string{"UID"}, "uid", true},
|
|
{[]string{"1.1"}, "memberOf", false},
|
|
{[]string{"member;binary"}, "member", true},
|
|
{[]string{"uid"}, "member", false},
|
|
}
|
|
for _, tc := range cases {
|
|
sel := messageSelection(tc.selection)
|
|
if got := attributeRequested(sel, tc.name); got != tc.want {
|
|
t.Errorf("selection %v for %q: got %t, want %t", tc.selection, tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// bind marks the test connection as authenticated so a search is not
|
|
// refused before it reaches the handler logic under test.
|
|
func bind(t *testing.T, s *server) {
|
|
t.Helper()
|
|
s.handleBind(&recordingWriter{}, mustDecode(t,
|
|
berBindRequest("uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com", "hunter2")))
|
|
}
|
|
|
|
// Membership costs one NetAuth round trip per entry, so it must only
|
|
// happen when the client actually asked for the attribute.
|
|
func TestEntitySearchSkipsUnrequestedGroups(t *testing.T) {
|
|
c := &fakeClient{
|
|
entitySearchResults: []*pb.Entity{testEntity("jsmith", 1000)},
|
|
entityGroupsResults: map[string][]*pb.Group{"jsmith": {testGroup("admins", 10)}},
|
|
}
|
|
s := boundTestServer(c)
|
|
bind(t, s)
|
|
w := &recordingWriter{}
|
|
|
|
s.handleSearchEntities(w, mustDecode(t,
|
|
berSearchRequestWithAttrs("ou=entities,dc=netauth,dc=example,dc=com", "uid")))
|
|
|
|
if len(w.responses) < 2 {
|
|
t.Fatalf("the search did not return any entries: %d responses", len(w.responses))
|
|
}
|
|
if len(c.entityGroupsCalls) != 0 {
|
|
t.Errorf("EntityGroups was called %d times for a search that did not request memberOf", len(c.entityGroupsCalls))
|
|
}
|
|
}
|
|
|
|
func TestEntitySearchFetchesRequestedGroups(t *testing.T) {
|
|
c := &fakeClient{
|
|
entitySearchResults: []*pb.Entity{testEntity("jsmith", 1000)},
|
|
entityGroupsResults: map[string][]*pb.Group{"jsmith": {testGroup("admins", 10)}},
|
|
}
|
|
s := boundTestServer(c)
|
|
bind(t, s)
|
|
w := &recordingWriter{}
|
|
|
|
s.handleSearchEntities(w, mustDecode(t,
|
|
berSearchRequestWithAttrs("ou=entities,dc=netauth,dc=example,dc=com", "uid", "memberOf")))
|
|
|
|
if len(c.entityGroupsCalls) != 1 {
|
|
t.Errorf("EntityGroups was called %d times, want 1", len(c.entityGroupsCalls))
|
|
}
|
|
}
|
|
|
|
func TestGroupSearchSkipsUnrequestedMembers(t *testing.T) {
|
|
c := &fakeClient{
|
|
groupSearchResults: []*pb.Group{testGroup("admins", 10)},
|
|
groupMembersResults: map[string][]*pb.Entity{"admins": {testEntity("jsmith", 1000)}},
|
|
}
|
|
s := boundTestServer(c)
|
|
bind(t, s)
|
|
w := &recordingWriter{}
|
|
|
|
s.handleSearchGroups(w, mustDecode(t,
|
|
berSearchRequestWithAttrs("ou=groups,dc=netauth,dc=example,dc=com", "cn")))
|
|
|
|
if len(w.responses) < 2 {
|
|
t.Fatalf("the search did not return any entries: %d responses", len(w.responses))
|
|
}
|
|
if len(c.groupMembersCalls) != 0 {
|
|
t.Errorf("GroupMembers was called %d times for a search that did not request member", len(c.groupMembersCalls))
|
|
}
|
|
}
|
|
|
|
func TestGroupSearchFetchesRequestedMembers(t *testing.T) {
|
|
c := &fakeClient{
|
|
groupSearchResults: []*pb.Group{testGroup("admins", 10)},
|
|
groupMembersResults: map[string][]*pb.Entity{"admins": {testEntity("jsmith", 1000)}},
|
|
}
|
|
s := boundTestServer(c)
|
|
bind(t, s)
|
|
w := &recordingWriter{}
|
|
|
|
s.handleSearchGroups(w, mustDecode(t,
|
|
berSearchRequestWithAttrs("ou=groups,dc=netauth,dc=example,dc=com", "cn", "member")))
|
|
|
|
if len(c.groupMembersCalls) != 1 {
|
|
t.Errorf("GroupMembers was called %d times, want 1", len(c.groupMembersCalls))
|
|
}
|
|
}
|