1 package user
2
3 import (
4 "fmt"
5 "io"
6 "reflect"
7 "sort"
8 "strconv"
9 "strings"
10 "testing"
11 )
12
13 func TestUserParseLine(t *testing.T) {
14 var (
15 a, b string
16 c []string
17 d int
18 )
19
20 parseLine([]byte(""), &a, &b)
21 if a != "" || b != "" {
22 t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
23 }
24
25 parseLine([]byte("a"), &a, &b)
26 if a != "a" || b != "" {
27 t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
28 }
29
30 parseLine([]byte("bad boys:corny cows"), &a, &b)
31 if a != "bad boys" || b != "corny cows" {
32 t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
33 }
34
35 parseLine([]byte(""), &c)
36 if len(c) != 0 {
37 t.Fatalf("c should be empty (%#v)", c)
38 }
39
40 parseLine([]byte("d,e,f:g:h:i,j,k"), &c, &a, &b, &c)
41 if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
42 t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
43 }
44
45 parseLine([]byte("::::::::::"), &a, &b, &c)
46 if a != "" || b != "" || len(c) != 0 {
47 t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
48 }
49
50 parseLine([]byte("not a number"), &d)
51 if d != 0 {
52 t.Fatalf("d should be 0 (%v)", d)
53 }
54
55 parseLine([]byte("b:12:c"), &a, &d, &b)
56 if a != "b" || b != "c" || d != 12 {
57 t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
58 }
59 }
60
61 func TestUserParsePasswd(t *testing.T) {
62 users, err := ParsePasswdFilter(strings.NewReader(`
63 root:x:0:0:root:/root:/bin/bash
64 adm:x:3:4:adm:/var/adm:/bin/false
65 this is just some garbage data
66 `), nil)
67 if err != nil {
68 t.Fatalf("Unexpected error: %v", err)
69 }
70 if len(users) != 3 {
71 t.Fatalf("Expected 3 users, got %v", len(users))
72 }
73 if users[0].Uid != 0 || users[0].Name != "root" {
74 t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name)
75 }
76 if users[1].Uid != 3 || users[1].Name != "adm" {
77 t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name)
78 }
79 }
80
81 func TestUserParseGroup(t *testing.T) {
82 groups, err := ParseGroupFilter(strings.NewReader(`
83 root:x:0:root
84 adm:x:4:root,adm,daemon
85 this is just some garbage data
86 `+largeGroup()), nil)
87 if err != nil {
88 t.Fatalf("Unexpected error: %v", err)
89 }
90 if len(groups) != 4 {
91 t.Fatalf("Expected 4 groups, got %v", len(groups))
92 }
93 if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 {
94 t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List))
95 }
96 if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 {
97 t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
98 }
99 }
100
101 func TestValidGetExecUser(t *testing.T) {
102 const passwdContent = `
103 root:x:0:0:root user:/root:/bin/bash
104 adm:x:42:43:adm:/var/adm:/bin/false
105 111:x:222:333::/var/garbage
106 odd:x:111:112::/home/odd:::::
107 user7456:x:7456:100:Vasya:/home/user7456
108 this is just some garbage data
109 `
110 groupContent := `
111 root:x:0:root
112 adm:x:43:
113 grp:x:1234:root,adm,user7456
114 444:x:555:111
115 odd:x:444:
116 this is just some garbage data
117 ` + largeGroup()
118
119 defaultExecUser := ExecUser{
120 Uid: 8888,
121 Gid: 8888,
122 Sgids: []int{8888},
123 Home: "/8888",
124 }
125
126 tests := []struct {
127 ref string
128 expected ExecUser
129 }{
130 {
131 ref: "root",
132 expected: ExecUser{
133 Uid: 0,
134 Gid: 0,
135 Sgids: []int{0, 1234},
136 Home: "/root",
137 },
138 },
139 {
140 ref: "adm",
141 expected: ExecUser{
142 Uid: 42,
143 Gid: 43,
144 Sgids: []int{1234},
145 Home: "/var/adm",
146 },
147 },
148 {
149 ref: "root:adm",
150 expected: ExecUser{
151 Uid: 0,
152 Gid: 43,
153 Sgids: defaultExecUser.Sgids,
154 Home: "/root",
155 },
156 },
157 {
158 ref: "adm:1234",
159 expected: ExecUser{
160 Uid: 42,
161 Gid: 1234,
162 Sgids: defaultExecUser.Sgids,
163 Home: "/var/adm",
164 },
165 },
166 {
167 ref: "42:1234",
168 expected: ExecUser{
169 Uid: 42,
170 Gid: 1234,
171 Sgids: defaultExecUser.Sgids,
172 Home: "/var/adm",
173 },
174 },
175 {
176 ref: "1337:1234",
177 expected: ExecUser{
178 Uid: 1337,
179 Gid: 1234,
180 Sgids: defaultExecUser.Sgids,
181 Home: defaultExecUser.Home,
182 },
183 },
184 {
185 ref: "1337",
186 expected: ExecUser{
187 Uid: 1337,
188 Gid: defaultExecUser.Gid,
189 Sgids: defaultExecUser.Sgids,
190 Home: defaultExecUser.Home,
191 },
192 },
193 {
194 ref: "",
195 expected: ExecUser{
196 Uid: defaultExecUser.Uid,
197 Gid: defaultExecUser.Gid,
198 Sgids: defaultExecUser.Sgids,
199 Home: defaultExecUser.Home,
200 },
201 },
202
203
204 {
205 ref: "111",
206 expected: ExecUser{
207 Uid: 111,
208 Gid: 112,
209 Sgids: defaultExecUser.Sgids,
210 Home: "/home/odd",
211 },
212 },
213 {
214 ref: "111:444",
215 expected: ExecUser{
216 Uid: 111,
217 Gid: 444,
218 Sgids: defaultExecUser.Sgids,
219 Home: "/home/odd",
220 },
221 },
222
223 {
224 ref: "7456",
225 expected: ExecUser{
226 Uid: 7456,
227 Gid: 100,
228 Sgids: []int{1234, 1000},
229 Home: "/home/user7456",
230 },
231 },
232 }
233
234 for _, test := range tests {
235 passwd := strings.NewReader(passwdContent)
236 group := strings.NewReader(groupContent)
237
238 execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group)
239 if err != nil {
240 t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
241 t.Fail()
242 continue
243 }
244
245 if !reflect.DeepEqual(test.expected, *execUser) {
246 t.Logf("ref: %v", test.ref)
247 t.Logf("got: %#v", execUser)
248 t.Logf("expected: %#v", test.expected)
249 t.Fail()
250 continue
251 }
252 }
253 }
254
255 func TestInvalidGetExecUser(t *testing.T) {
256 const passwdContent = `
257 root:x:0:0:root user:/root:/bin/bash
258 adm:x:42:43:adm:/var/adm:/bin/false
259 -42:x:12:13:broken:/very/broken
260 this is just some garbage data
261 `
262 const groupContent = `
263 root:x:0:root
264 adm:x:43:
265 grp:x:1234:root,adm
266 this is just some garbage data
267 `
268
269 tests := []string{
270
271 "notuser",
272 "notuser:notgroup",
273 "root:notgroup",
274 "notuser:adm",
275 "8888:notgroup",
276 "notuser:8888",
277
278
279 "-1:0",
280 "0:-3",
281 "-5:-2",
282 "-42",
283 "-43",
284 }
285
286 for _, test := range tests {
287 passwd := strings.NewReader(passwdContent)
288 group := strings.NewReader(groupContent)
289
290 execUser, err := GetExecUser(test, nil, passwd, group)
291 if err == nil {
292 t.Logf("got unexpected success when parsing '%s': %#v", test, execUser)
293 t.Fail()
294 continue
295 }
296 }
297 }
298
299 func TestGetExecUserNilSources(t *testing.T) {
300 const passwdContent = `
301 root:x:0:0:root user:/root:/bin/bash
302 adm:x:42:43:adm:/var/adm:/bin/false
303 this is just some garbage data
304 `
305 const groupContent = `
306 root:x:0:root
307 adm:x:43:
308 grp:x:1234:root,adm
309 this is just some garbage data
310 `
311
312 defaultExecUser := ExecUser{
313 Uid: 8888,
314 Gid: 8888,
315 Sgids: []int{8888},
316 Home: "/8888",
317 }
318
319 tests := []struct {
320 ref string
321 passwd, group bool
322 expected ExecUser
323 }{
324 {
325 ref: "",
326 passwd: false,
327 group: false,
328 expected: ExecUser{
329 Uid: 8888,
330 Gid: 8888,
331 Sgids: []int{8888},
332 Home: "/8888",
333 },
334 },
335 {
336 ref: "root",
337 passwd: true,
338 group: false,
339 expected: ExecUser{
340 Uid: 0,
341 Gid: 0,
342 Sgids: []int{8888},
343 Home: "/root",
344 },
345 },
346 {
347 ref: "0",
348 passwd: false,
349 group: false,
350 expected: ExecUser{
351 Uid: 0,
352 Gid: 8888,
353 Sgids: []int{8888},
354 Home: "/8888",
355 },
356 },
357 {
358 ref: "0:0",
359 passwd: false,
360 group: false,
361 expected: ExecUser{
362 Uid: 0,
363 Gid: 0,
364 Sgids: []int{8888},
365 Home: "/8888",
366 },
367 },
368 }
369
370 for _, test := range tests {
371 var passwd, group io.Reader
372
373 if test.passwd {
374 passwd = strings.NewReader(passwdContent)
375 }
376
377 if test.group {
378 group = strings.NewReader(groupContent)
379 }
380
381 execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group)
382 if err != nil {
383 t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
384 t.Fail()
385 continue
386 }
387
388 if !reflect.DeepEqual(test.expected, *execUser) {
389 t.Logf("got: %#v", execUser)
390 t.Logf("expected: %#v", test.expected)
391 t.Fail()
392 continue
393 }
394 }
395 }
396
397 func TestGetAdditionalGroups(t *testing.T) {
398 type foo struct {
399 groups []string
400 expected []int
401 hasError bool
402 }
403
404 groupContent := `
405 root:x:0:root
406 adm:x:43:
407 grp:x:1234:root,adm
408 adm:x:4343:root,adm-duplicate
409 this is just some garbage data
410 ` + largeGroup()
411 tests := []foo{
412 {
413
414 groups: []string{},
415 expected: []int{},
416 },
417 {
418
419 groups: []string{"adm"},
420 expected: []int{43},
421 },
422 {
423
424 groups: []string{"adm", "grp"},
425 expected: []int{43, 1234},
426 },
427 {
428
429 groups: []string{"adm", "grp", "not-exist"},
430 expected: nil,
431 hasError: true,
432 },
433 {
434
435 groups: []string{"43"},
436 expected: []int{43},
437 },
438 {
439
440 groups: []string{"adm", "10001"},
441 expected: []int{43, 10001},
442 },
443 {
444
445 groups: []string{"adm", "43"},
446 expected: []int{43},
447 },
448 {
449
450 groups: []string{"-1"},
451 expected: nil,
452 hasError: true,
453 },
454 {
455
456 groups: []string{strconv.FormatInt(1<<31, 10)},
457 expected: nil,
458 hasError: true,
459 },
460 {
461
462 groups: []string{"largegroup"},
463 expected: []int{1000},
464 },
465 }
466
467 for _, test := range tests {
468 group := strings.NewReader(groupContent)
469
470 gids, err := GetAdditionalGroups(test.groups, group)
471 if test.hasError && err == nil {
472 t.Errorf("Parse(%#v) expects error but has none", test)
473 continue
474 }
475 if !test.hasError && err != nil {
476 t.Errorf("Parse(%#v) has error %v", test, err)
477 continue
478 }
479 sort.Ints(gids)
480 if !reflect.DeepEqual(gids, test.expected) {
481 t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups)
482 }
483 }
484 }
485
486 func TestGetAdditionalGroupsNumeric(t *testing.T) {
487 tests := []struct {
488 groups []string
489 expected []int
490 hasError bool
491 }{
492 {
493
494 groups: []string{"1234", "5678"},
495 expected: []int{1234, 5678},
496 },
497 {
498
499 groups: []string{"1234", "fake"},
500 expected: nil,
501 hasError: true,
502 },
503 }
504
505 for _, test := range tests {
506 gids, err := GetAdditionalGroups(test.groups, nil)
507 if test.hasError && err == nil {
508 t.Errorf("Parse(%#v) expects error but has none", test)
509 continue
510 }
511 if !test.hasError && err != nil {
512 t.Errorf("Parse(%#v) has error %v", test, err)
513 continue
514 }
515 sort.Ints(gids)
516 if !reflect.DeepEqual(gids, test.expected) {
517 t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups)
518 }
519 }
520 }
521
522
523 func largeGroup() (res string) {
524 var b strings.Builder
525 b.WriteString("largegroup:x:1000:user1")
526 for i := 2; i <= 7500; i++ {
527 fmt.Fprintf(&b, ",user%d", i)
528 }
529 return b.String()
530 }
531
View as plain text