1package ldap_test
2
3import (
4	"strings"
5	"testing"
6
7	"gopkg.in/asn1-ber.v1"
8	"gopkg.in/ldap.v2"
9)
10
11type compileTest struct {
12	filterStr string
13
14	expectedFilter string
15	expectedType   int
16	expectedErr    string
17}
18
19var testFilters = []compileTest{
20	compileTest{
21		filterStr:      "(&(sn=Miller)(givenName=Bob))",
22		expectedFilter: "(&(sn=Miller)(givenName=Bob))",
23		expectedType:   ldap.FilterAnd,
24	},
25	compileTest{
26		filterStr:      "(|(sn=Miller)(givenName=Bob))",
27		expectedFilter: "(|(sn=Miller)(givenName=Bob))",
28		expectedType:   ldap.FilterOr,
29	},
30	compileTest{
31		filterStr:      "(!(sn=Miller))",
32		expectedFilter: "(!(sn=Miller))",
33		expectedType:   ldap.FilterNot,
34	},
35	compileTest{
36		filterStr:      "(sn=Miller)",
37		expectedFilter: "(sn=Miller)",
38		expectedType:   ldap.FilterEqualityMatch,
39	},
40	compileTest{
41		filterStr:      "(sn=Mill*)",
42		expectedFilter: "(sn=Mill*)",
43		expectedType:   ldap.FilterSubstrings,
44	},
45	compileTest{
46		filterStr:      "(sn=*Mill)",
47		expectedFilter: "(sn=*Mill)",
48		expectedType:   ldap.FilterSubstrings,
49	},
50	compileTest{
51		filterStr:      "(sn=*Mill*)",
52		expectedFilter: "(sn=*Mill*)",
53		expectedType:   ldap.FilterSubstrings,
54	},
55	compileTest{
56		filterStr:      "(sn=*i*le*)",
57		expectedFilter: "(sn=*i*le*)",
58		expectedType:   ldap.FilterSubstrings,
59	},
60	compileTest{
61		filterStr:      "(sn=Mi*l*r)",
62		expectedFilter: "(sn=Mi*l*r)",
63		expectedType:   ldap.FilterSubstrings,
64	},
65	// substring filters escape properly
66	compileTest{
67		filterStr:      `(sn=Mi*함*r)`,
68		expectedFilter: `(sn=Mi*\ed\95\a8*r)`,
69		expectedType:   ldap.FilterSubstrings,
70	},
71	// already escaped substring filters don't get double-escaped
72	compileTest{
73		filterStr:      `(sn=Mi*\ed\95\a8*r)`,
74		expectedFilter: `(sn=Mi*\ed\95\a8*r)`,
75		expectedType:   ldap.FilterSubstrings,
76	},
77	compileTest{
78		filterStr:      "(sn=Mi*le*)",
79		expectedFilter: "(sn=Mi*le*)",
80		expectedType:   ldap.FilterSubstrings,
81	},
82	compileTest{
83		filterStr:      "(sn=*i*ler)",
84		expectedFilter: "(sn=*i*ler)",
85		expectedType:   ldap.FilterSubstrings,
86	},
87	compileTest{
88		filterStr:      "(sn>=Miller)",
89		expectedFilter: "(sn>=Miller)",
90		expectedType:   ldap.FilterGreaterOrEqual,
91	},
92	compileTest{
93		filterStr:      "(sn<=Miller)",
94		expectedFilter: "(sn<=Miller)",
95		expectedType:   ldap.FilterLessOrEqual,
96	},
97	compileTest{
98		filterStr:      "(sn=*)",
99		expectedFilter: "(sn=*)",
100		expectedType:   ldap.FilterPresent,
101	},
102	compileTest{
103		filterStr:      "(sn~=Miller)",
104		expectedFilter: "(sn~=Miller)",
105		expectedType:   ldap.FilterApproxMatch,
106	},
107	compileTest{
108		filterStr:      `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`,
109		expectedFilter: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`,
110		expectedType:   ldap.FilterEqualityMatch,
111	},
112	compileTest{
113		filterStr:      `(objectGUID=абвгдеёжзийклмнопрстуфхцчшщъыьэюя)`,
114		expectedFilter: `(objectGUID=\d0\b0\d0\b1\d0\b2\d0\b3\d0\b4\d0\b5\d1\91\d0\b6\d0\b7\d0\b8\d0\b9\d0\ba\d0\bb\d0\bc\d0\bd\d0\be\d0\bf\d1\80\d1\81\d1\82\d1\83\d1\84\d1\85\d1\86\d1\87\d1\88\d1\89\d1\8a\d1\8b\d1\8c\d1\8d\d1\8e\d1\8f)`,
115		expectedType:   ldap.FilterEqualityMatch,
116	},
117	compileTest{
118		filterStr:      `(objectGUID=함수목록)`,
119		expectedFilter: `(objectGUID=\ed\95\a8\ec\88\98\eb\aa\a9\eb\a1\9d)`,
120		expectedType:   ldap.FilterEqualityMatch,
121	},
122	compileTest{
123		filterStr:      `(objectGUID=`,
124		expectedFilter: ``,
125		expectedType:   0,
126		expectedErr:    "unexpected end of filter",
127	},
128	compileTest{
129		filterStr:      `(objectGUID=함수목록`,
130		expectedFilter: ``,
131		expectedType:   0,
132		expectedErr:    "unexpected end of filter",
133	},
134	compileTest{
135		filterStr:      `((cn=)`,
136		expectedFilter: ``,
137		expectedType:   0,
138		expectedErr:    "unexpected end of filter",
139	},
140	compileTest{
141		filterStr:      `(&(objectclass=inetorgperson)(cn=中文))`,
142		expectedFilter: `(&(objectclass=inetorgperson)(cn=\e4\b8\ad\e6\96\87))`,
143		expectedType:   0,
144	},
145	// attr extension
146	compileTest{
147		filterStr:      `(memberOf:=foo)`,
148		expectedFilter: `(memberOf:=foo)`,
149		expectedType:   ldap.FilterExtensibleMatch,
150	},
151	// attr+named matching rule extension
152	compileTest{
153		filterStr:      `(memberOf:test:=foo)`,
154		expectedFilter: `(memberOf:test:=foo)`,
155		expectedType:   ldap.FilterExtensibleMatch,
156	},
157	// attr+oid matching rule extension
158	compileTest{
159		filterStr:      `(cn:1.2.3.4.5:=Fred Flintstone)`,
160		expectedFilter: `(cn:1.2.3.4.5:=Fred Flintstone)`,
161		expectedType:   ldap.FilterExtensibleMatch,
162	},
163	// attr+dn+oid matching rule extension
164	compileTest{
165		filterStr:      `(sn:dn:2.4.6.8.10:=Barney Rubble)`,
166		expectedFilter: `(sn:dn:2.4.6.8.10:=Barney Rubble)`,
167		expectedType:   ldap.FilterExtensibleMatch,
168	},
169	// attr+dn extension
170	compileTest{
171		filterStr:      `(o:dn:=Ace Industry)`,
172		expectedFilter: `(o:dn:=Ace Industry)`,
173		expectedType:   ldap.FilterExtensibleMatch,
174	},
175	// dn extension
176	compileTest{
177		filterStr:      `(:dn:2.4.6.8.10:=Dino)`,
178		expectedFilter: `(:dn:2.4.6.8.10:=Dino)`,
179		expectedType:   ldap.FilterExtensibleMatch,
180	},
181	compileTest{
182		filterStr:      `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`,
183		expectedFilter: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`,
184		expectedType:   ldap.FilterExtensibleMatch,
185	},
186
187	// compileTest{ filterStr: "()", filterType: FilterExtensibleMatch },
188}
189
190var testInvalidFilters = []string{
191	`(objectGUID=\zz)`,
192	`(objectGUID=\a)`,
193}
194
195func TestFilter(t *testing.T) {
196	// Test Compiler and Decompiler
197	for _, i := range testFilters {
198		filter, err := ldap.CompileFilter(i.filterStr)
199		if err != nil {
200			if i.expectedErr == "" || !strings.Contains(err.Error(), i.expectedErr) {
201				t.Errorf("Problem compiling '%s' - '%v' (expected error to contain '%v')", i.filterStr, err, i.expectedErr)
202			}
203		} else if filter.Tag != ber.Tag(i.expectedType) {
204			t.Errorf("%q Expected %q got %q", i.filterStr, ldap.FilterMap[uint64(i.expectedType)], ldap.FilterMap[uint64(filter.Tag)])
205		} else {
206			o, err := ldap.DecompileFilter(filter)
207			if err != nil {
208				t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error())
209			} else if i.expectedFilter != o {
210				t.Errorf("%q expected, got %q", i.expectedFilter, o)
211			}
212		}
213	}
214}
215
216func TestInvalidFilter(t *testing.T) {
217	for _, filterStr := range testInvalidFilters {
218		if _, err := ldap.CompileFilter(filterStr); err == nil {
219			t.Errorf("Problem compiling %s - expected err", filterStr)
220		}
221	}
222}
223
224func BenchmarkFilterCompile(b *testing.B) {
225	b.StopTimer()
226	filters := make([]string, len(testFilters))
227
228	// Test Compiler and Decompiler
229	for idx, i := range testFilters {
230		filters[idx] = i.filterStr
231	}
232
233	maxIdx := len(filters)
234	b.StartTimer()
235	for i := 0; i < b.N; i++ {
236		ldap.CompileFilter(filters[i%maxIdx])
237	}
238}
239
240func BenchmarkFilterDecompile(b *testing.B) {
241	b.StopTimer()
242	filters := make([]*ber.Packet, len(testFilters))
243
244	// Test Compiler and Decompiler
245	for idx, i := range testFilters {
246		filters[idx], _ = ldap.CompileFilter(i.filterStr)
247	}
248
249	maxIdx := len(filters)
250	b.StartTimer()
251	for i := 0; i < b.N; i++ {
252		ldap.DecompileFilter(filters[i%maxIdx])
253	}
254}
255